<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>junnhwan</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://junnhwan.github.io/</id>
  <link href="https://junnhwan.github.io/" rel="alternate"/>
  <link href="https://junnhwan.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, junnhwan</rights>
  <subtitle>Progress Infinitely</subtitle>
  <title>junnhwan's blog</title>
  <updated>2026-08-07T16:18:13.811Z</updated>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="面经" scheme="https://junnhwan.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    <category term="面经" scheme="https://junnhwan.github.io/tags/%E9%9D%A2%E7%BB%8F/"/>
    <category term="Go" scheme="https://junnhwan.github.io/tags/Go/"/>
    <category term="实习" scheme="https://junnhwan.github.io/tags/%E5%AE%9E%E4%B9%A0/"/>
    <content>
      <![CDATA[<blockquote><p>本文主要是记一些自己的碎碎念，还有暑假以来海投找日常实习的三个公司面试的复盘总结。面经问题清单都用截图给出，是用AI总结的，就是用飞书妙记来录音然后导出录音记录的txt，再丢给GPT总结。其他都是自己手敲的一些碎碎念，可能内容比较混乱。</p></blockquote><p>本人情况是28届双非本科，因为看身边同学有一部分开始实习了（甚至大一就开始的都有），网上也看到过很多同届的很早开始实习甚至是大厂实习了。由于目前暂时没有升学读研以及考公的想法，所以是想着早点找个实习，积累点经验，然后毕业找工。</p><p>之前刚开始学开发还有找实习的时候看到网上那些很早大厂实习这些还是会很焦虑的（xhs、牛客还是要少刷这些让人焦虑的啊，当然刷一些面经什么的还是有价值的），一直陷入无尽的自我怀疑与内耗中去。</p><p>但是想到之前看到过一句话说“比较是偷走幸福的小偷”，还有“在任何阶段，身边出现比自己更厉害的人都是大概率事件。不要因为别人走得更快便立刻否定自己，反超别人本身也不应当成为目标，更重要的是把注意力放回自己的成长，完成自己认可的事情”。</p><p>感觉确实也是这么回事，去比较是永远比不完的，真的更多要专注到自己身上，为这些比较而让自己内耗难受真的不值得，又想到了一句“向外求求而不得，向内求生生不息”。然后也是开始稍微专注于自己准备需要的那些老生常谈的东西了，也就是项目、八股、算法。</p><p>项目方面，一个是网上看到的别人的项目（一个结合视频理解这个场景的，然后沾点RAG），然后自己借助codex用go复刻了一下，自己稍微改了点功能，另一个就是比较烂大街的coding agent项目了；八股方面，看的小林coding，然后一些问题随时问gpt，尽量理解着去记而不是死记硬背；算法方面，由于主要投中小厂，不是大厂，几乎都是不怎么考算法的，所以没咋刷lc hot 100。</p><p>开始正式海投是期末考完之后，7.14开始，一直到8.06，目前为止BOSS上沟通900，只有两个面试，另外一个面试是在实习僧上刚投了几个就有的，下面开始放面经问题。</p><p>第一个面试是一个小厂的AI应用开发，7.16一面，7.20二面，然后一直没回复我了，连拒信都懒得给我发了，在实习僧上问HR直接已读不回😂，问题截图如下（分别为一二面，两面都是30min，都是问项目，无八股算法）：</p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E3%80%90%E5%B0%8F%E5%8E%82%E9%9D%A2%E7%BB%8F-AI%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E3%80%91FOSHO%E4%B8%80%E9%9D%A2_1_wanjune_%E6%9D%A5%E8%87%AA%E5%B0%8F%E7%BA%A2%E4%B9%A6%E7%BD%91%E9%A1%B5%E7%89%88.jpg" alt="【小厂面经\-AI应用开发】FOSHO一面\_1\_wanjune\_来自小红书网页版\.jpg"></p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E3%80%90%E5%B0%8F%E5%8E%82%E9%9D%A2%E7%BB%8F-AI%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E3%80%91FOSHO%E4%B8%80%E9%9D%A2_2_wanjune_%E6%9D%A5%E8%87%AA%E5%B0%8F%E7%BA%A2%E4%B9%A6%E7%BD%91%E9%A1%B5%E7%89%88.jpg" alt="【小厂面经\-AI应用开发】FOSHO一面\_2\_wanjune\_来自小红书网页版\.jpg"></p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E4%BA%8C%E9%9D%A2.png" alt="二面\.png"></p><p>第二个面试是深信服的 Go 后端开发日常实习，7.23一面，7.24二面，7.29HR通知二面挂，二面可能是KPI面，在dy上有个人说情况也是和我面的一样，面试官迟到10分钟，然后声音很模糊，面的过程中因为声音听不清想跟面试官确认一下问题，他还很不耐烦的语气😂也可能是我想多了。问题截图如下（分别为一二面，两面都是30min，都是问项目，无八股算法）：</p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/sangfor-Go%E5%BC%80%E5%8F%91%E4%B8%80%E9%9D%A2.png" alt="sangfor\-Go开发一面\.png"></p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/sangfor-Go%E5%BC%80%E5%8F%91%E4%BA%8C%E9%9D%A2.png" alt="sangfor\-Go开发二面\.png"></p><p>第三个面试是一个小厂的中台全栈开发，8.3一面，8.6HR面，8.7oc，是找实习以来第一个offer，150一天，感觉好像有点黑奴，但好处是距离宿舍就几百米，走路十分钟到，问题截图如下（一面技术面，拷打50min，问项目，以及八股，也暴露我挺多问题的一次面试，还要提一嘴的是这是唯一一个开摄像头的面试官，而且感觉是一个挺和善的技术老登，面试体验挺不错的，之前的几次面试都是只有我开摄像头而面试官没开；二面是HR面，问的都是比较日常的就没放了）：</p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E8%89%BE%E7%BB%B4%E4%B9%90%E6%B8%B8%E4%B8%80%E9%9D%A21.png" alt="艾维乐游一面1\.png"></p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E8%89%BE%E7%BB%B4%E4%B9%90%E6%B8%B8%E4%B8%80%E9%9D%A22.png" alt="艾维乐游一面2\.png"></p><p><img src="/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/%E8%89%BE%E7%BB%B4%E4%B9%90%E6%B8%B8%E4%B8%80%E9%9D%A23.png" alt="艾维乐游一面3\.png"></p><p>这三家面试总结下来，基本上问的都是我简历上第一个自己乱vibe的那个项目，问的主要就是<strong>文件处理分片上传和断点续传</strong>、<strong>消息队列的使用以及一些状态处理、失败重试</strong>的问题，RAG问的比较少（几乎是没有，除了AI应用开发那个，但是也没深入问）；然后coding agent的更是问的少了，有问的基本都是只有问<strong>上下文分层治理</strong>这一点，可能这个确实很烂大街了，也没什么应用场景，现在都是用codex、claude code、pi这些的，但是我觉得作为来学Agent开发一些思想还是不错的，但是作为简历项目可能不太行。</p><p>后续的目标：</p><p>后面就现在这个小厂实习试试吧，如果有点产出的话，简历就变成我第一个项目加上实习经历了，coding agent就能去掉了。然后后面也要把第一个项目再完善一下，现在漏洞可能还是比较多 ，不太能经得起拷打（虽然自己vibe的项目，在面试官眼中肯定都是很玩具的，问题都是硬问，还是要实习经历这种有实际问题的比较好）。以及八股算法都持续跟进一下，等第二版简历能写出来之后，再尝试一下中大厂的投递吧。</p>]]>
    </content>
    <id>https://junnhwan.github.io/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/</id>
    <link href="https://junnhwan.github.io/2026/08/08/%E4%B8%80%E4%BA%9B%E9%9A%8F%E8%AE%B0%EF%BC%8C%E4%BB%A5%E5%8F%8A%E8%BF%91%E6%9C%9F%E6%89%BE%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%EF%BC%887-14-8-07%EF%BC%89/"/>
    <published>2026-08-07T16:16:54.000Z</published>
    <summary>海投三周、三个面试，第一个实习 offer 到手前后的随记，以及三家公司的面试复盘总结。</summary>
    <title>一些随记，以及近期找实习面试复盘总结（7.14~8.07）</title>
    <updated>2026-08-07T16:18:13.811Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="面经" scheme="https://junnhwan.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    <category term="Agent" scheme="https://junnhwan.github.io/tags/Agent/"/>
    <category term="面经" scheme="https://junnhwan.github.io/tags/%E9%9D%A2%E7%BB%8F/"/>
    <category term="Go" scheme="https://junnhwan.github.io/tags/Go/"/>
    <content>
      <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>时间过得很快，眨眼就到了大二暑假，下学期就是大三了。</p><p>7 月 13 号考完试，按计划开始海投。前面几天基本没人理：Boss 上投了、聊了大概四五百份，很多已读不回，交换简历也没下文。直到 7 月 16 号，在实习僧上约到第一家——初创小厂 FOSHO，岗位是 AI 应用开发。一面 7.16，二面 7.20；写到这篇的时候已经 7.26 了，还没结果，估计大概率挂了。但两场下来还是能复盘出一些东西。</p><p>接着 7 月 22 号收到深信服 Go 后端开发的面试邀请。23 号一面，24 号二面——差不多 24 小时内把两轮技术面赶完。到现在 HR 也还没回音，二面过没过不知道；就算过了，HR 面会不会被横向，也还说不准。</p><p>总之先把这四场记下来，当后面复习的参照。补充一句：四场都大概半小时左右，节奏都偏快。</p><hr><p>先交代一下我的项目背景，后面读题会更清楚。两个项目都是 Go 写的：</p><ol><li><strong>偏业务 + AI 的后端</strong>：长音视频上传、异步转写&#x2F;摘要、问答这类链路  </li><li><strong>Coding Agent</strong>：更偏纯 Agent 能力（工具调用、上下文、安全策略等）</li></ol><p>这几场下来，有几个很明显的共同点：</p><ul><li><strong>几乎零八股、零算法</strong>——基本都围着项目挖，再往外扩场景题。我猜和还没到大三暑期实习那一档有关，面试官更想确认「你是不是真做过」。</li><li><strong>Agent 的上下文管理</strong>几乎必问，而且会追问。</li><li><strong>技术选型</strong>也常问：Go &#x2F; TypeScript &#x2F; Python 怎么选、为什么 Agent 也用 Go。</li></ul><p>同一套项目，FOSHO 更抠「AI 应用是不是说圆了」，深信服更抠「后端工程判断靠不靠谱」——对比着写一篇，反而比拆成两篇更有意思。</p><hr><h2 id="FOSHO-两轮技术面"><a href="#FOSHO-两轮技术面" class="headerlink" title="FOSHO 两轮技术面"></a>FOSHO 两轮技术面</h2><p>我猜一面偏 MT，二面偏 leader。一面面试官比较年轻，整体氛围也还行；二面感觉是个老登，估计就是 leader 了，压力也更大，会多问一些情景题和个人情况。</p><h3 id="一面印象"><a href="#一面印象" class="headerlink" title="一面印象"></a>一面印象</h3><p>流程很常规：自我介绍 → 围着第一个项目追问。两面其实都会先问：</p><ul><li>项目要解决什么问题、面向什么场景  </li><li>简历上的具体点（比如文件分片上传）  </li><li>外延一点：目前瓶颈是什么、打算怎么优化  </li><li>Agent 为什么用这个语言、怎么选型</li></ul><p>整体还是「简历深挖 + 轻量扩展」，没有特别刁钻的压测感。</p><p>一面题单截图：</p><p><img src="/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/fosho-%E4%B8%80%E9%9D%A2-1.jpg" alt="FOSHO 一面题单 1"></p><p><img src="/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/fosho-%E4%B8%80%E9%9D%A2-2.jpg" alt="FOSHO 一面题单 2"></p><h3 id="二面印象"><a href="#二面印象" class="headerlink" title="二面印象"></a>二面印象</h3><p>二面更「实际」：面试官会盯业务里<strong>模型是不是真符合需求</strong>，以及你的系统<strong>是不是真能理解用户意图</strong>。项目包装一旦和真实能力有缝，这里很容易被戳到。</p><p>二面题单截图：</p><p><img src="/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/fosho-%E4%BA%8C%E9%9D%A2.png" alt="FOSHO 二面题单"></p><h3 id="想记下来的问题（先挂题，不急着在这篇里写完整答案）"><a href="#想记下来的问题（先挂题，不急着在这篇里写完整答案）" class="headerlink" title="想记下来的问题（先挂题，不急着在这篇里写完整答案）"></a>想记下来的问题（先挂题，不急着在这篇里写完整答案）</h3><p>下面这些问题，暂时只当「回头要补的作业」。本篇不展开标准答，之后复习时再查、再整理。</p><ul><li>当用户对话零碎、跨轮次、意图还在跳跃时，怎么结合上下文判断当前意图？</li><li>编程场景里，如何把用户跨多轮的修改要求、函数信息、当前任务串起来？</li><li>为什么选这个模型？选型依据是什么？</li><li>过去一个月，你学了哪些新的 AI 相关知识？</li></ul><p>（完整题单我另有整理，需要的话后面可以再贴附录。）</p><hr><h2 id="深信服两轮技术面"><a href="#深信服两轮技术面" class="headerlink" title="深信服两轮技术面"></a>深信服两轮技术面</h2><h3 id="一面印象-1"><a href="#一面印象-1" class="headerlink" title="一面印象"></a>一面印象</h3><p>一面就是大量<strong>项目场景拷打</strong>，再往<strong>生产环境</strong>上扩：接口突然变慢怎么办、幂等怎么做、慢查询有没有概念……不太像背题考试，更像在看你有没有工程直觉。</p><p>一面题单截图：</p><p><img src="/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/sangfor-%E4%B8%80%E9%9D%A2.png" alt="深信服 Go 开发一面题单"></p><h4 id="一面想记录的问题"><a href="#一面想记录的问题" class="headerlink" title="一面想记录的问题"></a>一面想记录的问题</h4><ul><li>简历里的分片上传和断点续传，<strong>怎么保证文件是正确的</strong>？</li><li>（追问）怎么保证所有分片组合起来<strong>一定正确</strong>？（不只是「没缺片」）</li><li>AI 编程用过哪些 <strong>Skill</strong>？</li><li>（追问）这些 Skill <strong>不好用</strong> 的地方有哪些？</li><li>用 Kafka 做调度时，<strong>重试</strong> 和 <strong>失败恢复</strong> 是怎么做的？</li><li>设计异步任务创建接口时，如何避免 <strong>重复创建异步任务</strong>（幂等）？</li><li>生产上某接口原先几十毫秒，突然变成好几秒：怎么 <strong>排查</strong>？怎么 <strong>止损</strong>？</li><li>（追问）如果发现是 <strong>版本更新之后</strong> 才变慢，一般从哪些方向找原因？</li><li>（再追问）什么样的 <strong>代码改动</strong> 会让接口从几十毫秒飙到几秒？</li><li>主要用 MySQL 吗？有没有做过 <strong>慢查询优化</strong>？</li></ul><h3 id="二面印象-1"><a href="#二面印象-1" class="headerlink" title="二面印象"></a>二面印象</h3><p>二面更像 leader 面：除了项目选型、要解决的问题，还问了不少<strong>协作场景</strong>——产品施压、同事质疑、时间不够时怎么取舍。</p><p>想吐槽一下：面试官那边网络真的不太好，有几次听得不是特别清楚，答起来也容易飘。</p><p>二面题单截图：</p><p><img src="/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/sangfor-%E4%BA%8C%E9%9D%A2.png" alt="深信服 Go 开发二面题单"></p><h4 id="二面想记录的问题"><a href="#二面想记录的问题" class="headerlink" title="二面想记录的问题"></a>二面想记录的问题</h4><p><strong>项目 &#x2F; 可靠性：</strong></p><ul><li>简历里的 <strong>Kafka 异步调度</strong> 主要在解决什么问题？做了什么？</li><li>视频处理链路里，<strong>任务阶段 &#x2F; 状态</strong> 怎么区分、怎么表示？分片（或分段）状态怎么记？请拿 <strong>一次失败</strong> 走一遍。</li><li>假设 <strong>ASR 已成功返回</strong>，但消费者在 <strong>更新任务状态前崩溃</strong>；恢复后消息被再次消费，可能 <strong>重复调模型产生费用</strong>——你怎么处理？</li><li>长任务里 <strong>部分片段转写失败</strong>（比如两段 ASR 挂了），摘要 &#x2F; 后续阶段怎么处理？</li><li>Agent 的 <strong>命令安全</strong> 是怎么做的？</li></ul><p><strong>协作 &#x2F; 取舍情景题（三道）：</strong></p><ul><li>产品认为「每次执行命令前都问用户」影响体验，要求 <strong>默认全放开自动执行</strong>；你不同意，会怎么 <strong>推动 &#x2F; 变通</strong>？</li><li>资深同事认为 <strong>Multi-Agent 是过度设计</strong>，要求全部改成单 Agent；你仍觉得并行调研 &#x2F; 代码走读有价值——你会怎么处理？</li><li>项目演示只剩 <strong>两天</strong>：视频问答能跑，但 <strong>失败恢复、权限隔离、引用评测</strong> 都没做完——你会怎么 <strong>取舍</strong>？</li></ul><hr><h2 id="问题参考回答记录"><a href="#问题参考回答记录" class="headerlink" title="问题参考回答记录"></a>问题参考回答记录</h2><p>上面那些题，完整「标准答」我还在慢慢补。这里先记深信服一面几道场景题的参考思路（<strong>2026-07-26</strong>），主要是和 AI 讨论之后整理的；后面有更新再往这篇里加。</p><h3 id="1-接口突然变慢：先止损，再排查"><a href="#1-接口突然变慢：先止损，再排查" class="headerlink" title="1. 接口突然变慢：先止损，再排查"></a>1. 接口突然变慢：先止损，再排查</h3><p>核心原则：<strong>生产环境第一，先止损再排查。</strong></p><h4 id="止损"><a href="#止损" class="headerlink" title="止损"></a>止损</h4><ul><li><strong>刚发的新版本</strong> → 优先考虑 <strong>回滚</strong></li><li><strong>下游 &#x2F; 非核心依赖</strong>出问题 → <strong>熔断、降级</strong>，必要时给用户明确提示（维护中 &#x2F; 功能暂时不可用）</li><li><strong>突发大流量</strong>打挂部分节点 → <strong>限流</strong>，保住整体可用性</li></ul><h4 id="排查"><a href="#排查" class="headerlink" title="排查"></a>排查</h4><ul><li><strong>监控指标</strong>：CPU、内存、IO、goroutine 数量有没有异常飙升</li><li><strong>APM &#x2F; 链路追踪</strong>：看 trace 上哪一段相对基线明显变慢（MySQL、Redis、其他中间件、第三方 HTTP……）</li><li><strong>日志</strong>：MySQL 慢日志、应用 error 日志；顺带看连接池、有没有死锁、是否频繁 GC 等</li></ul><h3 id="2-若确认是「版本更新后」才变慢：从-diff-入手"><a href="#2-若确认是「版本更新后」才变慢：从-diff-入手" class="headerlink" title="2. 若确认是「版本更新后」才变慢：从 diff 入手"></a>2. 若确认是「版本更新后」才变慢：从 diff 入手</h3><p>可以围绕 <strong>Git diff</strong> 和变更面来拆：</p><ol><li><strong>SQL &#x2F; 数据访问变更</strong>  <ul><li>新 SQL 有没有漏索引、索引失效  </li><li>循环里打 SQL 造成 <strong>N+1</strong></li></ul></li><li><strong>代码逻辑与并发</strong>  <ul><li>全局大锁把并行打成串行  </li><li>goroutine 泄露、死循环、意外的 CPU 密集计算</li></ul></li><li><strong>配置与连接池</strong>  <ul><li>DB &#x2F; Redis 连接池参数  </li><li>HTTP client 最大连接数、超时时间</li></ul></li><li><strong>第三方 SDK &#x2F; 依赖</strong>  <ul><li>默认重试变多、同步阻塞、已知 bug 等</li></ul></li></ol><h3 id="3-什么样的代码改动，容易把接口从几十毫秒打到几秒？"><a href="#3-什么样的代码改动，容易把接口从几十毫秒打到几秒？" class="headerlink" title="3. 什么样的代码改动，容易把接口从几十毫秒打到几秒？"></a>3. 什么样的代码改动，容易把接口从几十毫秒打到几秒？</h3><p>结合 diff 看，常见「经典雷」包括：</p><ul><li>循环里调 RPC &#x2F; SQL → <strong>N+1</strong></li><li><strong>锁范围放大</strong>，或在锁里做 I&#x2F;O</li><li>第三方 HTTP <strong>没有 context timeout</strong></li><li>大对象频繁分配，触发明显 <strong>GC</strong> 压力</li><li>SQL <strong>隐式类型转换</strong>导致索引失效 → 全表扫描</li></ul><h3 id="4-MySQL-慢查询优化（三步走）"><a href="#4-MySQL-慢查询优化（三步走）" class="headerlink" title="4. MySQL 慢查询优化（三步走）"></a>4. MySQL 慢查询优化（三步走）</h3><ol><li><strong>发现</strong>：慢日志、APM  </li><li><strong>分析</strong>：<code>EXPLAIN</code>  </li><li><strong>优化</strong>：索引、SQL 重写、必要时动架构</li></ol><p>看 <code>EXPLAIN</code> 时我会重点盯：</p><table><thead><tr><th>字段</th><th>关注点</th></tr></thead><tbody><tr><td><code>type</code></td><td>尽量到 <code>ref</code> &#x2F; <code>range</code>，避免 <code>ALL</code> 全表扫、以及过重的 <code>index</code> 全索引扫</td></tr><tr><td><code>key</code> &#x2F; <code>key_len</code></td><td>是否真的用上索引；组合索引有没有踩 <strong>最左前缀</strong></td></tr><tr><td><code>Extra</code></td><td>尽量少 <code>Using filesort</code>、<code>Using temporary</code></td></tr></tbody></table><h4 id="小案例：深分页大-offset"><a href="#小案例：深分页大-offset" class="headerlink" title="小案例：深分页大 offset"></a>小案例：深分页大 offset</h4><p><strong>问题：</strong> 订单表数据量很大，后台翻页类似：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> <span class="token identifier"><span class="token punctuation">`</span>order<span class="token punctuation">`</span></span><span class="token keyword">WHERE</span> <span class="token keyword">status</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token keyword">ORDER</span> <span class="token keyword">BY</span> id <span class="token keyword">DESC</span><span class="token keyword">LIMIT</span> <span class="token number">500000</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>耗时可能到秒级。</p><p><strong>原因：</strong> 即便 <code>id</code> 是主键，也要先扫过大量行再丢掉；<code>SELECT *</code> 还会带来大量回表。</p><p><strong>优化思路（延迟关联）：</strong> 子查询只取 <code>id</code>（更容易走覆盖索引），再回表取 20 行完整数据，例如：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> t1<span class="token punctuation">.</span><span class="token operator">*</span><span class="token keyword">FROM</span> <span class="token identifier"><span class="token punctuation">`</span>order<span class="token punctuation">`</span></span> t1<span class="token keyword">JOIN</span> <span class="token punctuation">(</span>  <span class="token keyword">SELECT</span> id <span class="token keyword">FROM</span> <span class="token identifier"><span class="token punctuation">`</span>order<span class="token punctuation">`</span></span>  <span class="token keyword">WHERE</span> <span class="token keyword">status</span> <span class="token operator">=</span> <span class="token number">1</span>  <span class="token keyword">ORDER</span> <span class="token keyword">BY</span> id <span class="token keyword">DESC</span>  <span class="token keyword">LIMIT</span> <span class="token number">500000</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">)</span> t2 <span class="token keyword">ON</span> t1<span class="token punctuation">.</span>id <span class="token operator">=</span> t2<span class="token punctuation">.</span>id<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>（具体能降多少取决于数据量和索引，这里只记思路，不当作绝对数字。）</p><h3 id="5-异步创建任务接口：怎么做幂等"><a href="#5-异步创建任务接口：怎么做幂等" class="headerlink" title="5. 异步创建任务接口：怎么做幂等"></a>5. 异步创建任务接口：怎么做幂等</h3><p>一套比较稳妥的组合：</p><ol><li>客户端 &#x2F; 业务侧生成全局唯一 <strong>task key</strong>（幂等键）</li><li>服务端用 Redis <strong><code>SETNX</code></strong>（或等价防重）占坑：已创建则直接返回已有任务 ID + 当前状态</li><li><strong>兜底</strong>：DB 对 task key 建 <strong>唯一索引</strong>；极端并发下两人一起穿过 Redis，也能靠 <code>Duplicate entry</code> 收敛，捕获后返回已有任务</li><li>再配合 <strong>状态机 &#x2F; 状态查询</strong>：已是 processing &#x2F; success，就返回当前态，保证多次调用结果语义一致</li></ol><hr><h2 id="写在后面"><a href="#写在后面" class="headerlink" title="写在后面"></a>写在后面</h2><p>目前两家都还停在技术面之后的等待期，结果怎样先不硬猜。这篇主要是把时间线、两家气质、想继续啃的题，以及已经整理出来的几道生产向答法记下来。</p><p>后面如果：</p><ul><li>FOSHO &#x2F; 深信服有后续消息  </li><li>其它场次也面完  </li><li>上面那些「先挂着的题」补出了自己的答案</li></ul><p>再继续往这篇（或下一篇）更新。</p><p>先这样。</p>]]>
    </content>
    <id>https://junnhwan.github.io/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/</id>
    <link href="https://junnhwan.github.io/2026/07/26/%E8%BF%91%E4%B8%A4%E5%91%A8%E9%9D%A2%E8%AF%95%E5%A4%8D%E7%9B%98-FOSHO%E4%B8%8E%E6%B7%B1%E4%BF%A1%E6%9C%8D/"/>
    <published>2026-07-26T03:30:00.000Z</published>
    <summary>海投两周，复盘 FOSHO 与深信服四场技术面：题单、印象，以及几道生产向场景题的参考思路。</summary>
    <title>
      <![CDATA[近两周面试复盘：FOSHO（AI 应用开发）& 深信服（Go 后端开发）]]>
    </title>
    <updated>2026-07-26T04:04:53.329Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Agent" scheme="https://junnhwan.github.io/tags/Agent/"/>
    <category term="claude code" scheme="https://junnhwan.github.io/tags/claude-code/"/>
    <content>
      <![CDATA[<h2 id="Prologue"><a href="#Prologue" class="headerlink" title="Prologue"></a>Prologue</h2><p>这篇blog来记录学习 <a href="https://learn.shareai.run/">learn-claude-code</a> 的过程笔记，主要是为了通过学习一个mini coding agent来学习agent开发的一些相关知识点，记录内容主要是一些我自认为比较重要的，如果有比较简略的还需看原网站进行补充。</p><p>整体学习路径总览</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622184129156.png" alt="image-20260622184129156"></p><h2 id="s08-Context-Compact"><a href="#s08-Context-Compact" class="headerlink" title="s08 Context Compact"></a>s08 Context Compact</h2><blockquote><p>四层压缩管线策略，先便宜后贵</p></blockquote><p>关于cc的上下文治理</p><ul><li>applyToolResult<strong>Budget</strong>()，先看有没有大的工具结果（超过某个阈值就算大，具体看源码），让大结果落盘不一次性读</li><li><strong>snip</strong>Compact()，裁剪中间消息，举个教学版的例子就是，只保留头部3条以及尾部47条</li><li><strong>micro</strong>Compact()，指的是旧的工具结果用占位表示，如果需要知道这个信息，可以在占位语句提示模型re-run这个tool（例如：Earlier tool result compacted. Re-run if needed）</li><li>context<strong>Collapse</strong>（），上下文折叠</li><li>autoCompact（），就是一次LLM API调用，进行摘要，是最贵的决策，会消耗token</li></ul><p>简单讲就是 budget -&gt; snip -&gt; micro -&gt; collapse，触发阈值就compact_history</p><p>下面附一张网站的讲解，关于cc对read的tradeoff：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260621173336607.png" alt="image-20260621173336607"></p><p>关于cc的压缩的Prompt：</p><ul><li>强调compact过程<strong>不能调用工具</strong>，只输出文本，在开头和结尾都会强调：<code>CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.</code></li><li>要求模型<strong>先分析再总结</strong>，分析的包裹在&lt;analysis&gt;标签中，正式的摘要在&lt;summary&gt;中，analysis最后会被剥离</li></ul><h2 id="s09-Memory"><a href="#s09-Memory" class="headerlink" title="s09 Memory"></a>s09 Memory</h2><blockquote><p>压缩会丢失细节，但是记忆不会，文件仓库 + 索引 + 按需加载 → 跨压缩、跨会话的知识积累</p></blockquote><p>四类记忆：</p><ul><li>user ，人物画像，你是谁</li><li>feedback，怎么做事，用户提出的什么rules</li><li>project，正在做什么</li><li>reference，在哪找东西</li></ul><p>四种动作：</p><ul><li>存储，.memory &#x2F; *.md文件（yaml frontmatter，name&#x2F;desc&#x2F;type）和 MEMORY.md 索引</li><li>加载，SYSTEM prompt常驻索引MEMORY.md，LLM side-query选文件</li><li>提取，每轮结束LLM提取偏好&#x2F;约束（检索已有，避免重复）</li><li>整理，文件大于某阈值，去重&#x2F;合并&#x2F;剪枝，cc把这种机制叫做Dream，实际的门控是这四层：时间间隔、扫描节流、会话数、文件锁</li></ul><p>下面补充说明一下Dream的四层门控：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260621192030373.png" alt="image-20260621192030373"></p><h2 id="s13-Background-Tasks"><a href="#s13-Background-Tasks" class="headerlink" title="s13 Background Tasks"></a>s13 Background Tasks</h2><blockquote><p>任务 —— 后台线程跑命令，异步执行，不阻塞主循环，完成后以tool_result的形式注入通知</p></blockquote><p>如何触发：</p><p>在这个教程中，是通过关键词来触发启动后台任务的，如下：</p><pre class="line-numbers language-Python" data-language="Python"><code class="language-Python">def is_slow_operation(tool_name: str, tool_input: dict) -&gt; bool:    &quot;&quot;&quot;Fallback heuristic: commands likely to take &gt; 30s.&quot;&quot;&quot;    if tool_name !&#x3D; &quot;bash&quot;:        return False    cmd &#x3D; tool_input.get(&quot;command&quot;, &quot;&quot;).lower()    slow_keywords &#x3D; [&quot;install&quot;, &quot;build&quot;, &quot;test&quot;, &quot;deploy&quot;, &quot;compile&quot;,                     &quot;docker build&quot;, &quot;pip install&quot;, &quot;npm install&quot;,                     &quot;cargo build&quot;, &quot;pytest&quot;, &quot;make&quot;]    return any(kw in cmd for kw in slow_keywords)<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>而在cc中，它的Bash工具schema有个<code>run_in_background</code>的bool参数，由模型自己判断是否这个命令要后台运行</p><p>后台执行与生命周期：</p><p>把工具调用包装成一个worker函数，放在<code>daemon</code>线程执行，后台任务都有唯一id，还没完成之前用占位语句来表示：<code>[Background task {bg_id} started] Result will be available when complete.</code></p><blockquote><p>关于<strong>daemon线程</strong>，这个含义是，如果是daemon，主线程退出的同时这个daemon会被强行杀掉；如果不是daemon，主线程退出时会等它跑完，否则不会退。</p><p>其实就是<strong>生命周期</strong>的管理，看整个程序退出时，要不要等这个后台线程跑完</p><p>关于<strong>bg_id</strong>的作用，这个个人认为应该是因为后台执行这个做法把一次工具调用拆成了**“启动started”<strong>和</strong>“完成completed”**两个不连续的事件，所以要一个唯一标识来将二者重新认成同一件事</p></blockquote><p>通知收集：</p><p>后台任务完成，会收集结果并格式化用<code>&lt;task_notification&gt;</code>包裹来通知</p><p>实际运行流程可表示如下：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622184048805.png" alt="image-20260622184048805"></p><p>关于cc中的实现：</p><p>cc中没有用多线程，是在一个Node.js&#x2F;Bun单线程循环中，其“后台”只是不“await”，是通过stdout&#x2F;stderr重定向到文件，让进程独立运行</p><p>cc中命令队列的通知格式是结构化的xml：</p><pre class="line-numbers language-XML" data-language="XML"><code class="language-XML">&lt;task_notification&gt;  &lt;status&gt;completed&lt;&#x2F;status&gt;  &lt;summary&gt;Background command &quot;npm test&quot; completed (exit code 0)&lt;&#x2F;summary&gt;&lt;&#x2F;task_notification&gt;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h2 id="s15-Agent-Teams"><a href="#s15-Agent-Teams" class="headerlink" title="s15 Agent Teams"></a>s15 Agent Teams</h2><blockquote><p>团队 —— 多Agent协作，靠<code>jsonl</code>文件收件箱 + 队友线程</p></blockquote><p>先看一下 这里 Multi-Agent跟之前的SubAgent的区别，见如下表格：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622184026524.png" alt="image-20260622184026524"></p><p>实现原理：</p><ol><li><code>MessageBus</code>，文件收件箱</li></ol><p>Leader和每个队友都有各自的jsonl文件收件箱，发消息就是往通信对象的jsonl append一行json，读消息就是读取文件+删除（消费式）</p><ol start="2"><li><code>spawn_teammate_thread</code>，启动队友</li></ol><p>每个队友抛在daemon线程中，有自己的system prompt、messages、简化的工具集，完成后自动汇报，例如<code>BUS.send(name, &quot;lead&quot;, summary)</code>把结果发给leader</p><ol start="3"><li>Lead的inbox注入，每轮主循环结束后Lead检查收件箱有没有新的队友消息</li></ol><p>举个例子有如下Agent协作流程图：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622184004661.png" alt="image-20260622184004661"></p><hr><p>cc实现：</p><ul><li>cc的实现还有个权限冒泡机制更为严谨，说明如下图：</li></ul><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183819220-1782124703413-1.png" alt="image-20260622183819220"></p><ul><li>15种结构化消息：</li></ul><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183738571.png" alt="image-20260622183738571"></p><ul><li><p>没有中央消息总线，就是文件系统：cc的收件箱实现是直接写其他Agent的inbox（路径：<code>~/.claude/teams/{teamName}/inboxes/{agentName}.json</code>），同时要用<strong>文件锁保证并发安全</strong></p></li><li><p>队友生命周期：</p></li></ul><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183720460.png" alt="image-20260622183720460"></p><ul><li>Team Config团队配置</li></ul><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183703208.png" alt="image-20260622183703208"></p><h2 id="s16-Team-Protocols"><a href="#s16-Team-Protocols" class="headerlink" title="s16 Team Protocols"></a>s16 Team Protocols</h2><blockquote><p>协议 —— Agent 之间的结构化握手，request-response模式驱动协商</p></blockquote><p>上一节还有个队友生命周期<code>（Lead → 队友）</code>的问题，假设做到一半想要停止队友，有可能队友文件写到一半被杀了，所以就需要关机握手与消息约定，让队友收尾后退出，也就是**团队协议，**每个人都要遵守。还有计划审批<code>（队友 → Lead）</code>，也需要协议来沟通确认。</p><p>这一节新增三个 func，<strong>ProtocolState</strong>（请求状态追踪）、<strong>dispatch_message</strong>（按消息类型路由到处理器）、<strong>match_response</strong>（通过 request_id 关联回复与请求，含类型校验）</p><p>实现原理：</p><ol><li>ProtocolState 请求状态</li></ol><p>这是协议握手的一个核心数据结构，如果了解计网的话对这些字段应该不陌生，是很经典的一个请求-响应的数据模型</p><pre class="line-numbers language-Python" data-language="Python"><code class="language-Python">@dataclassclass ProtocolState:    request_id: str      *# 唯一 ID，如 &quot;req_004281&quot;*    type: str            *# &quot;shutdown&quot; | &quot;plan_approval&quot;*    sender: str          *# 发起方*    target: str          *# 接收方*    status: str          *# pending | approved | rejected*    payload: str         *# 计划文本或关机原因*    created_at: float    *# 时间戳*pending_requests: dict[str, ProtocolState] &#x3D; {}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>request_id</code>是贯穿一次协议握手的key，请求带出去，回复收回来</p><p>下面是以关机为例的一个完整链路：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183603273.png" alt="image-20260622183603273"></p><ol start="2"><li>dispatch_messages 按消息类型路由</li></ol><p>新增协议类型的话就新增一个if分支</p><pre class="line-numbers language-Python" data-language="Python"><code class="language-Python">def handle_inbox_message(name, msg, messages):    msg_type &#x3D; msg.get(&quot;type&quot;, &quot;message&quot;)    req_id &#x3D; msg.get(&quot;metadata&quot;, {}).get(&quot;request_id&quot;, &quot;&quot;)    if msg_type &#x3D;&#x3D; &quot;shutdown_request&quot;:        BUS.send(name, &quot;lead&quot;, &quot;Shutting down.&quot;, &quot;shutdown_response&quot;,                 {&quot;request_id&quot;: req_id, &quot;approve&quot;: True})        return True   *# 停止循环*    if msg_type &#x3D;&#x3D; &quot;plan_approval_response&quot;:        approve &#x3D; msg[&quot;metadata&quot;].get(&quot;approve&quot;, False)        messages.append({&quot;role&quot;: &quot;user&quot;,            &quot;content&quot;: &quot;[Plan approved]&quot; if approve else &quot;[Plan rejected]&quot;})    return False       *# 继续循环*<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="3"><li>match_response 类型校验</li></ol><p>校验响应类型是否匹配请求类型</p><pre class="line-numbers language-Python" data-language="Python"><code class="language-Python">def match_response(response_type, request_id, approve):    state &#x3D; pending_requests.get(request_id)    if not state:        return    if state.type &#x3D;&#x3D; &quot;shutdown&quot; and response_type !&#x3D; &quot;shutdown_response&quot;:        return  *# type mismatch, skip*    if state.type &#x3D;&#x3D; &quot;plan_approval&quot; and response_type !&#x3D; &quot;plan_approval_response&quot;:        return    if state.status !&#x3D; &quot;pending&quot;:        return  *# already resolved, skip duplicate*    state.status &#x3D; &quot;approved&quot; if approve else &quot;rejected&quot;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="4"><li><p>consume_lead_inbox 统一inbox消费</p></li><li><p>idle_loop 队友等待（轮询）而不是退出</p></li></ol><p>cc实现：</p><p>如下图：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183933510.png" alt="image-20260622183933510"></p><h2 id="s17-Autonomous-Agents"><a href="#s17-Autonomous-Agents" class="headerlink" title="s17 Autonomous Agents"></a>s17 Autonomous Agents</h2><blockquote><p>自治 —— 不依赖Lead分配，空闲时轮询，有活就干</p></blockquote><p>前两节都是要Lead自己分配任务手动assign给队友，这节要实现队友自组织，自己发现、认领、完成</p><h2 id="s18-Worktree-Isolation"><a href="#s18-Worktree-Isolation" class="headerlink" title="s18 Worktree Isolation"></a>s18 Worktree Isolation</h2><blockquote><p>隔离 —— 并行执行的任务隔离，worktree 按ID绑定管目录</p></blockquote><h2 id="s19-MCP-Tools"><a href="#s19-MCP-Tools" class="headerlink" title="s19 MCP Tools"></a>s19 MCP Tools</h2><blockquote><p>插件 —— 外部能力通过标准协议接入，Agent 发现、组装、调用 mcp tools ，不需要知道tools是谁写的</p></blockquote><h2 id="s20-Comprehensive-Agent-Turn"><a href="#s20-Comprehensive-Agent-Turn" class="headerlink" title="s20 Comprehensive Agent Turn"></a>s20 Comprehensive Agent Turn</h2><blockquote><p>综合 —— 将前面学习的综合起来，在<strong>一个</strong>循环中，挂<strong>多个</strong>不同机制：工具、权限、记忆、任务、团队、插件</p></blockquote><p>一个长期工作的coding agent需要：</p><ul><li><p>工具分发和权限边界</p></li><li><p>hooks 扩展点</p></li><li><p>todo 计划和任务图</p></li><li><p>技能、记忆、系统 prompt 组装</p></li><li><p>压缩和错误恢复</p></li><li><p>后台任务和 cron 调度</p></li><li><p>团队、协议、自治认领</p></li><li><p>worktree 隔离</p></li><li><p>MCP 外部工具接入</p></li></ul><p>总览图：</p><p><img src="/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/image-20260622183459610.png" alt="image-20260622183459610"></p><pre class="line-numbers language-Plain" data-language="Plain"><div class="caption"><span>Text</span></div><code class="language-Plain">用户输入  → UserPromptSubmit hooks  → cron&#x2F;background 通知注入  → context compact  → memory + skills + MCP 状态组装 system prompt  → LLM  → has tool_use block?      否 → Stop hooks → 返回      是 → PreToolUse hooks + permission          → TOOL_HANDLERS &#x2F; MCP handlers &#x2F; background dispatch          → PostToolUse hooks          → tool_result &#x2F; task_notification 回 messages          → 下一轮<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]>
    </content>
    <id>https://junnhwan.github.io/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</id>
    <link href="https://junnhwan.github.io/2026/06/21/learn-claude-code-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    <published>2026-06-21T09:04:39.000Z</published>
    <summary>
      <![CDATA[<h2 id="Prologue"><a href="#Prologue" class="headerlink" title="Prologue"></a>Prologue</h2><p>这篇blog来记录学习 <a href="https://learn.shareai.run]]>
    </summary>
    <title>learn-claude-code 学习记录</title>
    <updated>2026-07-26T03:41:24.388Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day06"><a href="#Sky-takeout-Day06" class="headerlink" title="Sky-takeout  Day06"></a>Sky-takeout  Day06</h1><hr><blockquote><p>【前言】</p><p>Day06 的 Task ：首先是初步了解 HttpClient 的作用，并通过一个简单入门案例了解应用场景；然后介绍微信小程序开发，做好准备工作后再通过案例了解微信小程序的实际开发流程；最后是动手开发微信登录功能并导入商品浏览功能。</p></blockquote><span id="more"></span><h2 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h2><ul><li><p>HttpClient</p></li><li><p>微信小程序开发</p></li><li><p>微信登录</p></li><li><p>导入商品浏览功能代码</p></li></ul><h2 id="HttpClient"><a href="#HttpClient" class="headerlink" title="HttpClient"></a>HttpClient</h2><ol><li><p><strong>介绍：</strong></p><p> HttpClient 是Apache Jakarta Common 下的子项目，可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包，并且它支持 HTTP 协议最新的版本和建议。</p></li><li><p><strong>HttpClient作用：</strong></p></li></ol><ul><li>发送HTTP请求</li><li>接收响应数据</li></ul><ol start="3"><li><p><strong>HttpClient应用场景：</strong></p><p> 当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时，其实，应用程序本身并未实现这些功能，都是在应用程序里访问提供这些功能的服务，访问这些服务需要发送HTTP请求，并且接收响应数据，可通过HttpClient来实现。</p></li><li><p><strong>HttpClient的maven坐标：</strong></p></li></ol><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.apache.httpcomponents<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>httpclient<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>4.5.13<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><ol start="5"><li><strong>HttpClient的核心API：</strong></li></ol><ul><li>HttpClient：Http客户端对象类型，使用该类型对象可发起Http请求。</li><li>HttpClients：可认为是构建器，可创建HttpClient对象。</li><li>CloseableHttpClient：实现类，实现了HttpClient接口。</li><li>HttpGet：Get方式请求类型。</li><li>HttpPost：Post方式请求类型。</li></ul><ol start="6"><li><strong>HttpClient发送请求步骤：</strong></li></ol><ul><li>创建HttpClient对象</li><li>创建Http请求对象</li><li>调用HttpClient的execute方法发送请求</li></ul><ol start="7"><li><strong>入门案例</strong></li></ol><p>正常来说，首先，应该导入HttpClient相关的坐标，但在项目中，就算不导入，也可以使用相关的API。</p><p>因为在项目中已经引入了aliyun-sdk-oss坐标：</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>com.aliyun.oss<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>aliyun-sdk-oss<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>上述依赖的底层已经包含了HttpClient相关依赖。<br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014202838122.png" alt="image-20251014202838122" style="zoom:33%;"></p><ul><li><strong>GET方式请求</strong></li></ul><p><strong>实现步骤：</strong></p><ol><li>创建HttpClient对象</li><li>创建请求对象</li><li>发送请求，接受响应结果</li><li>解析结果</li><li>关闭资源</li></ol><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>test</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span></span><span class="token class-name">HttpEntity</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span>client<span class="token punctuation">.</span>methods<span class="token punctuation">.</span></span><span class="token class-name">CloseableHttpResponse</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span>client<span class="token punctuation">.</span>methods<span class="token punctuation">.</span></span><span class="token class-name">HttpGet</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span>impl<span class="token punctuation">.</span>client<span class="token punctuation">.</span></span><span class="token class-name">CloseableHttpClient</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span>impl<span class="token punctuation">.</span>client<span class="token punctuation">.</span></span><span class="token class-name">HttpClients</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>http<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">EntityUtils</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>jupiter<span class="token punctuation">.</span>api<span class="token punctuation">.</span></span><span class="token class-name">Test</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>test<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">SpringBootTest</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@SpringBootTest</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">HttpClientTest</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 测试通过httpclient发送GET方式的请求     */</span>    <span class="token annotation punctuation">@Test</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testGET</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">{</span>        <span class="token comment">//创建httpclient对象</span>        <span class="token class-name">CloseableHttpClient</span> httpClient <span class="token operator">=</span> <span class="token class-name">HttpClients</span><span class="token punctuation">.</span><span class="token function">createDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//创建请求对象</span>        <span class="token class-name">HttpGet</span> httpGet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpGet</span><span class="token punctuation">(</span><span class="token string">"http://localhost:8080/user/shop/status"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//发送请求，接受响应结果</span>        <span class="token class-name">CloseableHttpResponse</span> response <span class="token operator">=</span> httpClient<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>httpGet<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//获取服务端返回的状态码</span>        <span class="token keyword">int</span> statusCode <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getStatusLine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"服务端返回的状态码为："</span> <span class="token operator">+</span> statusCode<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">HttpEntity</span> entity <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getEntity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> body <span class="token operator">=</span> <span class="token class-name">EntityUtils</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"服务端返回的数据为："</span> <span class="token operator">+</span> body<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//关闭资源</span>        response<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        httpClient<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在访问<a href="http://localhost:8080/user/shop/status%E8%AF%B7%E6%B1%82%E6%97%B6%EF%BC%8C%E9%9C%80%E8%A6%81%E6%8F%90%E5%89%8D%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE%E3%80%82">http://localhost:8080/user/shop/status请求时，需要提前启动项目。</a></p><ul><li><strong>POST方式请求</strong></li></ul><p>在HttpClientTest中添加POST方式请求方法，相比GET请求来说，POST请求若携带参数需要封装请求体对象，并将该对象设置在请求对象中。</p><p><strong>实现步骤：</strong></p><ol><li>创建HttpClient对象</li><li>创建请求对象</li><li>发送请求，接收响应结果</li><li>解析响应结果</li><li>关闭资源</li></ol><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 测试通过httpclient发送POST方式的请求    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testPOST</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">{</span>       <span class="token comment">// 创建httpclient对象</span>       <span class="token class-name">CloseableHttpClient</span> httpClient <span class="token operator">=</span> <span class="token class-name">HttpClients</span><span class="token punctuation">.</span><span class="token function">createDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//创建请求对象</span>       <span class="token class-name">HttpPost</span> httpPost <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HttpPost</span><span class="token punctuation">(</span><span class="token string">"http://localhost:8080/admin/employee/login"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">JSONObject</span> jsonObject <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JSONObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       jsonObject<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"username"</span><span class="token punctuation">,</span><span class="token string">"admin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       jsonObject<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">,</span><span class="token string">"123456"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">StringEntity</span> entity <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringEntity</span><span class="token punctuation">(</span>jsonObject<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//指定请求编码方式</span>       entity<span class="token punctuation">.</span><span class="token function">setContentEncoding</span><span class="token punctuation">(</span><span class="token string">"utf-8"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//数据格式</span>       entity<span class="token punctuation">.</span><span class="token function">setContentType</span><span class="token punctuation">(</span><span class="token string">"application/json"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       httpPost<span class="token punctuation">.</span><span class="token function">setEntity</span><span class="token punctuation">(</span>entity<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//发送请求</span>       <span class="token class-name">CloseableHttpResponse</span> response <span class="token operator">=</span> httpClient<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>httpPost<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//解析返回结果</span>       <span class="token keyword">int</span> statusCode <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getStatusLine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatusCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"响应码为："</span> <span class="token operator">+</span> statusCode<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">HttpEntity</span> entity1 <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">getEntity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">String</span> body <span class="token operator">=</span> <span class="token class-name">EntityUtils</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span>entity1<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"响应数据为："</span> <span class="token operator">+</span> body<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token comment">//关闭资源</span>       response<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       httpClient<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="微信小程序开发"><a href="#微信小程序开发" class="headerlink" title="微信小程序开发"></a>微信小程序开发</h2><blockquote><p>具体介绍和注册小程序等准备工作这里就不放了，具体可以看课程或者讲义来大致了解一下，下面只讲一下小程序的结构。</p></blockquote><p>实际上，小程序的开发本质上属于前端开发，主要使用JavaScript开发</p><p><strong>小程序目录结构：</strong></p><p>小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成，必须放在项目的根目录，如下：<br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014203656810.png" alt="image-20251014203656810"></p><p><strong>文件说明：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014203718477.png" alt="image-20251014203718477" style="zoom:50%;"></p><p>**app.js：**必须存在，主要存放小程序的逻辑代码</p><p>**app.json：**必须存在，小程序配置文件，主要存放小程序的公共配置</p><p><strong>app.wxss:</strong>  非必须存在，主要存放小程序公共样式表，类似于前端的CSS样式</p><p>对小程序主体三个文件了解后，其实一个小程序又有多个页面。比如说，有商品浏览页面、购物车的页面、订单支付的页面、商品的详情页面等等。那这些页面会放在哪呢？</p><p><strong>小程序页面：</strong></p><p>会存放在pages目录，每个<strong>小程序页面</strong>主要由四个文件组成：<br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014203811617.png" alt="image-20251014203811617"></p><p><strong>文件说明：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014203833117.png" alt="image-20251014203833117" style="zoom:50%;"></p><p>**js文件：**必须存在，存放页面业务逻辑代码，编写的js代码。</p><p>**wxml文件：**必须存在，存放页面结构，主要是做页面布局，页面效果展示的11，类似于HTML页面。</p><p>**json文件：**非必须，存放页面相关的配置。</p><p>**wxss文件：**非必须，存放页面样式表，相当于CSS文件。</p><h2 id="微信登录"><a href="#微信登录" class="headerlink" title="微信登录"></a>微信登录</h2><h3 id="分析与设计"><a href="#分析与设计" class="headerlink" title="分析与设计"></a>分析与设计</h3><p><strong>原型分析：</strong></p><p>​用户进入到小程序的时候，微信授权登录之后才能点餐。需要获取当前微信用户的相关信息，比如昵称、头像等，这样才能够进入到小程序进行下单操作。是基于微信登录来实现小程序的登录功能，没有采用传统账户密码登录的方式。若第一次使用小程序来点餐，就是一个新用户，需要把这个新的用户保存到数据库当中完成自动注册。</p><p><strong>业务规则：</strong></p><ul><li>基于微信登录实现小程序的登录功能</li><li>如果是新用户需要自动完成注册</li></ul><p><strong>接口设计：</strong></p><p>通过微信登录的流程，如果要完成微信登录的话，最终就要获得微信用户的openid。在小程序端获取授权码后，向后端服务发送请求，并携带授权码，这样后端服务在收到授权码后，就可以去请求微信接口服务。最终，后端向小程序返回openid和token等数据。</p><p>基于上述的登录流程，就可以设计出该接口的<strong>请求参数</strong>和<strong>返回数据</strong>。</p><p>**说明：**请求路径&#x2F;user&#x2F;user&#x2F;login,第一个user代表用户端，第二个user代表用户模块。<br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014230151767.png" alt="image-20251014230151767"></p><p><strong>表设计：</strong></p><p>当用户第一次使用小程序时，会完成自动注册，把用户信息存储到<strong>user</strong>表中。</p><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>openid</td><td>varchar(45)</td><td>微信用户的唯一标识</td><td></td></tr><tr><td>name</td><td>varchar(32)</td><td>用户姓名</td><td></td></tr><tr><td>phone</td><td>varchar(11)</td><td>手机号</td><td></td></tr><tr><td>sex</td><td>varchar(2)</td><td>性别</td><td></td></tr><tr><td>id_number</td><td>varchar(18)</td><td>身份证号</td><td></td></tr><tr><td>avatar</td><td>varchar(500)</td><td>微信用户头像路径</td><td></td></tr><tr><td>create_time</td><td>datetime</td><td>注册时间</td><td></td></tr></tbody></table><p>**说明：**手机号字段比较特殊，个人身份注册的小程序没有权限获取到微信用户的手机号。如果是以企业的资质<br>注册的小程序就能够拿到微信用户的手机号。</p><h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a>代码编写</h3><h4 id="定义相关配置"><a href="#定义相关配置" class="headerlink" title="定义相关配置"></a>定义相关配置</h4><p><strong>配置微信登录所需配置项：</strong></p><p>application-dev.yml</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">sky</span><span class="token punctuation">:</span>  <span class="token key atrule">wechat</span><span class="token punctuation">:</span>    <span class="token key atrule">appid</span><span class="token punctuation">:</span> <span class="token important">*****(你注册的appid)</span>    <span class="token key atrule">secret</span><span class="token punctuation">:</span> <span class="token important">*****(你注册的密钥)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>application.yml</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">sky</span><span class="token punctuation">:</span>  <span class="token key atrule">wechat</span><span class="token punctuation">:</span>    <span class="token key atrule">appid</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.wechat.appid<span class="token punctuation">}</span>    <span class="token key atrule">secret</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.wechat.secret<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><strong>配置为微信用户生成jwt令牌时使用的配置项：</strong></p><p>application.yml</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">sky</span><span class="token punctuation">:</span>  <span class="token key atrule">jwt</span><span class="token punctuation">:</span>    <span class="token comment"># 设置jwt签名加密时使用的秘钥</span>    <span class="token key atrule">admin-secret-key</span><span class="token punctuation">:</span> itcast    <span class="token comment"># 设置jwt过期时间</span>    <span class="token key atrule">admin-ttl</span><span class="token punctuation">:</span> <span class="token number">7200000</span>    <span class="token comment"># 设置前端传递过来的令牌名称</span>    <span class="token key atrule">admin-token-name</span><span class="token punctuation">:</span> token    <span class="token key atrule">user-secret-key</span><span class="token punctuation">:</span> itheima    <span class="token key atrule">user-ttl</span><span class="token punctuation">:</span> <span class="token number">7200000</span>    <span class="token key atrule">user-token-name</span><span class="token punctuation">:</span> authentication<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="DTO、VO以及三层架构"><a href="#DTO、VO以及三层架构" class="headerlink" title="DTO、VO以及三层架构"></a>DTO、VO以及三层架构</h4><p><strong>根据传入参数设计DTO类：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015084154614.png" alt="image-20251015084154614" style="zoom:50%;"></p><p>在sky-pojo模块，UserLoginDTO.java已定义</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">Data</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">Serializable</span></span><span class="token punctuation">;</span><span class="token comment">/** * C端用户登录 */</span><span class="token annotation punctuation">@Data</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserLoginDTO</span> <span class="token keyword">implements</span> <span class="token class-name">Serializable</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> code<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>根据返回数据设计VO类：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251014232329374.png" alt="image-20251014232329374" style="zoom:50%;"></p><p>在sky-pojo模块，UserLoginVO.java已定义</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>vo</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">AllArgsConstructor</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">Builder</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">Data</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span></span><span class="token class-name">NoArgsConstructor</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">Serializable</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Data</span><span class="token annotation punctuation">@Builder</span><span class="token annotation punctuation">@NoArgsConstructor</span><span class="token annotation punctuation">@AllArgsConstructor</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserLoginVO</span> <span class="token keyword">implements</span> <span class="token class-name">Serializable</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token class-name">Long</span> id<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> openid<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> token<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>UserController.java</strong>：实现 装配 JWT 令牌和 openid 和用户id 返回给前端</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>user</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">JwtClaimsConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">UserLoginDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">User</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>properties<span class="token punctuation">.</span></span><span class="token class-name">JwtProperties</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">UserService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>utils<span class="token punctuation">.</span></span><span class="token class-name">JwtUtil</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>vo<span class="token punctuation">.</span></span><span class="token class-name">UserLoginVO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">PostMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestBody</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RestController</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">HashMap</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Map</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@Slf4j</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/user/user"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"C端用户相关接口"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserController</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">UserService</span> userService<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">JwtProperties</span> jwtProperties<span class="token punctuation">;</span>    <span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"用户微信登录"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">UserLoginVO</span><span class="token punctuation">></span></span> <span class="token function">login</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">UserLoginDTO</span> userLoginDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"用户微信登录：{}"</span><span class="token punctuation">,</span> userLoginDTO<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">User</span> user <span class="token operator">=</span> userService<span class="token punctuation">.</span><span class="token function">wxlogin</span><span class="token punctuation">(</span>userLoginDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 上面 user 从数据库中获得了 用于微信登录的 openid 和用户主键 id</span>        <span class="token comment">// 下面再创建一个 Jwt 令牌，然后封装成 UserLoginVO返回给前端</span>        <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> claims <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 这里能获取id，因为再数据库层用了useGeneratedKeys, 并将id绑定再实体类的id属性</span>        claims<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token class-name">JwtClaimsConstant</span><span class="token punctuation">.</span><span class="token constant">USER_ID</span><span class="token punctuation">,</span> user<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> token <span class="token operator">=</span> <span class="token class-name">JwtUtil</span><span class="token punctuation">.</span><span class="token function">createJWT</span><span class="token punctuation">(</span>jwtProperties<span class="token punctuation">.</span><span class="token function">getUserSecretKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> jwtProperties<span class="token punctuation">.</span><span class="token function">getUserTtl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> claims<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">UserLoginVO</span> userLoginVO <span class="token operator">=</span> <span class="token class-name">UserLoginVO</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>                                            <span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                                            <span class="token punctuation">.</span><span class="token function">openid</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token function">getOpenid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                                            <span class="token punctuation">.</span><span class="token function">token</span><span class="token punctuation">(</span>token<span class="token punctuation">)</span>                                            <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>userLoginVO<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 将封装好的VO返回</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>UserServiceImpl.java</strong> : 实现获取微信用户的openid和微信登录功能，若是新用户直接注册</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span>impl</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>alibaba<span class="token punctuation">.</span>fastjson<span class="token punctuation">.</span></span><span class="token class-name">JSON</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>alibaba<span class="token punctuation">.</span>fastjson<span class="token punctuation">.</span></span><span class="token class-name">JSONObject</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">MessageConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">UserLoginDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">User</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>exception<span class="token punctuation">.</span></span><span class="token class-name">LoginFailedException</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">UserMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>properties<span class="token punctuation">.</span></span><span class="token class-name">WeChatProperties</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">UserService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>utils<span class="token punctuation">.</span></span><span class="token class-name">HttpClientUtil</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Service</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>time<span class="token punctuation">.</span></span><span class="token class-name">LocalDateTime</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">HashMap</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Map</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Service</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">UserService</span> <span class="token punctuation">{</span>    <span class="token comment">// 微信接口服务地址</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> <span class="token constant">WX_LOGIN</span> <span class="token operator">=</span> <span class="token string">"https://api.weixin.qq.com/sns/jscode2session"</span><span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">UserMapper</span> userMapper<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">WeChatProperties</span> weChatProperties<span class="token punctuation">;</span>    <span class="token comment">/**     * 用户微信登录     * @param userLoginDTO 用户登录信息     * @return 用户信息     */</span>    <span class="token annotation punctuation">@Override</span>    <span class="token keyword">public</span> <span class="token class-name">User</span> <span class="token function">wxlogin</span><span class="token punctuation">(</span><span class="token class-name">UserLoginDTO</span> userLoginDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment">// 根据微信授权码 code (在入参DTO传来的)获取用户唯一标识 openid</span>        <span class="token class-name">String</span> openid <span class="token operator">=</span> <span class="token function">getOpenid</span><span class="token punctuation">(</span>userLoginDTO<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// openid为空则抛出业务异常</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>openid <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">LoginFailedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">LOGIN_FAILED</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token class-name">User</span> user <span class="token operator">=</span> userMapper<span class="token punctuation">.</span><span class="token function">getByOpenid</span><span class="token punctuation">(</span>openid<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 判断用户信息是否存在数据库中，不存在说明是新用户，则进行注册 即插入用户信息</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>user <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">// 先将用户信息填充好，然后插入数据库</span>            user <span class="token operator">=</span> <span class="token class-name">User</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>                    <span class="token punctuation">.</span><span class="token function">openid</span><span class="token punctuation">(</span>openid<span class="token punctuation">)</span>                    <span class="token punctuation">.</span><span class="token function">createTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            userMapper<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token keyword">return</span> user<span class="token punctuation">;</span> <span class="token comment">// 返回用户信息</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 调用微信接口服务，获取用户的openid     * @param code 用户登录信息中的微信授权码     * @return openid 字符串     */</span>    <span class="token comment">// 利用 httpclient 工具类向微信接口服务发起请求，以获取用户唯一标识 openid</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> <span class="token function">getOpenid</span><span class="token punctuation">(</span><span class="token class-name">String</span> code<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> map <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"appid"</span><span class="token punctuation">,</span> weChatProperties<span class="token punctuation">.</span><span class="token function">getAppid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"secret"</span><span class="token punctuation">,</span> weChatProperties<span class="token punctuation">.</span><span class="token function">getSecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"js_code"</span><span class="token punctuation">,</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span>        map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"grant_type"</span><span class="token punctuation">,</span> <span class="token string">"authorization_code"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> json <span class="token operator">=</span> <span class="token class-name">HttpClientUtil</span><span class="token punctuation">.</span><span class="token function">doGet</span><span class="token punctuation">(</span><span class="token constant">WX_LOGIN</span><span class="token punctuation">,</span> map<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">JSONObject</span> jsonObject <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parseObject</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> jsonObject<span class="token punctuation">.</span><span class="token function">getString</span><span class="token punctuation">(</span><span class="token string">"openid"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 将微信接口服务返回的json中的openid值返回</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>UserMapper.java</strong>：根据openid查询用户是否存在以及实现插入，用于 Service 层判断该用户是否要注册，新用户就往表插入数据代表注册</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">User</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Select</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">UserMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 根据 openid 查询用户信息， 存在即可登录，不存在则要注册，后续 insert 用户信息     * @param openid 微信用户唯一标识 openid     * @return 用户信息     */</span>    <span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from user where openid = #{openid};"</span><span class="token punctuation">)</span>    <span class="token class-name">User</span> <span class="token function">getByOpenid</span><span class="token punctuation">(</span><span class="token class-name">String</span> openid<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 插入用户信息， 即新用户进行注册操作     * @param user 用户信息     */</span>    <span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token class-name">User</span> user<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>UserMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" ?></span><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">mapper</span> <span class="token name">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mapper</span> <span class="token attr-name">namespace</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.mapper.UserMapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>insert</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>insert<span class="token punctuation">"</span></span> <span class="token attr-name">useGeneratedKeys</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">keyProperty</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        insert into user (openid, name, phone, sex, id_number, avatar, create_time)        values        (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>insert</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mapper</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="配置拦截器"><a href="#配置拦截器" class="headerlink" title="配置拦截器"></a>配置拦截器</h4><p>**编写拦截器JwtTokenUserInterceptor：**统一拦截用户端发送的请求并进行jwt校验</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>interceptor</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">JwtClaimsConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">BaseContext</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>properties<span class="token punctuation">.</span></span><span class="token class-name">JwtProperties</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>utils<span class="token punctuation">.</span></span><span class="token class-name">JwtUtil</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>jsonwebtoken<span class="token punctuation">.</span></span><span class="token class-name">Claims</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>messaging<span class="token punctuation">.</span>handler<span class="token punctuation">.</span></span><span class="token class-name">HandlerMethod</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Component</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span></span><span class="token class-name">HandlerInterceptor</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">javax<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>http<span class="token punctuation">.</span></span><span class="token class-name">HttpServletRequest</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">javax<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>http<span class="token punctuation">.</span></span><span class="token class-name">HttpServletResponse</span></span><span class="token punctuation">;</span><span class="token comment">/** * 用户端JWT令牌拦截器 */</span><span class="token annotation punctuation">@Component</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JwtTokenUserInterceptor</span> <span class="token keyword">implements</span> <span class="token class-name">HandlerInterceptor</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">JwtProperties</span> jwtProperties<span class="token punctuation">;</span>    <span class="token comment">/**     * 校验jwt， 在目标方法执行之前执行     * @param request 请求对象     * @param response 响应对象     * @param handler 处理器（controller方法）     * @return true：放行，继续执行后续操作；false：拦截，不继续执行后续操作     * @throws Exception 异常     */</span>    <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">preHandle</span><span class="token punctuation">(</span><span class="token class-name">HttpServletRequest</span> request<span class="token punctuation">,</span> <span class="token class-name">HttpServletResponse</span> response<span class="token punctuation">,</span> <span class="token class-name">Object</span> handler<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>        <span class="token comment">//判断当前拦截到的是Controller的方法还是其他资源</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>handler <span class="token keyword">instanceof</span> <span class="token class-name">HandlerMethod</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">//当前拦截到的不是动态方法，直接放行</span>            <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token comment">//1、从请求头中获取令牌</span>        <span class="token class-name">String</span> token <span class="token operator">=</span> request<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span>jwtProperties<span class="token punctuation">.</span><span class="token function">getUserTokenName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//2、校验令牌</span>        <span class="token keyword">try</span> <span class="token punctuation">{</span>            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"jwt校验:{}"</span><span class="token punctuation">,</span> token<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token class-name">Claims</span> claims <span class="token operator">=</span> <span class="token class-name">JwtUtil</span><span class="token punctuation">.</span><span class="token function">parseJWT</span><span class="token punctuation">(</span>jwtProperties<span class="token punctuation">.</span><span class="token function">getUserSecretKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> token<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token class-name">Long</span> userId <span class="token operator">=</span> <span class="token class-name">Long</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>claims<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">JwtClaimsConstant</span><span class="token punctuation">.</span><span class="token constant">USER_ID</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"当前用户的id：{}"</span><span class="token punctuation">,</span> userId<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">setCurrentId</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment">//3、通过，放行</span>            <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">//4、不通过，响应 401 状态码</span>            response<span class="token punctuation">.</span><span class="token function">setStatus</span><span class="token punctuation">(</span><span class="token number">401</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>在WebMvcConfiguration配置类中注册拦截器：</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">JwtTokenUserInterceptor</span> jwtTokenUserInterceptor<span class="token punctuation">;</span><span class="token comment">/**     * 注册自定义拦截器     * @param registry     */</span>    <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">addInterceptors</span><span class="token punctuation">(</span><span class="token class-name">InterceptorRegistry</span> registry<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"开始注册自定义拦截器..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//.........</span>        registry<span class="token punctuation">.</span><span class="token function">addInterceptor</span><span class="token punctuation">(</span>jwtTokenUserInterceptor<span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">addPathPatterns</span><span class="token punctuation">(</span><span class="token string">"/user/**"</span><span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">excludePathPatterns</span><span class="token punctuation">(</span><span class="token string">"/user/user/login"</span><span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">excludePathPatterns</span><span class="token punctuation">(</span><span class="token string">"/user/shop/status"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><p>微信发送授权码 code 给服务端 Controller</p><p>Service中根据 code 获取 openid 并判断是否需要注册新用户</p><h2 id="导入商品浏览功能代码"><a href="#导入商品浏览功能代码" class="headerlink" title="导入商品浏览功能代码"></a>导入商品浏览功能代码</h2><h3 id="分析与设计-1"><a href="#分析与设计-1" class="headerlink" title="分析与设计"></a>分析与设计</h3><p>根据原型图先<strong>粗粒度</strong>设计接口，共包含4个接口。</p><p><strong>接口设计：</strong></p><ul><li>查询分类</li><li>根据分类id查询菜品</li><li>根据分类id查询套餐</li><li>根据套餐id查询包含的菜品</li></ul><p>接下来<strong>细粒度</strong>分析每个接口，明确每个接口的请求方式、请求路径、传入参数和返回值。</p><p><strong>1). 查询分类</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015103046044.png" alt="image-20251015103046044" style="zoom:50%;"></p><p><strong>2). 根据分类id查询菜品</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015103217600.png" alt="image-20251015103217600" style="zoom:50%;"></p><p><strong>3). 根据分类id查询套餐</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015103337195.png" alt="image-20251015103337195" style="zoom:50%;"></p><p><strong>4). 根据套餐id查询包含的菜品</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015103351466.png" alt="image-20251015103351466" style="zoom:50%;"></p><h3 id="代码导入"><a href="#代码导入" class="headerlink" title="代码导入"></a>代码导入</h3><p>导入资料中的商品浏览功能代码即可可按照mapper–&gt;service–&gt;controller依次导入，这样代码不会显示相应的报错。</p><h3 id="功能测试-1"><a href="#功能测试-1" class="headerlink" title="功能测试"></a>功能测试</h3><p><strong>菜品和套餐分类查询：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015105411465.png" alt="image-20251015105411465"></p><p><strong>具体分类下的菜品查询：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015105419948.png" alt="image-20251015105419948"></p><p><strong>菜品口味查询：</strong><br><img src="/2025/10/15/sky-takeout-day06-note/image-20251015105426391.png" alt="image-20251015105426391"></p><h2 id="附：其他盲区知识补充"><a href="#附：其他盲区知识补充" class="headerlink" title="附：其他盲区知识补充"></a>附：其他盲区知识补充</h2><h3 id="Service注解与Mapper注解"><a href="#Service注解与Mapper注解" class="headerlink" title="Service注解与Mapper注解"></a>Service注解与Mapper注解</h3><p>在 MyBatis 框架中，<code>@Mapper</code>注解之所以加在<strong>Mapper 接口</strong>上，是由其<strong>动态代理的实现机制</strong>决定的，具体可从以下几点分析：</p><h4 id="1-MyBatis-的-“接口-代理”-设计逻辑"><a href="#1-MyBatis-的-“接口-代理”-设计逻辑" class="headerlink" title="1. MyBatis 的 “接口 - 代理” 设计逻辑"></a>1. MyBatis 的 “接口 - 代理” 设计逻辑</h4><p>MyBatis 采用 <strong>“接口 + 动态代理”</strong> 的方式工作：</p><ul><li>我们定义的<code>Mapper接口</code>仅声明 SQL 操作的方法签名（如<code>List&lt;User&gt; selectAll()</code>）。</li><li>MyBatis 在运行时，会通过<strong>动态代理技术</strong>，为每个标注<code>@Mapper</code>的接口<strong>自动生成实现类（代理类）</strong>。这个代理类会负责解析 XML 映射文件（或注解式 SQL）、执行 SQL、处理结果映射等核心操作。</li></ul><p>因此，<code>@Mapper</code>注解加在接口上，是为了告诉 MyBatis：<strong>“需要为这个接口生成代理实现类，以便执行 SQL 操作”</strong>。</p><h4 id="2-与-Service的本质区别"><a href="#2-与-Service的本质区别" class="headerlink" title="2. 与@Service的本质区别"></a>2. 与<code>@Service</code>的本质区别</h4><p><code>@Service</code>是 Spring 的注解，用于标记<strong>具体的服务实现类</strong>，目的是让 Spring IoC 容器识别并管理其对象实例（创建对象、依赖注入等）。而<code>@Mapper</code>是 MyBatis 的注解，用于标记<strong>需要生成代理的接口</strong>，目的是让 MyBatis 为接口动态生成 “能执行 SQL 的实现类”。</p><p>简单来说：</p><ul><li><code>@Service</code>针对<strong>具体类</strong>，由 Spring 直接管理实例；</li><li><code>@Mapper</code>针对<strong>接口</strong>，由 MyBatis 通过动态代理生成实现类。</li></ul><h4 id="补充：简化配置的-MapperScan"><a href="#补充：简化配置的-MapperScan" class="headerlink" title="补充：简化配置的@MapperScan"></a>补充：简化配置的<code>@MapperScan</code></h4><p>为了避免在每个 Mapper 接口上重复加<code>@Mapper</code>，还可以在 Spring Boot 的配置类上使用<code>@MapperScan</code>注解，指定 Mapper 接口的扫描包路径，例如：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@SpringBootApplication</span><span class="token annotation punctuation">@MapperScan</span><span class="token punctuation">(</span><span class="token string">"com.example.mapper"</span><span class="token punctuation">)</span> <span class="token comment">// 扫描该包下的所有Mapper接口</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Application</span> <span class="token punctuation">{</span>    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">SpringApplication</span><span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token class-name">Application</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>综上，<code>@Mapper</code>加在接口上是 MyBatis“接口 + 动态代理” 设计的必然结果，和<code>@Service</code>的使用场景、框架机制存在本质差异。</p>]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/15/sky-takeout-day06-note/</id>
    <link href="https://junnhwan.github.io/2025/10/15/sky-takeout-day06-note/"/>
    <published>2025-10-15T09:55:44.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day06"><a href="#Sky-takeout-Day06" class="headerlink" title="Sky-takeout  Day06"></a>Sky-takeout  Day06</h1><hr>
<blockquote>
<p>【前言】</p>
<p>Day06 的 Task ：首先是初步了解 HttpClient 的作用，并通过一个简单入门案例了解应用场景；然后介绍微信小程序开发，做好准备工作后再通过案例了解微信小程序的实际开发流程；最后是动手开发微信登录功能并导入商品浏览功能。</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day06-note</title>
    <updated>2026-07-26T03:41:24.387Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day05"><a href="#Sky-takeout-Day05" class="headerlink" title="Sky-takeout  Day05"></a>Sky-takeout  Day05</h1><hr><blockquote><p>【前言】</p><p>Day05 的 Task 是 Redis 的入门，了解 Redis 数据类型、常用命令，如何在Java中使用Redis，以及 Redis 在项目中的应用场景，通过项目案例使用 Redis。</p></blockquote><span id="more"></span><h2 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h2><ul><li>Redis入门</li><li>Redis数据类型</li><li>Redis常用命令</li><li>在Java中操作redis</li><li>店铺营业状态设置</li></ul><h2 id="Redis入门"><a href="#Redis入门" class="headerlink" title="Redis入门"></a>Redis入门</h2><ol><li><p><strong>简介：<strong>Redis是一个基于</strong>内存</strong>的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的<strong>存储中间件</strong>。</p></li><li><p><strong>主要特点：</strong></p></li></ol><ul><li>基于内存存储，读写性能高  </li><li>适合存储热点数据（热点商品、资讯、新闻）</li><li>企业应用广泛</li></ul><p>Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库，官方提供的数据是可以达到100000+的QPS（每秒内查询次数）。它存储的value类型比较丰富，也被称为结构化的NoSql数据库。</p><p>NoSql（Not Only SQL），不仅仅是SQL，泛指<strong>非关系型数据库</strong>。NoSql数据库并不是要取代关系型数据库，而是关系型数据库的补充。</p><ol start="3"><li><strong>Redis服务启动与停止：</strong></li></ol><p><strong>windows 版</strong> Redis 服务启动命令：<strong>redis-server.exe redis.windows.conf</strong></p><p>Redis服务默认端口号为 <strong>6379</strong> ，通过快捷键<strong>Ctrl + C</strong> 即可停止Redis服务</p><p>当Redis服务启动成功后，可通过客户端进行连接。客户端连接命令：redis-cli.exe</p><p>通过redis-cli.exe命令默认连接的是本地的redis服务，并且使用默认6379端口。也可以通过指定如下参数连接：</p><ul><li><p>-h ip地址</p></li><li><p>-p 端口号</p></li><li><p>-a 密码（如果需要）</p></li></ul><ol start="4"><li><strong>修改Redis配置文件：</strong></li></ol><p>若要设置Redis服务密码，修改redis.windows.conf</p><pre class="line-numbers language-none"><code class="language-none">requirepass 123456<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><strong>注意：</strong></p><ul><li>修改密码后需要重启Redis服务才能生效</li><li>Redis配置文件中 # 表示注释</li></ul><p>重启Redis后，再次连接Redis时，需加上密码，否则连接失败。</p><h2 id="Redis数据类型"><a href="#Redis数据类型" class="headerlink" title="Redis数据类型"></a>Redis数据类型</h2><ol><li><strong>五种常用数据类型介绍</strong></li></ol><p>Redis存储的是key-value结构的数据，其中key是字符串类型，value有5种常用的数据类型：</p><ul><li>字符串 string</li><li>哈希 hash</li><li>列表 list</li><li>集合 set</li><li>有序集合 sorted set &#x2F; zset</li></ul><ol start="2"><li><strong>数据类型特点：</strong> <img src="/2025/10/14/sky-takeout-day05-note/image-20251014180847137.png" alt="image-20251014180847137" style="zoom:50%;"></li></ol><p><strong>解释说明：</strong></p><ul><li>字符串(string)：普通字符串，Redis中最简单的数据类型</li><li>哈希(hash)：也叫散列，类似于Java中的HashMap结构</li><li>列表(list)：按照插入顺序排序，可以有重复元素，类似于Java中的LinkedList</li><li>集合(set)：无序集合，没有重复元素，类似于Java中的HashSet</li><li>有序集合(sorted set&#x2F;zset)：集合中每个元素关联一个分数(score)，根据分数升序排序，没有重复元素</li></ul><h2 id="Redis常用命令"><a href="#Redis常用命令" class="headerlink" title="Redis常用命令"></a>Redis常用命令</h2><p>以下只介绍 常用的 命令，更多命令可以参考Redis中文网：<a href="https://www.redis.net.cn/">https://www.redis.net.cn</a></p><h3 id="字符串操作命令"><a href="#字符串操作命令" class="headerlink" title="字符串操作命令"></a>字符串操作命令</h3><p>Redis 中字符串类型常用命令：</p><ul><li><strong>SET</strong> key value          设置指定key的值</li><li><strong>GET</strong> key                                        获取指定key的值</li><li><strong>SETEX</strong> key seconds value         设置指定key的值，并将 key 的过期时间设为 seconds 秒</li><li><strong>SETNX</strong> key value                        只有在 key    不存在时设置 key 的值</li></ul><h3 id="哈希操作命令"><a href="#哈希操作命令" class="headerlink" title="哈希操作命令"></a>哈希操作命令</h3><p>Redis hash 是一个string类型的 field 和 value 的映射表，hash特别适合用于存储对象，常用命令：</p><ul><li><strong>HSET</strong> key field value             将哈希表 key 中的字段 field 的值设为 value</li><li><strong>HGET</strong> key field                       获取存储在哈希表中指定字段的值</li><li><strong>HDEL</strong> key field                       删除存储在哈希表中的指定字段</li><li><strong>HKEYS</strong> key                              获取哈希表中所有字段</li><li><strong>HVALS</strong> key                              获取哈希表中所有值</li></ul><h3 id="列表操作命令"><a href="#列表操作命令" class="headerlink" title="列表操作命令"></a>列表操作命令</h3><p>Redis 列表是简单的字符串列表，按照插入顺序排序，常用命令：</p><ul><li><strong>LPUSH</strong> key value1 [value2]         将一个或多个值插入到列表头部</li><li><strong>LRANGE</strong> key start stop                获取列表指定范围内的元素</li><li><strong>RPOP</strong> key                                       移除并获取列表最后一个元素</li><li><strong>LLEN</strong> key                                        获取列表长度</li><li><strong>BRPOP</strong> key1 [key2 ] timeout       移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超    时或发现可弹出元素为止</li></ul><h3 id="集合操作命令"><a href="#集合操作命令" class="headerlink" title="集合操作命令"></a>集合操作命令</h3><p>Redis set 是string类型的无序集合。集合成员是唯一的，这就意味着集合中不能出现重复的数据，常用命令：</p><ul><li><strong>SADD</strong> key member1 [member2]            向集合添加一个或多个成员</li><li><strong>SMEMBERS</strong> key                                         返回集合中的所有成员</li><li><strong>SCARD</strong> key                                                  获取集合的成员数</li><li><strong>SINTER</strong> key1 [key2]                                   返回给定所有集合的交集</li><li><strong>SUNION</strong> key1 [key2]                                 返回所有给定集合的并集</li><li><strong>SREM</strong> key member1 [member2]            移除集合中一个或多个成员</li></ul><h3 id="有序集合操作命令"><a href="#有序集合操作命令" class="headerlink" title="有序集合操作命令"></a>有序集合操作命令</h3><p>Redis有序集合是string类型元素的集合，且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令：</p><p>常用命令：</p><ul><li><strong>ZADD</strong> key score1 member1 [score2 member2]     向有序集合添加一个或多个成员</li><li><strong>ZRANGE</strong> key start stop [WITHSCORES]                     通过索引区间返回有序集合中指定区间内的成员</li><li><strong>ZINCRBY</strong> key increment member                              有序集合中对指定成员的分数加上增量 increment</li><li><strong>ZREM</strong> key member [member …]                                移除有序集合中的一个或多个成员</li></ul><h3 id="通用命令"><a href="#通用命令" class="headerlink" title="通用命令"></a>通用命令</h3><p>Redis的通用命令是不分数据类型的，都可以使用的命令：</p><ul><li>KEYS pattern 查找所有符合给定模式( pattern)的 key </li><li>EXISTS key 检查给定 key 是否存在</li><li>TYPE key 返回 key 所储存的值的类型</li><li>DEL key 该命令用于在 key 存在是删除 key</li></ul><h2 id="在Java中操作redis"><a href="#在Java中操作redis" class="headerlink" title="在Java中操作redis"></a>在Java中操作redis</h2><h3 id="Redis的Java客户端：Spring-Data-Redis"><a href="#Redis的Java客户端：Spring-Data-Redis" class="headerlink" title="Redis的Java客户端：Spring Data Redis"></a>Redis的Java客户端：Spring Data Redis</h3><p>Spring Data Redis 是 Spring 的一部分，提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务，对 Redis 底层开发包进行了高度封装。在 Spring 项目中，可以使用Spring Data Redis来简化 Redis 操作。</p><p>Spring Boot提供了对应的Starter，maven坐标：</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-data-redis<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>Spring Data Redis中提供了一个高度封装的类：<strong>RedisTemplate</strong>，对相关api进行了归类封装,将同一类型操作封装为operation接口，具体分类如下：</p><ul><li>ValueOperations：string数据操作</li><li>SetOperations：set类型数据操作</li><li>ZSetOperations：zset类型数据操作</li><li>HashOperations：hash类型的数据操作</li><li>ListOperations：list类型的数据操作</li></ul><h3 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h3><p>进入到sky-server模块</p><p><strong>1). 导入Spring Data Redis的maven坐标</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>     <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>     <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-data-redis<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><strong>2). 配置Redis数据源</strong></p><p>在application-dev.yml中添加</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">sky</span><span class="token punctuation">:</span>  <span class="token key atrule">redis</span><span class="token punctuation">:</span>    <span class="token key atrule">host</span><span class="token punctuation">:</span> localhost    <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">6379</span>    <span class="token key atrule">password</span><span class="token punctuation">:</span> <span class="token number">123456</span>    <span class="token key atrule">database</span><span class="token punctuation">:</span> <span class="token number">10</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>解释说明：</strong></p><p>database:指定使用Redis的哪个数据库，Redis服务启动后默认有16个数据库，编号分别是从0到15。</p><p>可以通过修改Redis配置文件来指定数据库的数量。</p><p>在application.yml中添加读取application-dev.yml中的相关Redis配置</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">spring</span><span class="token punctuation">:</span>  <span class="token key atrule">profiles</span><span class="token punctuation">:</span>    <span class="token key atrule">active</span><span class="token punctuation">:</span> dev  <span class="token key atrule">redis</span><span class="token punctuation">:</span>    <span class="token key atrule">host</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.redis.host<span class="token punctuation">}</span>    <span class="token key atrule">port</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.redis.port<span class="token punctuation">}</span>    <span class="token key atrule">password</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.redis.password<span class="token punctuation">}</span>    <span class="token key atrule">database</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span>sky.redis.database<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>3). 编写配置类，创建RedisTemplate对象</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>config</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Bean</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Configuration</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>connection<span class="token punctuation">.</span></span><span class="token class-name">RedisConnectionFactory</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">RedisTemplate</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>serializer<span class="token punctuation">.</span></span><span class="token class-name">GenericJackson2JsonRedisSerializer</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>serializer<span class="token punctuation">.</span></span><span class="token class-name">StringRedisSerializer</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Configuration</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RedisConfiguration</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Bean</span>    <span class="token keyword">public</span> <span class="token class-name">RedisTemplate</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> <span class="token function">redisTemplate</span><span class="token punctuation">(</span><span class="token class-name">RedisConnectionFactory</span> redisConnectionFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"开始创建RedisTemplate模板对象..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">RedisTemplate</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> redisTemplate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RedisTemplate</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">setConnectionFactory</span><span class="token punctuation">(</span>redisConnectionFactory<span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token comment">// Key序列化器（String类型，避免Key乱码）</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">setKeySerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StringRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// Value序列化器（JSON格式，支持对象、集合等复杂类型，且可读性好）</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">setValueSerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">GenericJackson2JsonRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// Hash结构的Key序列化器</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">setHashKeySerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StringRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// Hash结构的Value序列化器</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">setHashValueSerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">GenericJackson2JsonRedisSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                redisTemplate<span class="token punctuation">.</span><span class="token function">afterPropertiesSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 初始化RedisTemplate</span>        <span class="token keyword">return</span> redisTemplate<span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>解释说明：</strong></p><p>当前配置类不是必须的，因为 <strong>Spring Boot 框架会自动装配 RedisTemplate 对象</strong>，但是<strong>默认的key序列化器为</strong></p><p><strong>JdkSerializationRedisSerializer</strong>，导致我们<strong>存到Redis中后的数据和原始数据有差别</strong>，故<strong>设置为</strong></p><p><strong>StringRedisSerializer序列化器。</strong></p><p><strong>注：</strong></p><ul><li><p><code>RedisTemplate</code>是<strong>泛型类</strong>（<code>RedisTemplate&lt;K, V&gt;</code>），若未指定泛型类型（如<code>&lt;String, Object&gt;</code>），编译器会因 “原始类型使用”“未检查调用” 报警告。这些警告虽不影响运行，但会降低代码规范性，且可能隐藏潜在类型转换问题。</p></li><li><p>**解决方法：**修改<code>RedisConfiguration</code>类中的<code>redisTemplate</code>方法，指定泛型为<code>&lt;String, Object&gt;</code>（Redis 的 Key 通常为 String，Value 为任意对象），并完善序列化器配置（避免 Redis 存储数据时出现乱码）。</p></li><li><p>泛型<code>&lt;String, Object&gt;</code>的指定，消除了 “原始类型使用”“未检查调用” 的警告。</p></li><li><p>完善的序列化器配置（<code>StringRedisSerializer</code>+<code>GenericJackson2JsonRedisSerializer</code>），确保 Redis 中 Key、Value 的存储格式清晰（避免乱码），且支持对象、集合等复杂类型的序列化 &#x2F; 反序列化。</p></li></ul><p><strong>4). 通过RedisTemplate对象操作Redis</strong></p><p>在test下新建测试类</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>test</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>jupiter<span class="token punctuation">.</span>api<span class="token punctuation">.</span></span><span class="token class-name">Test</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>test<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">SpringBootTest</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@SpringBootTest</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SpringDataRedisTest</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">RedisTemplate</span> redisTemplate<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Test</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testRedisTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>redisTemplate<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//string数据操作</span>        <span class="token class-name">ValueOperations</span> valueOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//hash类型的数据操作</span>        <span class="token class-name">HashOperations</span> hashOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//list类型的数据操作</span>        <span class="token class-name">ListOperations</span> listOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//set类型数据操作</span>        <span class="token class-name">SetOperations</span> setOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//zset类型数据操作</span>        <span class="token class-name">ZSetOperations</span> zSetOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForZSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>测试：</p><p><img src="/2025/10/14/sky-takeout-day05-note/image-20251014181653327.png" alt="image-20251014181653327"></p><p>说明RedisTemplate对象注入成功，并且通过该RedisTemplate对象获取操作5种数据类型相关对象。</p><h3 id="操作常见类型数据"><a href="#操作常见类型数据" class="headerlink" title="操作常见类型数据"></a>操作常见类型数据</h3><p><strong>1). 操作字符串类型数据</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 操作字符串类型的数据    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">// set get setex setnx</span>       redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">,</span><span class="token string">"小明"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">String</span> city <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">)</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>city<span class="token punctuation">)</span><span class="token punctuation">;</span>       redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"code"</span><span class="token punctuation">,</span><span class="token string">"1234"</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span><span class="token constant">MINUTES</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setIfAbsent</span><span class="token punctuation">(</span><span class="token string">"lock"</span><span class="token punctuation">,</span><span class="token string">"1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setIfAbsent</span><span class="token punctuation">(</span><span class="token string">"lock"</span><span class="token punctuation">,</span><span class="token string">"2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>2). 操作哈希类型数据</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 操作哈希类型的数据    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">//hset hget hdel hkeys hvals</span>       <span class="token class-name">HashOperations</span> hashOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForHash</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       hashOperations<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">,</span><span class="token string">"name"</span><span class="token punctuation">,</span><span class="token string">"tom"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       hashOperations<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">,</span><span class="token string">"age"</span><span class="token punctuation">,</span><span class="token string">"20"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">String</span> name <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">)</span> hashOperations<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">,</span> <span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Set</span> keys <span class="token operator">=</span> hashOperations<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>keys<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">List</span> values <span class="token operator">=</span> hashOperations<span class="token punctuation">.</span><span class="token function">values</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>values<span class="token punctuation">)</span><span class="token punctuation">;</span>       hashOperations<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">,</span><span class="token string">"age"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>3). 操作列表类型数据</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 操作列表类型的数据    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">//lpush lrange rpop llen</span>       <span class="token class-name">ListOperations</span> listOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       listOperations<span class="token punctuation">.</span><span class="token function">leftPushAll</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token string">"c"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       listOperations<span class="token punctuation">.</span><span class="token function">leftPush</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">,</span><span class="token string">"d"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">List</span> mylist <span class="token operator">=</span> listOperations<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mylist<span class="token punctuation">)</span><span class="token punctuation">;</span>       listOperations<span class="token punctuation">.</span><span class="token function">rightPop</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Long</span> size <span class="token operator">=</span> listOperations<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>4). 操作集合类型数据</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 操作集合类型的数据    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">//sadd smembers scard sinter sunion srem</span>       <span class="token class-name">SetOperations</span> setOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       setOperations<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token string">"c"</span><span class="token punctuation">,</span><span class="token string">"d"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       setOperations<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"set2"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token string">"x"</span><span class="token punctuation">,</span><span class="token string">"y"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Set</span> members <span class="token operator">=</span> setOperations<span class="token punctuation">.</span><span class="token function">members</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>members<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Long</span> size <span class="token operator">=</span> setOperations<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Set</span> intersect <span class="token operator">=</span> setOperations<span class="token punctuation">.</span><span class="token function">intersect</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">,</span> <span class="token string">"set2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>intersect<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Set</span> union <span class="token operator">=</span> setOperations<span class="token punctuation">.</span><span class="token function">union</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">,</span> <span class="token string">"set2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>union<span class="token punctuation">)</span><span class="token punctuation">;</span>       setOperations<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>5). 操作有序集合类型数据</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 操作有序集合类型的数据    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testZset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">//zadd zrange zincrby zrem</span>       <span class="token class-name">ZSetOperations</span> zSetOperations <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForZSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       zSetOperations<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       zSetOperations<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">,</span><span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       zSetOperations<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span><span class="token string">"c"</span><span class="token punctuation">,</span><span class="token number">9</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Set</span> zset1 <span class="token operator">=</span> zSetOperations<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>zset1<span class="token punctuation">)</span><span class="token punctuation">;</span>       zSetOperations<span class="token punctuation">.</span><span class="token function">incrementScore</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span><span class="token string">"c"</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       zSetOperations<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">"zset1"</span><span class="token punctuation">,</span><span class="token string">"a"</span><span class="token punctuation">,</span><span class="token string">"b"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>6). 通用命令操作</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**    * 通用命令操作    */</span>   <span class="token annotation punctuation">@Test</span>   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testCommon</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       <span class="token comment">//keys exists type del</span>       <span class="token class-name">Set</span> keys <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token string">"*"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>keys<span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Boolean</span> name <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">hasKey</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Boolean</span> set1 <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">hasKey</span><span class="token punctuation">(</span><span class="token string">"set1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Object</span> key <span class="token operator">:</span> keys<span class="token punctuation">)</span> <span class="token punctuation">{</span>           <span class="token class-name">DataType</span> type <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span><span class="token function">name</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token punctuation">}</span>       redisTemplate<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">"mylist"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="店铺营业状态设置"><a href="#店铺营业状态设置" class="headerlink" title="店铺营业状态设置"></a>店铺营业状态设置</h2><h3 id="需求分析与接口设计"><a href="#需求分析与接口设计" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><ul><li>进到苍穹外卖后台，显示餐厅的营业状态，营业状态分为<strong>营业中</strong>和<strong>打烊中</strong>，若当前餐厅处于营业状态，自动接收任何订单，客户可在小程序进行下单操作；若当前餐厅处于打烊状态，不接受任何订单，客户便无法在小程序进行下单操作。</li></ul><p><strong>产品原型：</strong><br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014181952632.png" alt="image-20251014181952632" style="zoom: 50%;"></p><p><strong>接口设计：</strong></p><ul><li>设置营业状态</li><li>管理端查询营业状态</li><li>用户端查询营业状态</li></ul><p>**注：**从技术层面分析，其实管理端和用户端查询营业状态时，可通过一个接口去实现即可。因为营业状态是一致的。但是，本项目约定：</p><ul><li><strong>管理端</strong>发出的请求，统一使用&#x2F;admin作为前缀。</li><li><strong>用户端</strong>发出的请求，统一使用&#x2F;user作为前缀。</li></ul><p>因为访问路径不一致，故分为两个接口实现。</p><p><strong>1). 设置营业状态</strong><br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014182121927.png" alt="image-20251014182121927" style="zoom:50%;"></p><p><strong>2). 管理端营业状态</strong><br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014182128446.png" alt="image-20251014182128446" style="zoom:50%;"></p><p><strong>3). 用户端营业状态</strong><br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014182134665.png" alt="image-20251014182134665" style="zoom:50%;"></p><p><strong>营业状态存储方式</strong></p><p>虽然，可以通过一张表来存储营业状态数据，但整个表中只有一个字段，所以意义不大。</p><p>营业状态数据存储方式：基于Redis的字符串来进行存储。**约定：**1表示营业 0表示打烊。<br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014182318284.png" alt="image-20251014182318284"></p><h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>admin&#x2F;ShopController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>admin</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">RedisTemplate</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token punctuation">(</span><span class="token string">"adminShopController"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"admin/shop"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"管理端-商铺相关接口"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ShopController</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token constant">KEY</span> <span class="token operator">=</span> <span class="token string">"SHOP_STATUS_"</span><span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">RedisTemplate</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> redisTemplate<span class="token punctuation">;</span>    <span class="token comment">/**     * 设置店铺营业状态     *     * @param status 店铺状态 0-打烊 1-营业     * @return 无     */</span>    <span class="token annotation punctuation">@PutMapping</span><span class="token punctuation">(</span><span class="token string">"/{status}"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"设置店铺营业状态"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">setStatus</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Integer</span> status<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"设置店铺营业状态：{}"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>status <span class="token operator">==</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"营业"</span> <span class="token operator">:</span> <span class="token string">"打烊"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 将店铺状态存入 Redis</span>        redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token constant">KEY</span><span class="token punctuation">,</span> status<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 获取店铺营业状态     *     * @return 店铺状态 0-打烊 1-营业     */</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"获取店铺营业状态"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/status"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Integer</span><span class="token punctuation">></span></span> <span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Integer</span> status <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Integer</span><span class="token punctuation">)</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token constant">KEY</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"获取店铺营业状态：{}"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>status <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> status <span class="token operator">==</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"营业"</span> <span class="token operator">:</span> <span class="token string">"打烊"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>status<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>user&#x2F;ShopController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>user</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>redis<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">RedisTemplate</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">GetMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RestController</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"user/shop"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"用户端-商铺相关接口"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ShopController</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> <span class="token constant">KEY</span> <span class="token operator">=</span> <span class="token string">"SHOP_STATUS_"</span><span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">RedisTemplate</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> redisTemplate<span class="token punctuation">;</span>    <span class="token comment">/**     * 获取店铺营业状态     *     * @return 店铺状态 0-打烊 1-营业     */</span>    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/status"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"获取店铺营业状态"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Integer</span><span class="token punctuation">></span></span> <span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Integer</span> status <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Integer</span><span class="token punctuation">)</span> redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token constant">KEY</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"获取店铺营业状态：{}"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>status <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> status <span class="token operator">==</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"营业"</span> <span class="token operator">:</span> <span class="token string">"打烊"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>status<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><p><strong>注意！：</strong> 测试时记得要把Redis服务开启，否则连接不到Redis，数据都存不了，更别谈测试了。</p><p>在接口文档测试中，管理端和用户端的接口放在一起，不方便区分。</p><p>接下来，我们要实现管理端和用户端接口进行区分。</p><p>在WebMvcConfiguration.java中，分别扫描”com.sky.controller.admin”和”com.sky.controller.user”这两个包。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Bean</span>   <span class="token keyword">public</span> <span class="token class-name">Docket</span> <span class="token function">docket1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"准备生成接口文档..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">ApiInfo</span> apiInfo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ApiInfoBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">title</span><span class="token punctuation">(</span><span class="token string">"苍穹外卖项目接口文档"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">version</span><span class="token punctuation">(</span><span class="token string">"2.0"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">description</span><span class="token punctuation">(</span><span class="token string">"苍穹外卖项目接口文档"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Docket</span> docket <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Docket</span><span class="token punctuation">(</span><span class="token class-name">DocumentationType</span><span class="token punctuation">.</span><span class="token constant">SWAGGER_2</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">groupName</span><span class="token punctuation">(</span><span class="token string">"管理端接口"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">apiInfo</span><span class="token punctuation">(</span>apiInfo<span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span>               <span class="token comment">//指定生成接口需要扫描的包</span>               <span class="token punctuation">.</span><span class="token function">apis</span><span class="token punctuation">(</span><span class="token class-name">RequestHandlerSelectors</span><span class="token punctuation">.</span><span class="token function">basePackage</span><span class="token punctuation">(</span><span class="token string">"com.sky.controller.admin"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">paths</span><span class="token punctuation">(</span><span class="token class-name">PathSelectors</span><span class="token punctuation">.</span><span class="token function">any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token keyword">return</span> docket<span class="token punctuation">;</span>   <span class="token punctuation">}</span>   <span class="token annotation punctuation">@Bean</span>   <span class="token keyword">public</span> <span class="token class-name">Docket</span> <span class="token function">docket2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>       log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"准备生成接口文档..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">ApiInfo</span> apiInfo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ApiInfoBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">title</span><span class="token punctuation">(</span><span class="token string">"苍穹外卖项目接口文档"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">version</span><span class="token punctuation">(</span><span class="token string">"2.0"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">description</span><span class="token punctuation">(</span><span class="token string">"苍穹外卖项目接口文档"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token class-name">Docket</span> docket <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Docket</span><span class="token punctuation">(</span><span class="token class-name">DocumentationType</span><span class="token punctuation">.</span><span class="token constant">SWAGGER_2</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">groupName</span><span class="token punctuation">(</span><span class="token string">"用户端接口"</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">apiInfo</span><span class="token punctuation">(</span>apiInfo<span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span>               <span class="token comment">//指定生成接口需要扫描的包</span>               <span class="token punctuation">.</span><span class="token function">apis</span><span class="token punctuation">(</span><span class="token class-name">RequestHandlerSelectors</span><span class="token punctuation">.</span><span class="token function">basePackage</span><span class="token punctuation">(</span><span class="token string">"com.sky.controller.user"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">paths</span><span class="token punctuation">(</span><span class="token class-name">PathSelectors</span><span class="token punctuation">.</span><span class="token function">any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>               <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>       <span class="token keyword">return</span> docket<span class="token punctuation">;</span>   <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>重启服务器，再次访问接口文档，可进行选择<strong>用户端接口</strong>或者<strong>管理端接口</strong><br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014182641746.png" alt="image-20251014182641746" style="zoom:50%;"></p><p>设置状态测试：</p><p><img src="/2025/10/14/sky-takeout-day05-note/image-20251014192349262.png" alt="image-20251014192349262"></p><p>获取状态测试：<br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014192442953.png" alt="image-20251014192442953"></p><p>日志查看<br><img src="/2025/10/14/sky-takeout-day05-note/image-20251014192518244.png" alt="image-20251014192518244"></p>]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/14/sky-takeout-day05-note/</id>
    <link href="https://junnhwan.github.io/2025/10/14/sky-takeout-day05-note/"/>
    <published>2025-10-14T11:32:28.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day05"><a href="#Sky-takeout-Day05" class="headerlink" title="Sky-takeout  Day05"></a>Sky-takeout  Day05</h1><hr>
<blockquote>
<p>【前言】</p>
<p>Day05 的 Task 是 Redis 的入门，了解 Redis 数据类型、常用命令，如何在Java中使用Redis，以及 Redis 在项目中的应用场景，通过项目案例使用 Redis。</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day05-note</title>
    <updated>2026-07-26T03:41:24.388Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day04"><a href="#Sky-takeout-Day04" class="headerlink" title="Sky-takeout  Day04"></a>Sky-takeout  Day04</h1><hr><blockquote><p>【前言】</p><p>Day04 的 Task 是套餐管理相关代码的开发 ，也是对多表业务的分析能力的锻炼</p></blockquote><span id="more"></span><h2 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h2><ul><li>新增套餐</li><li>套餐分页查询</li><li>删除套餐</li><li>修改套餐</li><li>起售停售套餐</li></ul><h2 id="1-新增套餐"><a href="#1-新增套餐" class="headerlink" title="1. 新增套餐"></a>1. 新增套餐</h2><h3 id="需求分析与接口设计"><a href="#需求分析与接口设计" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p>业务规则：</p><ul><li>套餐名称唯一</li><li>套餐必须属于某个分类</li><li>套餐必须包含菜品</li><li>名称、分类、价格、图片为必填项</li><li>添加菜品窗口需要根据分类类型来展示菜品</li><li>新增的套餐默认为停售状态</li></ul><p>接口设计（共涉及到 4 个接口）：</p><ul><li>根据类型查询分类（已完成）</li><li>根据分类 id 查询菜品</li><li>图片上传（已完成）</li><li>新增套餐</li></ul><p><strong>产品原型：</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013090412130.png" alt="image-20251013090412130" style="zoom: 50%;"></p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013090422861.png" alt="image-20251013090422861" style="zoom:50%;"><p>可以看到，在新增套餐中还涉及到了菜品的一个接口，就是 <strong>根据分类 id 查询菜品并列出菜品</strong>，所以需要在 DishController 再加一个接口 来根据分类 id 展示菜品（list）</p><p><strong>接口设计：</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013090823018.png" alt="image-20251013090823018" style="zoom:50%;"></p><hr><img src="/2025/10/13/sky-takeout-day04-note/image-20251013090830998.png" alt="image-20251013090830998" style="zoom:50%;"><p><strong>表设计：</strong></p><p>setmeal 表为套餐表，用于存储套餐的信息。具体表结构如下：</p><table><thead><tr><th>字段名</th><th>数据类型</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>套餐名称</td><td>唯一</td></tr><tr><td>category_id</td><td>bigint</td><td>分类 id</td><td>逻辑外键</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>套餐价格</td><td></td></tr><tr><td>image</td><td>varchar(255)</td><td>图片路径</td><td></td></tr><tr><td>description</td><td>varchar(255)</td><td>套餐描述</td><td></td></tr><tr><td>status</td><td>int</td><td>售卖状态</td><td>1 起售 0 停售</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td><td></td></tr><tr><td>update_time</td><td>datetime</td><td>最后修改时间</td><td></td></tr><tr><td>create_user</td><td>bigint</td><td>创建人 id</td><td></td></tr><tr><td>update_user</td><td>bigint</td><td>最后修改人 id</td><td></td></tr></tbody></table><p>setmeal_dish 表为套餐菜品关系表，用于存储套餐和菜品的关联关系。具体表结构如下：</p><table><thead><tr><th>字段名</th><th>数据类型</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>setmeal_id</td><td>bigint</td><td>套餐 id</td><td>逻辑外键</td></tr><tr><td>dish_id</td><td>bigint</td><td>菜品 id</td><td>逻辑外键</td></tr><tr><td>name</td><td>varchar(32)</td><td>菜品名称</td><td>冗余字段</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>菜品单价</td><td>冗余字段</td></tr><tr><td>copies</td><td>int</td><td>菜品份数</td><td></td></tr></tbody></table><h3 id="流程分析"><a href="#流程分析" class="headerlink" title="流程分析"></a>流程分析</h3><h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a>代码编写</h3><h4 id="1）根据分类-id-查询并列出菜品"><a href="#1）根据分类-id-查询并列出菜品" class="headerlink" title="1）根据分类 id 查询并列出菜品"></a>1）根据分类 id 查询并列出菜品</h4><p><strong>DishController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据分类条件查询列出菜品 * @param categoryId 分类id * @return 菜品列表 */</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/list"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"根据分类条件查询并列出菜品"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Long</span> categoryId<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"根据分类条件查询并列出菜品: {}"</span><span class="token punctuation">,</span> categoryId<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span></span> dishList <span class="token operator">=</span> dishService<span class="token punctuation">.</span><span class="token function">list</span><span class="token punctuation">(</span>categoryId<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>dishList<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishServiceImpl.java</strong></p><p>这里注意 status <strong>状态是 ENABLE</strong>，一开始写成 DISABLE 了，没有菜品列出，后面发现是状态设置错了。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据分类条件查询列出菜品 * @param categoryId 分类id * @return 菜品列表 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Long</span> categoryId<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">Dish</span> dish <span class="token operator">=</span> <span class="token class-name">Dish</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">ENABLE</span><span class="token punctuation">)</span> <span class="token comment">// 注意这里是只查询起售状态的菜品</span>            <span class="token punctuation">.</span><span class="token function">categoryId</span><span class="token punctuation">(</span>categoryId<span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> dishMapper<span class="token punctuation">.</span><span class="token function">list</span><span class="token punctuation">(</span>dish<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 动态条件查询菜品 * @param dish 条件 * @return 菜品列表 */</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Dish</span> dish<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span> <span class="token attr-name">resultType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.entity.Dish<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    select * from dish    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>where</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name != null and name != ''<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            and name like concat('%', #{name}, '%')        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>categoryId != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            and category_id = #{categoryId}        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            and status = #{status}        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>where</span><span class="token punctuation">></span></span>    order by create_time desc<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2）新增套餐"><a href="#2）新增套餐" class="headerlink" title="2）新增套餐"></a>2）新增套餐</h4><p>SetmealController.java</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>admin</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">SetmealDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">SetmealService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">PostMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestBody</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RestController</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"套餐管理"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"admin/setmeal"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SetmealController</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">SetmealService</span> setmealService<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增套餐     * @param setmealDTO 套餐信息     * @return 结果     */</span>    <span class="token annotation punctuation">@PostMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"新增套餐"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">SetmealDTO</span> setmealDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 这里要加 @RequestBody 将前端传递的json数据转换为java对象</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"新增套餐: {}"</span><span class="token punctuation">,</span> setmealDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        setmealService<span class="token punctuation">.</span><span class="token function">saveWithDish</span><span class="token punctuation">(</span>setmealDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>SetmealServiceImpl.java</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span>impl</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">SetmealDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Setmeal</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">SetmealDish</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">SetmealDishMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">SetmealMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">SetmealService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span></span><span class="token class-name">BeanUtils</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Service</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Service</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SetmealServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">SetmealService</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">SetmealMapper</span> setmealMapper<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">SetmealDishMapper</span> setmealDishMapper<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增套餐     * @param setmealDTO 套餐信息     */</span>    <span class="token annotation punctuation">@Override</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">saveWithDish</span><span class="token punctuation">(</span><span class="token class-name">SetmealDTO</span> setmealDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Setmeal</span> setmeal <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Setmeal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>setmealDTO<span class="token punctuation">,</span> setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 1. 向setmeal表插入套餐</span>        setmealMapper<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 2. 向setmeal_dish表插入套餐和菜品的关联关系</span>        <span class="token comment">// 先获取套餐id</span>        <span class="token class-name">Long</span> setmealId <span class="token operator">=</span> setmeal<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 再获取套餐和菜品的关联关系</span>        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealDish</span><span class="token punctuation">></span></span> setmealDishes <span class="token operator">=</span> setmealDTO<span class="token punctuation">.</span><span class="token function">getSetmealDishes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 设置套餐id</span>        setmealDishes<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>setmealDish <span class="token operator">-></span> setmealDish<span class="token punctuation">.</span><span class="token function">setSetmealId</span><span class="token punctuation">(</span>setmealId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 批量插入套餐和菜品的关联关系</span>        setmealDishMapper<span class="token punctuation">.</span><span class="token function">insertBatch</span><span class="token punctuation">(</span>setmealDishes<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>SetmealMapper.java</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 插入套餐, 并且将主键回填到实体类对象中 (useGeneratedKeys = true, keyProperty = "id") * @param setmeal 套餐 */</span><span class="token annotation punctuation">@AutoFill</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">INSERT</span><span class="token punctuation">)</span><span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token class-name">Setmeal</span> setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>SetmealMapper.xml</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" ?></span><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">mapper</span> <span class="token name">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mapper</span> <span class="token attr-name">namespace</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.mapper.SetmealMapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>insert</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>insert<span class="token punctuation">"</span></span> <span class="token attr-name">useGeneratedKeys</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">keyProperty</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        insert into setmeal (category_id, name, price, description, image, create_time, update_time, create_user, update_user)        values        (#{categoryId}, #{name}, #{price}, #{description}, #{image}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>insert</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mapper</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>SetmealDishMapper.java</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 批量插入套餐和菜品的关联关系 * @param setmealDishes 套餐和菜品的关联关系 */</span><span class="token keyword">void</span> <span class="token function">insertBatch</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealDish</span><span class="token punctuation">></span></span> setmealDishes<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>SetmealDishMapper.xml</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token operator">&lt;</span>insert id<span class="token operator">=</span><span class="token string">"insertBatch"</span><span class="token operator">></span>    insert into setmeal_dish <span class="token punctuation">(</span>setmeal_id<span class="token punctuation">,</span> dish_id<span class="token punctuation">,</span> name<span class="token punctuation">,</span> price<span class="token punctuation">,</span> copies<span class="token punctuation">)</span>    values    <span class="token operator">&lt;</span>foreach collection<span class="token operator">=</span><span class="token string">"setmealDishes"</span> item<span class="token operator">=</span><span class="token string">"sd"</span> separator<span class="token operator">=</span><span class="token string">","</span><span class="token operator">></span>        <span class="token punctuation">(</span>#<span class="token punctuation">{</span>sd<span class="token punctuation">.</span>setmealId<span class="token punctuation">}</span><span class="token punctuation">,</span> #<span class="token punctuation">{</span>sd<span class="token punctuation">.</span>dishId<span class="token punctuation">}</span><span class="token punctuation">,</span> #<span class="token punctuation">{</span>sd<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token punctuation">,</span> #<span class="token punctuation">{</span>sd<span class="token punctuation">.</span>price<span class="token punctuation">}</span><span class="token punctuation">,</span> #<span class="token punctuation">{</span>sd<span class="token punctuation">.</span>copies<span class="token punctuation">}</span><span class="token punctuation">)</span>    <span class="token operator">&lt;</span><span class="token operator">/</span>foreach<span class="token operator">></span><span class="token operator">&lt;</span><span class="token operator">/</span>insert<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013112100927.png" alt="image-20251013112100927"></p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013112107482.png" alt="image-20251013112107482"></p><p>成功在表中插入数据<br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013112114859.png" alt="image-20251013112114859"></p><ul><li><strong>注意</strong> 这里有误！！后来在写删除套餐的时候才发现，插入时 status 不应该是 1，之前编写新增套餐代码的时候在 mapper 的 xml 映射文件遗漏了 status 字段，而且在 service 层忘记设置了初始状态为停售 DISABLE（也就是 0）</li></ul><h2 id="2-套餐分页查询"><a href="#2-套餐分页查询" class="headerlink" title="2. 套餐分页查询"></a>2. 套餐分页查询</h2><h3 id="需求分析与接口设计-1"><a href="#需求分析与接口设计-1" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>业务规则：</strong></p><ul><li>根据页码进行分页展示</li><li>每页展示 10 条数据</li><li>可以根据需要，按照套餐名称、分类、售卖状态进行查询</li></ul><p><strong>产品原型</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013112348704.png" alt="image-20251013112348704"></p><p>注意因为视图需要套餐对应的分类名称，所以分页时 <strong>Page 的泛型是 VO 对象，即 SetmealVO。</strong></p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013113648754.png" alt="image-20251013113648754" style="zoom:50%;"><p><strong>接口设计：</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013112435652.png" alt="image-20251013112435652"></p><h3 id="流程分析-1"><a href="#流程分析-1" class="headerlink" title="流程分析"></a>流程分析</h3><h3 id="代码编写-1"><a href="#代码编写-1" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>SetmealController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 分页查询套餐 * @param setmealPageQueryDTO 分页查询参数 * @return 套餐分页结果 */</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/page"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"分页查询套餐"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">PageResult</span><span class="token punctuation">></span></span> <span class="token function">page</span><span class="token punctuation">(</span><span class="token class-name">SetmealPageQueryDTO</span> setmealPageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"分页查询套餐: {}"</span><span class="token punctuation">,</span> setmealPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">PageResult</span> pageResult <span class="token operator">=</span> setmealService<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>setmealPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>pageResult<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 分页查询套餐 * @param setmealPageQueryDTO 分页查询参数 * @return 套餐分页结果 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">PageResult</span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">SetmealPageQueryDTO</span> setmealPageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">PageHelper</span><span class="token punctuation">.</span><span class="token function">startPage</span><span class="token punctuation">(</span>setmealPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> setmealPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPageSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealVO</span><span class="token punctuation">></span></span> page <span class="token operator">=</span> setmealMapper<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>setmealPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PageResult</span><span class="token punctuation">(</span>page<span class="token punctuation">.</span><span class="token function">getTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span><span class="token function">getResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 分页查询套餐 * @param setmealPageQueryDTO 分页查询参数 * @return 套餐分页结果 */</span><span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealVO</span><span class="token punctuation">></span></span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">SetmealPageQueryDTO</span> setmealPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pageQuery<span class="token punctuation">"</span></span> <span class="token attr-name">resultType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.vo.SetmealVO<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    select s.*, c.name as categoryName    from setmeal s left outer join category c    on s.category_id = c.id    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>where</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            s.name like concat('%', #{name}, '%')        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            and s.status = #{status}        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>categoryId != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            and s.category_id = #{categoryId}        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>where</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-1"><a href="#功能测试-1" class="headerlink" title="功能测试"></a>功能测试</h3><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013114913208.png" alt="image-20251013114913208"></p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013114722042.png" alt="image-20251013114722042"></p><h2 id="3-删除套餐"><a href="#3-删除套餐" class="headerlink" title="3. 删除套餐"></a>3. 删除套餐</h2><h3 id="需求分析与接口设计-2"><a href="#需求分析与接口设计-2" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>业务规则：</strong></p><ul><li>可以一次删除一个套餐，也可以批量删除套餐</li><li>起售中的套餐不能删除</li></ul><p><strong>接口设计：</strong></p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013115744668.png" alt="image-20251013115744668" style="zoom:50%;"><h3 id="流程分析-2"><a href="#流程分析-2" class="headerlink" title="流程分析"></a>流程分析</h3><h3 id="代码编写-2"><a href="#代码编写-2" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>SetmealController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 删除套餐 * @param ids 套餐id集合 * @return 结果 */</span><span class="token annotation punctuation">@DeleteMapping</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"删除套餐"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">delete</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">"ids"</span><span class="token punctuation">)</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"删除套餐: {}"</span><span class="token punctuation">,</span> ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    setmealService<span class="token punctuation">.</span><span class="token function">deleteWithDish</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 删除套餐, 同时删除套餐和菜品的关联数据 * @param ids 套餐id集合 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteWithDish</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 起售中的套餐不能删除</span>    <span class="token comment">// 1. 查询套餐状态, 确认是否可以删除</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Long</span> id <span class="token operator">:</span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Setmeal</span> setmeal <span class="token operator">=</span> setmealMapper<span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>setmeal <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>setmeal<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">ENABLE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 套餐起售中</span>            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DeletionNotAllowedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">SETMEAL_ON_SALE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    <span class="token comment">// 2. 删除套餐表中的数据</span>    <span class="token comment">// delete from setmeal where id in (1,2,3)</span>    setmealMapper<span class="token punctuation">.</span><span class="token function">deleteByIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 3. 删除套餐和菜品的关联数据</span>    <span class="token comment">// delete from setmeal_dish where setmeal_id in (1,2,3)</span>    setmealDishMapper<span class="token punctuation">.</span><span class="token function">deleteBySetmealIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询套餐  (用于删除套餐时, 查询套餐状态) * @param id 套餐id */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from setmeal where id = #{id}"</span><span class="token punctuation">)</span><span class="token class-name">Setmeal</span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/** * 根据id删除套餐 * @param ids 套餐id集合 */</span><span class="token keyword">void</span> <span class="token function">deleteByIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>delete</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>deleteByIds<span class="token punctuation">"</span></span> <span class="token attr-name">parameterType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    delete from setmeal where id in    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ids<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        #{id}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>delete</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealDishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据套餐id删除对应的套餐和菜品的关联关系 * @param setmealIds 套餐id集合 */</span><span class="token keyword">void</span> <span class="token function">deleteBySetmealIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> setmealIds<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealDishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>delete</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>deleteBySetmealIds<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    delete from setmeal_dish where setmeal_id in    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>setmealIds<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>setmealId<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        #{setmealId}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>delete</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-2"><a href="#功能测试-2" class="headerlink" title="功能测试"></a>功能测试</h3><p>起售中无法删除</p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013135201884.png" alt="image-20251013135201884"></p><p>删除成功</p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013135207956.png" alt="image-20251013135207956"></p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013135259589.png" alt="image-20251013135259589"></p><h2 id="4-修改套餐"><a href="#4-修改套餐" class="headerlink" title="4. 修改套餐"></a>4. 修改套餐</h2><h3 id="需求分析与接口设计-3"><a href="#需求分析与接口设计-3" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>产品原型：</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013140428967.png" alt="image-20251013140428967" style="zoom:50%;"></p><p><strong>接口设计（共涉及到5个接口）：</strong></p><ul><li>根据id查询套餐</li><li>根据类型查询分类（已完成）</li><li>根据分类id查询菜品（已完成）</li><li>图片上传（已完成）</li><li>修改套餐</li></ul><h3 id="流程分析-3"><a href="#流程分析-3" class="headerlink" title="流程分析"></a>流程分析</h3><h3 id="代码编写-3"><a href="#代码编写-3" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>SetmealController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询套餐信息, 包括套餐和菜品的关联信息 * @param id 套餐id * @return 套餐信息 */</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"根据id查询套餐信息(包括套餐和菜品的关联信息)"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealVO</span><span class="token punctuation">></span></span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 这里要加 @PathVariable 将路径参数中的id参数绑定到方法的id参数上</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"根据id查询套餐信息: {}"</span><span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">SetmealVO</span> setmealVO <span class="token operator">=</span> setmealService<span class="token punctuation">.</span><span class="token function">getByIdWithDishes</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>setmealVO<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/** * 修改套餐(包括套餐和菜品的关联信息) * @param setmealDTO 套餐信息 * @return 结果 */</span><span class="token annotation punctuation">@PutMapping</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"修改套餐"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">SetmealDTO</span> setmealDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 这里要加 @RequestBody 将前端传递的json数据转换为java对象</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"修改套餐: {}"</span><span class="token punctuation">,</span> setmealDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    setmealService<span class="token punctuation">.</span><span class="token function">updateWithDish</span><span class="token punctuation">(</span>setmealDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询套餐信息, 包括套餐和菜品的关联信息 * @param id 套餐id * @return 套餐信息 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">SetmealVO</span> <span class="token function">getByIdWithDishes</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 1. 查询套餐基本信息</span>    <span class="token class-name">Setmeal</span> setmeal <span class="token operator">=</span> setmealMapper<span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 2. 查询套餐和菜品的关联信息</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealDish</span><span class="token punctuation">></span></span> setmealDishes <span class="token operator">=</span> setmealDishMapper<span class="token punctuation">.</span><span class="token function">getByIdWithDishes</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 3. 将套餐基本信息与套餐菜品信息封装到VO对象中并返回</span>    <span class="token class-name">SetmealVO</span> setmealVO <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SetmealVO</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>setmeal<span class="token punctuation">,</span> setmealVO<span class="token punctuation">)</span><span class="token punctuation">;</span>    setmealVO<span class="token punctuation">.</span><span class="token function">setSetmealDishes</span><span class="token punctuation">(</span>setmealDishes<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> setmealVO<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/** * 修改套餐(包括套餐和菜品的关联信息) * @param setmealDTO 套餐信息 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">updateWithDish</span><span class="token punctuation">(</span><span class="token class-name">SetmealDTO</span> setmealDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">Setmeal</span> setmeal <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Setmeal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>setmealDTO<span class="token punctuation">,</span> setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 1. 首先，更新setmeal表中的基本信息</span>    setmealMapper<span class="token punctuation">.</span><span class="token function">updateById</span><span class="token punctuation">(</span>setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 2. 然后，更新setmeal_dish表中的关联信息</span>    <span class="token class-name">Long</span> setmealId <span class="token operator">=</span> setmeal<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 2.1 先删除原有的关联信息</span>    setmealDishMapper<span class="token punctuation">.</span><span class="token function">deleteBySetmealIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>setmealId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 2.2 再重新插入新的关联信息</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealDish</span><span class="token punctuation">></span></span> setmealDishes <span class="token operator">=</span> setmealDTO<span class="token punctuation">.</span><span class="token function">getSetmealDishes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 设置套餐id</span>    setmealDishes<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>setmealDish <span class="token operator">-></span> setmealDish<span class="token punctuation">.</span><span class="token function">setSetmealId</span><span class="token punctuation">(</span>setmealId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 批量插入套餐和菜品的关联关系</span>    setmealDishMapper<span class="token punctuation">.</span><span class="token function">insertBatch</span><span class="token punctuation">(</span>setmealDishes<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id动态修改套餐信息 * @param setmeal 套餐信息 */</span><span class="token annotation punctuation">@AutoFill</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">UPDATE</span><span class="token punctuation">)</span><span class="token keyword">void</span> <span class="token function">updateById</span><span class="token punctuation">(</span><span class="token class-name">Setmeal</span> setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>update</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>updateById<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    update setmeal    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>set</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>categoryId != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>category_id = #{categoryId},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>name = #{name},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>price != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>price = #{price},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>status = #{status},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>description = #{description},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>image = #{image},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        update_time = #{updateTime},        update_user = #{updateUser}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>set</span><span class="token punctuation">></span></span>    where id = #{id}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>update</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealDishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据套餐id查询对应的套餐和菜品的关联关系 * @param setmealId 套餐id * @return 套餐和菜品的关联关系 */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from setmeal_dish where setmeal_id = #{setmealId}"</span><span class="token punctuation">)</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SetmealDish</span><span class="token punctuation">></span></span> <span class="token function">getByIdWithDishes</span><span class="token punctuation">(</span><span class="token class-name">Long</span> setmealId<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-3"><a href="#功能测试-3" class="headerlink" title="功能测试"></a>功能测试</h3><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013152423948.png" alt="image-20251013152423948"></p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013152426440.png" alt="image-20251013152426440"></p><h2 id="5-起售停售套餐"><a href="#5-起售停售套餐" class="headerlink" title="5. 起售停售套餐"></a>5. 起售停售套餐</h2><h3 id="需求分析与接口设计-4"><a href="#需求分析与接口设计-4" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p>业务规则：</p><ul><li>可以对状态为起售的套餐进行停售操作，可以对状态为停售的套餐进行起售操作</li><li>起售的套餐可以展示在用户端，停售的套餐不能展示在用户端</li><li>起售套餐时，如果套餐内包含停售的菜品，则不能起售</li></ul><p><strong>接口设计：</strong><br><img src="/2025/10/13/sky-takeout-day04-note/image-20251013162309637.png" alt="image-20251013162309637" style="zoom:50%;"></p><h3 id="流程分析-4"><a href="#流程分析-4" class="headerlink" title="流程分析"></a>流程分析</h3><h3 id="代码编写-4"><a href="#代码编写-4" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>SetmealController</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 起售/停售套餐 * @param status 状态 * @param id 套餐id集合 * @return 结果 */</span><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/status/{status}"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"起售/停售套餐"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"起售/停售套餐: {}, {}"</span><span class="token punctuation">,</span> status<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    setmealService<span class="token punctuation">.</span><span class="token function">startOrStop</span><span class="token punctuation">(</span>status<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealServiceImpl</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 起售/停售套餐 * @param status 套餐状态 * @param id 套餐id */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 如果要设置为起售，需要检查套餐内的菜品是否都为起售状态</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>status<span class="token punctuation">,</span> <span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">ENABLE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 要起售</span>        <span class="token comment">// select a.* from dish a</span>        <span class="token comment">// left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?;</span>        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span></span> dishList <span class="token operator">=</span> dishMapper<span class="token punctuation">.</span><span class="token function">getBySetmealId</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 查询套餐内的菜品</span>        dishList<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>dish <span class="token operator">-></span> <span class="token punctuation">{</span>            <span class="token keyword">if</span> <span class="token punctuation">(</span>dish<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span><span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">DISABLE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">SetmealEnableFailedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">SETMEAL_ENABLE_FAILED</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 只要有一个菜品是停售状态, 就不能起售</span>    <span class="token punctuation">}</span>    <span class="token comment">// 可以起售，更新套餐状态</span>    <span class="token class-name">Setmeal</span> setmeal <span class="token operator">=</span> <span class="token class-name">Setmeal</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span>status<span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    setmealMapper<span class="token punctuation">.</span><span class="token function">updateById</span><span class="token punctuation">(</span>setmeal<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据套餐id查询对应的菜品 * @param id 套餐id * @return 菜品列表 */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select d.* from dish d "</span> <span class="token operator">+</span>        <span class="token string">"left join setmeal_dish sd on d.id = sd.dish_id "</span> <span class="token operator">+</span>        <span class="token string">"where sd.setmeal_id = #{id} "</span><span class="token punctuation">)</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Dish</span><span class="token punctuation">></span></span> <span class="token function">getBySetmealId</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-4"><a href="#功能测试-4" class="headerlink" title="功能测试"></a>功能测试</h3><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013170117923.png" alt="image-20251013170117923"></p><p><img src="/2025/10/13/sky-takeout-day04-note/image-20251013170122129.png" alt="image-20251013170122129"></p>]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/13/sky-takeout-day04-note/</id>
    <link href="https://junnhwan.github.io/2025/10/13/sky-takeout-day04-note/"/>
    <published>2025-10-13T10:09:05.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day04"><a href="#Sky-takeout-Day04" class="headerlink" title="Sky-takeout  Day04"></a>Sky-takeout  Day04</h1><hr>
<blockquote>
<p>【前言】</p>
<p>Day04 的 Task 是套餐管理相关代码的开发 ，也是对多表业务的分析能力的锻炼</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day04-note</title>
    <updated>2026-07-26T03:41:24.387Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day03"><a href="#Sky-takeout-Day03" class="headerlink" title="Sky-takeout  Day03"></a>Sky-takeout  Day03</h1><hr><blockquote><p>【前言】<br>Day03 的 Task 是 <strong>公共字段自动填充</strong> 与 <strong>菜品管理相关功能</strong> 的代码开发，主要的菜品管理部分也是基础 CRUD 的练习，另外就是公共字段自动填充这种偏向技术一点的开发技巧的学习。</p><p>公共字段自动填充主要涉及到 枚举、注解、Spring的 AOP 、反射 的知识，用<strong>自定义注解</strong>实现了当执行 <em><strong>insert新增* 与 *update更新</strong></em>  时在Mapper层对创建人、创建时间和修改人、修改时间的字段的自动填充，避免了在Service设置属性的冗余重复代码。</p><p>剩余的菜品管理就是 CRUD 练习了， 只是相比前面的员工管理模块，菜品管理会涉及到菜品Dish与分类Category、套餐Setmeal的<strong>多表的联系</strong>，分析起来会更比员工管理的单表更复杂一点，要考虑的比较多，但是这部分练习能锻炼到我们对<strong>多表的业务功能的分析能力</strong>。</p></blockquote><span id="more"></span><h2 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h2><ul><li>公共字段自动填充</li><li>新增菜品</li><li>菜品分页查询</li><li>删除菜品</li><li>修改菜品</li></ul><h2 id="1-公共字段自动填充"><a href="#1-公共字段自动填充" class="headerlink" title="1. 公共字段自动填充"></a>1. 公共字段自动填充</h2><h3 id="公共字段问题引入"><a href="#公共字段问题引入" class="headerlink" title="公共字段问题引入"></a>公共字段问题引入</h3><p>​前面在<strong>新增员工</strong>或者<strong>新增菜品分类</strong>时需要设置创建时间、创建人、修改时间、修改人等字段，在<strong>编辑员工</strong>或者<strong>编辑菜品分类</strong>时需要设置修改时间、修改人等字段。这些字段属于<strong>公共字段</strong>，很多表中都会有这些字段，如下：</p><table><thead><tr><th><strong>序号</strong></th><th><strong>字段名</strong></th><th><strong>含义</strong></th><th><strong>数据类型</strong></th></tr></thead><tbody><tr><td>1</td><td>create_time</td><td>创建时间</td><td>datetime</td></tr><tr><td>2</td><td>create_user</td><td>创建人id</td><td>bigint</td></tr><tr><td>3</td><td>update_time</td><td>修改时间</td><td>datetime</td></tr><tr><td>4</td><td>update_user</td><td>修改人id</td><td>bigint</td></tr></tbody></table><p>对于这些字段，我们的赋值方式为： </p><p>1). 在新增数据时, 将createTime、updateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。</p><p>2). 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。</p><p>代码层面，复制方式如下，通过set属性来设置这些公共字段的值</p><p>Insert时<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012092311740.png" alt="image-20251012092311740" style="zoom: 33%;"></p><p>Update时<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012092427001.png" alt="image-20251012092427001" style="zoom:33%;"></p><p>如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐，那能不能对于这些公共字段在某个地方统一处理，来简化开发呢？</p><ul><li>答案是可以的，我们使用<strong>AOP切面编程，实现功能增强</strong>，来完成公共字段自动填充功能。</li></ul><p>这样做的好处：</p><ul><li>减少重复代码，提升开发效率；</li><li>保证字段赋值的一致性（如时间格式、操作人 ID 的来源统一）；</li><li>降低人为疏忽导致的字段遗漏风险。</li></ul><h3 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h3><p><strong>实现步骤：</strong></p><p>1). 自定义注解 AutoFill，用于标识需要进行公共字段自动填充的方法</p><p>2). 自定义切面类 AutoFillAspect，统一拦截加入了 AutoFill 注解的方法，通过反射为公共字段赋值</p><p>3). 在 Mapper 的方法上加入 AutoFill 注解</p><p>实现上述步骤，需掌握以下知识</p><p>**技术点：**枚举、注解、AOP、反射</p><h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a>代码编写</h3><h4 id="1）编写注解，自定义注解AutoFill"><a href="#1）编写注解，自定义注解AutoFill" class="headerlink" title="1）编写注解，自定义注解AutoFill"></a>1）编写注解，自定义注解AutoFill</h4><p>在sky-server模块，创建com.sky.annotation包。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>annotation</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>enumeration<span class="token punctuation">.</span></span><span class="token class-name">OperationType</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">ElementType</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Retention</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RetentionPolicy</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Target</span></span><span class="token punctuation">;</span><span class="token comment">/** * 自定义公共字段自动填充注解，用于标记需要自动填充公共字段的方法 */</span><span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span><span class="token class-name">ElementType</span><span class="token punctuation">.</span><span class="token constant">METHOD</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span><span class="token class-name">RetentionPolicy</span><span class="token punctuation">.</span><span class="token constant">RUNTIME</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token annotation punctuation">@interface</span> <span class="token class-name">AutoFill</span> <span class="token punctuation">{</span>    <span class="token comment">// 数据库操作类型，如插入或更新 INSERT, UPDATE</span>    <span class="token class-name">OperationType</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在初始项目的枚举包中已定义了操作类型枚举<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012095830097.png" alt="image-20251012095830097" style="zoom:33%;"></p><h4 id="2）编写切面类-自定义AutoFillAspect切面"><a href="#2）编写切面类-自定义AutoFillAspect切面" class="headerlink" title="2）编写切面类, 自定义AutoFillAspect切面"></a>2）编写切面类, 自定义AutoFillAspect切面</h4><p>在sky-server模块，创建com.sky.aspect包。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>aspect</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">AutoFill</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">AutoFillConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">BaseContext</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>enumeration<span class="token punctuation">.</span></span><span class="token class-name">OperationType</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span></span><span class="token class-name">JoinPoint</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Aspect</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Before</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Pointcut</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>aspectj<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span></span><span class="token class-name">MethodSignature</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Component</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span></span><span class="token class-name">InvocationTargetException</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>reflect<span class="token punctuation">.</span></span><span class="token class-name">Method</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>time<span class="token punctuation">.</span></span><span class="token class-name">LocalDateTime</span></span><span class="token punctuation">;</span><span class="token comment">/** * 自定义切面类，处理公共字段自动填充逻辑 */</span><span class="token annotation punctuation">@Aspect</span><span class="token annotation punctuation">@Component</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AutoFillAspect</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 切入点，匹配所有标记了 @AutoFill 注解的方法     */</span>    <span class="token comment">// 切入点表达式，匹配所有在 com.sky.mapper 包下的方法，并且这些方法上有 @AutoFill 注解</span>    <span class="token comment">// execution(* com.sky.mapper.*.*(..)) 匹配 com.sky.mapper 包及其子包下的所有方法</span>    <span class="token comment">// &amp;&amp; @annotation(com.sky.annotation.AutoFill) 匹配那些被 @AutoFill 注解标记的方法</span>    <span class="token comment">// 这样，任何调用 com.sky.mapper 包下的方法且这些方法被 @AutoFill 注解标记时，</span>    <span class="token comment">// 都会触发这个切入点，从而可以在相应的通知中实现自动填充公共字段的逻辑</span>    <span class="token annotation punctuation">@Pointcut</span><span class="token punctuation">(</span><span class="token string">"execution(* com.sky.mapper.*.*(..)) &amp;&amp; @annotation(com.sky.annotation.AutoFill)"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">autoFillPointCut</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>    <span class="token comment">/**     * 前置通知，在切入点方法执行前，执行通知--进行公共字段自动填充逻辑 (反射 + AOP)     * @param joinPoint 连接点信息     */</span>    <span class="token annotation punctuation">@Before</span><span class="token punctuation">(</span><span class="token string">"autoFillPointCut()"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">autoFill</span><span class="token punctuation">(</span><span class="token class-name">JoinPoint</span> joinPoint<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"自动填充公共字段..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 1.获取当前拦截方法的数据库操作类型（插入或更新）</span>        <span class="token comment">// 注意这里自动导包的话要导入 org.aspectj.lang.reflect.MethodSignature，</span>        <span class="token comment">// 不是 java.lang.reflect.MethodSignature</span>        <span class="token comment">// 否则调用不了getMethod()方法</span>        <span class="token class-name">MethodSignature</span> signature <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">MethodSignature</span><span class="token punctuation">)</span> joinPoint<span class="token punctuation">.</span><span class="token function">getSignature</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 获取方法签名</span>        <span class="token class-name">AutoFill</span> annotation <span class="token operator">=</span> signature<span class="token punctuation">.</span><span class="token function">getMethod</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getAnnotation</span><span class="token punctuation">(</span><span class="token class-name">AutoFill</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 获取方法上的注解</span>        <span class="token class-name">OperationType</span> operationType <span class="token operator">=</span> annotation<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取注解的值（操作类型）</span>        <span class="token comment">// 2.获取当前拦截方法的参数对象（实体类对象）</span>        <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args <span class="token operator">=</span> joinPoint<span class="token punctuation">.</span><span class="token function">getArgs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取方法参数</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>args <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> args<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token comment">// 如果没有参数，直接返回</span>        <span class="token punctuation">}</span>        <span class="token class-name">Object</span> entity <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 项目约定第一个参数是实体类对象</span>        <span class="token comment">// 3.根据操作类型，准备需要填充的公共字段及其值（如创建时间、更新时间、创建人、更新人等）</span>        <span class="token class-name">LocalDateTime</span> now <span class="token operator">=</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">Long</span> currentId <span class="token operator">=</span> <span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 4.使用反射机制，给实体类对象的公共字段赋值（如创建时间、更新时间、创建人、更新人等）</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>operationType <span class="token operator">==</span> <span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">INSERT</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">// 插入操作，准备插入时需要填充的字段及其值 (四个字段)</span>            <span class="token keyword">try</span> <span class="token punctuation">{</span>                <span class="token comment">// 通过反射获取实体类的公共字段的set方法</span>                <span class="token class-name">Method</span> setCreateUser <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_CREATE_USER</span><span class="token punctuation">,</span> <span class="token class-name">Long</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token class-name">Method</span> setCreateTime <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_CREATE_TIME</span><span class="token punctuation">,</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token class-name">Method</span> setUpdateUser <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_UPDATE_USER</span><span class="token punctuation">,</span> <span class="token class-name">Long</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token class-name">Method</span> setUpdateTime <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_UPDATE_TIME</span><span class="token punctuation">,</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token comment">// 调用set方法，给实体类对象的公共字段赋值</span>                setCreateUser<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> currentId<span class="token punctuation">)</span><span class="token punctuation">;</span>                setCreateTime<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> now<span class="token punctuation">)</span><span class="token punctuation">;</span>                setUpdateUser<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> currentId<span class="token punctuation">)</span><span class="token punctuation">;</span>                setUpdateTime<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> now<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchMethodException</span> <span class="token operator">|</span> <span class="token class-name">IllegalAccessException</span> <span class="token operator">|</span> <span class="token class-name">InvocationTargetException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>operationType <span class="token operator">==</span> <span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">UPDATE</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">// 更新操作，准备更新时需要填充的字段及其值 (两个字段)</span>            <span class="token keyword">try</span> <span class="token punctuation">{</span>                <span class="token comment">// 通过反射获取实体类的公共字段的set方法</span>                <span class="token class-name">Method</span> setUpdateUser <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_UPDATE_USER</span><span class="token punctuation">,</span> <span class="token class-name">Long</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token class-name">Method</span> setUpdateTime <span class="token operator">=</span> entity<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDeclaredMethod</span><span class="token punctuation">(</span><span class="token class-name">AutoFillConstant</span><span class="token punctuation">.</span><span class="token constant">SET_UPDATE_TIME</span><span class="token punctuation">,</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>                <span class="token comment">// 调用set方法，给实体类对象的公共字段赋值</span>                setUpdateUser<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> currentId<span class="token punctuation">)</span><span class="token punctuation">;</span>                setUpdateTime<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span>entity<span class="token punctuation">,</span> now<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchMethodException</span> <span class="token operator">|</span> <span class="token class-name">IllegalAccessException</span> <span class="token operator">|</span> <span class="token class-name">InvocationTargetException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token punctuation">}</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="3）在Mapper的编辑（插入、更新）相关方法加上注解"><a href="#3）在Mapper的编辑（插入、更新）相关方法加上注解" class="headerlink" title="3）在Mapper的编辑（插入、更新）相关方法加上注解"></a>3）在Mapper的编辑（插入、更新）相关方法加上注解<img src="/2025/10/12/sky-takeout-day03-note/image-20251012100810762.png" alt="image-20251012100810762" style="zoom:33%;"></h4><p><strong>同时</strong>，将业务层为公共字段赋值的代码注释掉。</p><p>1). 将员工管理的新增和编辑方法中的公共字段赋值的代码注释。</p><p>2). 将菜品分类管理的新增和修改方法中的公共字段赋值的代码注释。</p><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><ul><li><p>初步测试，发现前置通知成功拦截方法</p>  <img src="/2025/10/12/sky-takeout-day03-note/image-20251012101400679.png" alt="image-20251012101400679" style="zoom:33%;"></li><li><p>完善切面类中前置通知的公共字段自动填充逻辑后，进行调试，观察线程和变量捕获情况</p>  <img src="/2025/10/12/sky-takeout-day03-note/image-20251012121509064.png" alt="image-20251012121509064" style="zoom:50%;"></li></ul><h2 id="2-新增菜品"><a href="#2-新增菜品" class="headerlink" title="2.新增菜品"></a>2.新增菜品</h2><h3 id="需求分析与接口设计"><a href="#需求分析与接口设计" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p>新增菜品的<strong>产品原型</strong>：<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012122838118.png" alt="image-20251012122838118" style="zoom: 50%;"></p><p><strong>业务规则：</strong></p><ul><li>菜品名称必须是唯一的</li><li>菜品必须属于某个分类下，不能单独存在</li><li>新增菜品时可以根据情况选择菜品的口味</li><li>每个菜品必须对应一张图片</li></ul><p>**接口设计：**明确每个接口的请求方式、请求路径、传入参数和返回值。</p><ul><li>根据类型查询分类（已完成）</li><li>文件上传</li><li>新增菜品</li></ul><p><strong>1. 根据类型查询分类</strong></p><p><img src="/2025/10/12/sky-takeout-day03-note/../../my-note/typora/my-sky-takeout-note/day03.assets/image-20221121165033612-1760243432816-9.png" alt="image-20221121165033612" style="zoom:50%;"> <img src="/2025/10/12/sky-takeout-day03-note/image-20221121165043619.png" alt="image-20221121165043619" style="zoom:50%;"></p><p><strong>2. 文件上传</strong></p><p><img src="/2025/10/12/sky-takeout-day03-note/../../my-note/typora/my-sky-takeout-note/day03.assets/image-20221121165201319-1760243432816-11.png" alt="image-20221121165201319" style="zoom:50%;"><img src="/2025/10/12/sky-takeout-day03-note/image-20221121165215634.png" alt="image-20221121165215634" style="zoom:50%;"></p><p><strong>3. 新增菜品</strong></p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20221121165254961.png" alt="image-20221121165254961" style="zoom: 50%;"><img src="/2025/10/12/sky-takeout-day03-note/../../TODO/cqwm/讲义/day03/assets/image-20221121165308394.png" alt="image-20221121165308394" style="zoom: 50%;"><img src="/2025/10/12/sky-takeout-day03-note/../../my-note/typora/my-sky-takeout-note/day03.assets/image-20221121165322687-1760243432807-7.png" alt="image-20221121165322687" style="zoom: 50%;"></p><p>**数据库设计：**涉及两张表——dish和dish_flavor，通过逻辑外键建立联系</p><p><strong>1). 菜品表:dish</strong></p><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>菜品名称</td><td>唯一</td></tr><tr><td>category_id</td><td>bigint</td><td>分类id</td><td>逻辑外键</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>菜品价格</td><td></td></tr><tr><td>image</td><td>varchar(255)</td><td>图片路径</td><td></td></tr><tr><td>description</td><td>varchar(255)</td><td>菜品描述</td><td></td></tr><tr><td>status</td><td>int</td><td>售卖状态</td><td>1起售 0停售</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td><td></td></tr><tr><td>update_time</td><td>datetime</td><td>最后修改时间</td><td></td></tr><tr><td>create_user</td><td>bigint</td><td>创建人id</td><td></td></tr><tr><td>update_user</td><td>bigint</td><td>最后修改人id</td><td></td></tr></tbody></table><p><strong>2). 菜品口味表:dish_flavor</strong></p><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>dish_id</td><td>bigint</td><td>菜品id</td><td>逻辑外键</td></tr><tr><td>name</td><td>varchar(32)</td><td>口味名称</td><td></td></tr><tr><td>value</td><td>varchar(255)</td><td>口味值</td><td></td></tr></tbody></table><h3 id="代码编写-1"><a href="#代码编写-1" class="headerlink" title="代码编写"></a>代码编写</h3><h4 id="1-文件上传"><a href="#1-文件上传" class="headerlink" title="1. 文件上传"></a>1. 文件上传</h4><p>在项目初始代码已有一部分阿里OSS配置文件<br><strong>AliOssProperties.java</strong>  （阿里云OSS属性配置JavaBean类）</p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012142649962.png" alt="image-20251012142649962"></p><p><strong>application.yml</strong> （这里读取了配置属性JavaBean类的字段值）</p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012142702849.png" alt="image-20251012142702849"></p><p><strong>AliOssUtil.java</strong> （这里是文件上传的具体实现）</p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012142710484.png" alt="image-20251012142710484"></p><p><strong>OssConfiguration.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>config</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>properties<span class="token punctuation">.</span></span><span class="token class-name">AliOssProperties</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>utils<span class="token punctuation">.</span></span><span class="token class-name">AliOssUtil</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>autoconfigure<span class="token punctuation">.</span>condition<span class="token punctuation">.</span></span><span class="token class-name">ConditionalOnMissingBean</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Bean</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Configuration</span></span><span class="token punctuation">;</span><span class="token comment">/** * OSS配置类, 用于创建AliOssUtil对象, 读取配置文件中的OSS相关配置, 并提供给AliOssUtil使用 */</span><span class="token annotation punctuation">@Configuration</span> <span class="token comment">// 表示该类是一个配置类, 可以包含Bean定义</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OssConfiguration</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Bean</span> <span class="token comment">// 将方法的返回值作为Bean对象注册到Spring容器中</span>    <span class="token annotation punctuation">@ConditionalOnMissingBean</span> <span class="token comment">// 当容器中没有指定类型的Bean时, 才会创建该Bean</span>    <span class="token keyword">public</span> <span class="token class-name">AliOssUtil</span> <span class="token function">aliOssUtil</span><span class="token punctuation">(</span><span class="token class-name">AliOssProperties</span> aliOssProperties<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"开始创建阿里云文件上传工具类对象AliOssUtil, {}"</span><span class="token punctuation">,</span> aliOssProperties<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">AliOssUtil</span><span class="token punctuation">(</span>                aliOssProperties<span class="token punctuation">.</span><span class="token function">getEndpoint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>                aliOssProperties<span class="token punctuation">.</span><span class="token function">getAccessKeyId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>                aliOssProperties<span class="token punctuation">.</span><span class="token function">getAccessKeySecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>                aliOssProperties<span class="token punctuation">.</span><span class="token function">getBucketName</span><span class="token punctuation">(</span><span class="token punctuation">)</span>        <span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>CommonController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>admin</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">MessageConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>utils<span class="token punctuation">.</span></span><span class="token class-name">AliOssUtil</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RequestMapping</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">RestController</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>multipart<span class="token punctuation">.</span></span><span class="token class-name">MultipartFile</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>io<span class="token punctuation">.</span></span><span class="token class-name">IOException</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">UUID</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@Slf4j</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/admin/common"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"通用接口"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CommonController</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">AliOssUtil</span> aliOssUtil<span class="token punctuation">;</span>    <span class="token comment">/**     * 文件上传     *     * @param file 文件     * @return 文件访问路径     */</span>    <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/upload"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"文件上传"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">upload</span><span class="token punctuation">(</span><span class="token class-name">MultipartFile</span> file<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"文件上传: {}"</span><span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 获取原始文件名</span>        <span class="token class-name">String</span> originalFilename <span class="token operator">=</span> file<span class="token punctuation">.</span><span class="token function">getOriginalFilename</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 获取文件后缀名</span>        <span class="token class-name">String</span> extension <span class="token operator">=</span> originalFilename<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>originalFilename<span class="token punctuation">.</span><span class="token function">lastIndexOf</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 构造新文件名(防止文件名重复)</span>        <span class="token class-name">String</span> objectName <span class="token operator">=</span> <span class="token constant">UUID</span><span class="token punctuation">.</span><span class="token function">randomUUID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> extension<span class="token punctuation">;</span>        <span class="token comment">// 上传文件到OSS, 并接收返回的文件访问路径</span>        <span class="token keyword">try</span> <span class="token punctuation">{</span>            <span class="token class-name">String</span> fileUrl <span class="token operator">=</span> aliOssUtil<span class="token punctuation">.</span><span class="token function">upload</span><span class="token punctuation">(</span>file<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> objectName<span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment">// 返回文件访问路径</span>            <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>fileUrl<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>            log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"文件上传失败: {}"</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">UPLOAD_FAILED</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 上传失败</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-新增菜品-1"><a href="#2-新增菜品-1" class="headerlink" title="2. 新增菜品"></a>2. 新增菜品</h4><p><strong>DishController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>admin</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">DishDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">DishService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/admin/dish"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"菜品管理"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DishController</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">DishService</span> dishService<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增菜品     * @param dishDTO 菜品信息     * @return 操作结果     */</span>    <span class="token annotation punctuation">@PostMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"新增菜品"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">DishDTO</span> dishDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 这里DTO信息记得要加RequestBody，否则接受不到数据 @RequestBody 将请求体中的json数据转换为对应的Java对象</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"新增菜品: {}"</span><span class="token punctuation">,</span> dishDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        dishService<span class="token punctuation">.</span><span class="token function">saveWithFlavor</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishServiceImpl.java</strong></p><p>这里注意涉及多表要<strong>开启事务</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span>impl</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">DishDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Dish</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">DishFlavor</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">DishFlavorMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">DishMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">DishService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span></span><span class="token class-name">BeanUtils</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Service</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Service</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DishServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">DishService</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">DishMapper</span> dishMapper<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">DishFlavorMapper</span> dishFlavorMapper<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增菜品，同时保存对应的口味数据, 所以方法名字WithFlavor体现出来, 需要操作两张表：dish、dish_flavor, 需要开启事务, 保证数据一致性     * @param dishDTO 菜品信息     */</span>    <span class="token annotation punctuation">@Override</span>    <span class="token annotation punctuation">@Transactional</span>  <span class="token comment">// 涉及多表操作，开启事务</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">saveWithFlavor</span><span class="token punctuation">(</span><span class="token class-name">DishDTO</span> dishDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Dish</span> dish <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">,</span> dish<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 1. 向菜品表中插入 1 条数据，保存菜品的基本信息到菜品表dish</span>        dishMapper<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>dish<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 2. 向菜品口味表中插入 n 条数据（一个菜品可能有0 或多个以上口味），保存菜品的口味数据到菜品口味表dish_flavor</span>        <span class="token class-name">Long</span> dishId <span class="token operator">=</span> dish<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取菜品id</span>        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">DishFlavor</span><span class="token punctuation">></span></span> flavors <span class="token operator">=</span> dishDTO<span class="token punctuation">.</span><span class="token function">getFlavors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取口味数据</span>        <span class="token comment">// 需要判断flavors是否为空，不为空才进行插入</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>flavors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>flavors<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token comment">// 遍历口味数据，逐个设置口味对应的菜品id</span>            flavors<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>flavor <span class="token operator">-></span> flavor<span class="token punctuation">.</span><span class="token function">setDishId</span><span class="token punctuation">(</span>dishId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token comment">// 批量插入口味数据</span>            dishFlavorMapper<span class="token punctuation">.</span><span class="token function">insertBatch</span><span class="token punctuation">(</span>flavors<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">AutoFill</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Dish</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>enumeration<span class="token punctuation">.</span></span><span class="token class-name">OperationType</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Select</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">DishMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 根据分类id查询菜品数量     * @param categoryId 分类Id     * @return 数量     */</span>    <span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select count(id) from dish where category_id = #{categoryId}"</span><span class="token punctuation">)</span>    <span class="token class-name">Integer</span> <span class="token function">countByCategoryId</span><span class="token punctuation">(</span><span class="token class-name">Long</span> categoryId<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 插入菜品, 这里记得需要在xml设置属性：useKeyProperty：主键自增, keyProperty：插入后会将生成的主键回填到实体类对象中的id属性     * @param dish 菜品     */</span>    <span class="token annotation punctuation">@AutoFill</span><span class="token punctuation">(</span><span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">INSERT</span><span class="token punctuation">)</span>    <span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token class-name">Dish</span> dish<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishFlavorMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">DishFlavor</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">DishFlavorMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 批量插入口味数据     * @param flavors 口味数据     */</span>    <span class="token keyword">void</span> <span class="token function">insertBatch</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">DishFlavor</span><span class="token punctuation">></span></span> flavors<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" ?></span><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">mapper</span> <span class="token name">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mapper</span> <span class="token attr-name">namespace</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.mapper.DishMapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>insert</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>insert<span class="token punctuation">"</span></span> <span class="token attr-name">useGeneratedKeys</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">keyProperty</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        insert into dish        (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)        values        (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>insert</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mapper</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishFlavorMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" ?></span><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">mapper</span> <span class="token name">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mapper</span> <span class="token attr-name">namespace</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.mapper.DishFlavorMapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>insert</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>insertBatch<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        insert into dish_flavor (dish_id, name, value) values        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flavors<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>df<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            (#{df.dishId}, #{df.name}, #{df.value})        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>insert</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mapper</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="Debug问题发现记录"><a href="#Debug问题发现记录" class="headerlink" title="Debug问题发现记录"></a>Debug问题发现记录</h3><h4 id="1）在Controller那里，DTO是json传来的，需要加RequestBody注解，否则会导致接收不到数据，全为null"><a href="#1）在Controller那里，DTO是json传来的，需要加RequestBody注解，否则会导致接收不到数据，全为null" class="headerlink" title="1）在Controller那里，DTO是json传来的，需要加RequestBody注解，否则会导致接收不到数据，全为null"></a>1）在Controller那里，DTO是json传来的，需要加RequestBody注解，否则会导致接收不到数据，全为null</h4><h4 id="2）在dishMapper-xml那里不要忘记加两个属性值配置：useGeneratedKeys-true-和-keyProperty-”id”"><a href="#2）在dishMapper-xml那里不要忘记加两个属性值配置：useGeneratedKeys-true-和-keyProperty-”id”" class="headerlink" title="2）在dishMapper.xml那里不要忘记加两个属性值配置：useGeneratedKeys &#x3D; true 和 keyProperty &#x3D;  ”id”"></a>2）在dishMapper.xml那里不要忘记加两个属性值配置：useGeneratedKeys &#x3D; true 和 keyProperty &#x3D;  ”id”</h4><h4 id="3）dish-flavor表没有公共字段需要填充，不用加AutoFill注解"><a href="#3）dish-flavor表没有公共字段需要填充，不用加AutoFill注解" class="headerlink" title="3）dish_flavor表没有公共字段需要填充，不用加AutoFill注解"></a>3）dish_flavor表没有公共字段需要填充，不用加AutoFill注解</h4><h4 id="4）还有一个Bug是因为dishFlavor中insert-into的字段顺序和foreach里面的顺序没对应上，导致insertBatch操作一直有问题但是没报错，因为一开始那里是用代码补全来写的，后面仔细看才发现顺序没对上😭"><a href="#4）还有一个Bug是因为dishFlavor中insert-into的字段顺序和foreach里面的顺序没对应上，导致insertBatch操作一直有问题但是没报错，因为一开始那里是用代码补全来写的，后面仔细看才发现顺序没对上😭" class="headerlink" title="4）还有一个Bug是因为dishFlavor中insert into的字段顺序和foreach里面的顺序没对应上，导致insertBatch操作一直有问题但是没报错，因为一开始那里是用代码补全来写的，后面仔细看才发现顺序没对上😭"></a>4）还有一个Bug是因为dishFlavor中insert into的字段顺序和foreach里面的顺序没对应上，导致insertBatch操作一直有问题但是没报错，因为一开始那里是用代码补全来写的，后面仔细看才发现顺序没对上😭</h4><h4 id="5）写分页查询发现遗漏了一点，这里做个补充，涉及多表要开启事务-Transactional注解保证原子性（在Service那里）"><a href="#5）写分页查询发现遗漏了一点，这里做个补充，涉及多表要开启事务-Transactional注解保证原子性（在Service那里）" class="headerlink" title="5）写分页查询发现遗漏了一点，这里做个补充，涉及多表要开启事务@Transactional注解保证原子性（在Service那里）"></a>5）写分页查询发现遗漏了一点，这里做个补充，涉及多表要<strong>开启事务</strong>@Transactional注解保证原子性（在Service那里）</h4><h3 id="功能测试-1"><a href="#功能测试-1" class="headerlink" title="功能测试"></a>功能测试</h3><h4 id="文件上传测试"><a href="#文件上传测试" class="headerlink" title="文件上传测试"></a>文件上传测试</h4><p>在文件上传时，若文件成功上传到阿里云，但是前端没有回显，需要在阿里云做如下配置 ：<br>设置文件管理的 “阻止公共访问” 和 ”读写权限”</p><p>1）关闭阻止公共访问<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012141856723.png" alt="image-20251012141856723" style="zoom:33%;"></p><p>2）读写权限设为公共读<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012141904531.png" alt="image-20251012141904531" style="zoom:33%;"></p><p>设置好后发现图片显示就没问题了</p><p>前端图片显示正常<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012142202054.png" alt="image-20251012142202054" style="zoom: 25%;"></p><p>成功上传到阿里云OSS<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012142212893.png" alt="image-20251012142212893" style="zoom:25%;"></p><h4 id="新增菜品测试"><a href="#新增菜品测试" class="headerlink" title="新增菜品测试"></a>新增菜品测试</h4><p>注意：在测试新增菜品时，名字不要设置成在表中已有的，我一开始拿“北冰洋”来测试发现一直失败，后面才发现是名字要唯一，表中初始自带了北冰洋所以测试不了</p><p>测试成功图，因为还没做分页查询，所以直接查看数据库是否添加成功新数据</p><p>菜品添加成功<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012153940610.png" alt="image-20251012153940610"></p><p>口味添加成功<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012155802631.png" alt="image-20251012155802631"></p><h2 id="3-菜品分页查询"><a href="#3-菜品分页查询" class="headerlink" title="3.菜品分页查询"></a>3.菜品分页查询</h2><h3 id="需求分析与接口设计-1"><a href="#需求分析与接口设计-1" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>业务规则：</strong></p><ul><li>根据页码展示菜品信息</li><li>每页展示10条数据</li><li>分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询</li></ul><p><strong>接口设计：</strong><br><img src="/2025/10/12/sky-takeout-day03-note/image-20221121202019258.png" alt="image-20221121202019258" style="zoom:50%;"> <img src="/2025/10/12/sky-takeout-day03-note/../../TODO/cqwm/讲义/day03/assets/image-20221121202033284.png" alt="image-20221121202033284" style="zoom:50%;"></p><ul><li><p>这里大体上步骤跟前面员工分页查询一致，但是需要注意的是由于前端展示Dish时还需要分类名称，所以多封装了一个 <strong>DishVO</strong> （除了多分类名称，还有口味数组）实体类作为分页结果<strong>Page的泛型</strong>。</p>  <img src="/2025/10/12/sky-takeout-day03-note/image-20251012165640112.png" alt="image-20251012165640112" style="zoom:33%;"></li><li><p>此外就是这个分页查询会涉及到<strong>dish和category两张表</strong>的，用了一个<strong>左外连接</strong>来查询二表信息。</p></li></ul><h3 id="代码编写-2"><a href="#代码编写-2" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>DishController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 分页查询菜品 * @param dishPageQueryDTO 分页查询参数 * @return 分页查询结果 */</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/page"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"分页查询菜品"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">PageResult</span><span class="token punctuation">></span></span> <span class="token function">page</span><span class="token punctuation">(</span><span class="token class-name">DishPageQueryDTO</span> dishPageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"分页查询菜品: {}"</span><span class="token punctuation">,</span> dishPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">PageResult</span> pageResult <span class="token operator">=</span> dishService<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>dishPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>pageResult<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 分页查询菜品 * @param dishPageQueryDTO 分页查询参数 * @return 分页查询结果 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">PageResult</span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">DishPageQueryDTO</span> dishPageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">PageHelper</span><span class="token punctuation">.</span><span class="token function">startPage</span><span class="token punctuation">(</span>dishPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> dishPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPageSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 需要注意的是这里 Page 的泛型是 DishVO， 因为返回给前端的视图，除了要有菜品信息Dish，还要有分类名称categoryName，而项目中的 DishVO 中已经做好了分类名称的封装</span>    <span class="token comment">// 具体分类名称的封装是在 mapper.xml 中通过 left join 关联查询的 (c.name as categoryName), 见 dishMapper.xml</span>    <span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">DishVO</span><span class="token punctuation">></span></span> page <span class="token operator">=</span> dishMapper<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>dishPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PageResult</span><span class="token punctuation">(</span>page<span class="token punctuation">.</span><span class="token function">getTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span><span class="token function">getResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pageQuery<span class="token punctuation">"</span></span> <span class="token attr-name">resultType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.vo.DishVO<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        select d.*, c.name ascategoryName        from dish d left outer join category c        on d.category_id = c.id        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>where</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name != null and name != ''<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                and d.name like concat('%', #{name}, '%')            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>categoryId != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>>                and d.category_id = #{categoryId}            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>                and d.status = #{status}            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>where</span><span class="token punctuation">></span></span>        order by d.update_time desc    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-2"><a href="#功能测试-2" class="headerlink" title="功能测试"></a>功能测试</h3><p>接口文档测试</p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012170915227.png" alt="image-20251012170915227"></p><p>前后端联调测试（图片不在我的阿里云所以显示不了，这是正常的）</p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012170933704.png" alt="image-20251012170933704"></p><h2 id="4-删除菜品"><a href="#4-删除菜品" class="headerlink" title="4.删除菜品"></a>4.删除菜品</h2><h3 id="需求分析与接口设计-2"><a href="#需求分析与接口设计-2" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>业务规则：</strong></p><ul><li>可以一次删除一个菜品，也可以批量删除菜品</li><li>起售中的菜品不能删除</li><li>被套餐关联的菜品不能删除</li><li>删除菜品后，关联的口味数据也需要删除掉</li></ul><p><strong>接口设计:</strong><br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012171359093.png" alt="image-20251012171359093" style="zoom: 50%;"></p><p>**注意：**删除一个菜品和批量删除菜品共用一个接口，故ids可包含多个菜品id,之间用逗号分隔。</p><p><strong>表设计：</strong></p><p>菜品删除会涉及三张表<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012171525991.png" alt="image-20251012171525991" style="zoom:33%;"></p><p><strong>注意事项：</strong></p><ul><li>在dish表中删除菜品基本数据时，同时，也要把关联在dish_flavor表中的数据一块删除。</li><li>setmeal_dish表为菜品和套餐关联的中间表。</li><li>若删除的菜品数据关联着某个套餐，此时，删除失败。</li><li>若要删除套餐关联的菜品数据，先解除两者关联，再对菜品进行删除。</li></ul><h3 id="代码编写-3"><a href="#代码编写-3" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>DishController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * (批量)删除菜品 * @param ids 菜品id，多个id用逗号分隔 * @return 操作结果 */</span><span class="token annotation punctuation">@DeleteMapping</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"删除菜品"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">delete</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">"ids"</span><span class="token punctuation">)</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 这里用@RequestParam注解接收ids参数</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"(批量)删除菜品: {}"</span><span class="token punctuation">,</span> ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    dishService<span class="token punctuation">.</span><span class="token function">deleteBatch</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * (批量)删除菜品 * @param ids 菜品id，多个id用逗号分隔 */</span><span class="token annotation punctuation">@Override</span><span class="token annotation punctuation">@Transactional</span>  <span class="token comment">// 涉及多表操作，开启事务</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteBatch</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 菜品处于起售状态，不能删除</span>    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id <span class="token operator">:</span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Dish</span> dish <span class="token operator">=</span> dishMapper<span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>dish<span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">ENABLE</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DeletionNotAllowedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">DISH_ON_SALE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>    <span class="token punctuation">}</span>    <span class="token comment">// 菜品被套餐关联，不能删除</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> setmealIds <span class="token operator">=</span> setmealDishMapper<span class="token punctuation">.</span><span class="token function">getSetmealIdsByDishIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>setmealIds <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>setmealIds<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DeletionNotAllowedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">DISH_BE_RELATED_BY_SETMEAL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">// 这里有个优化，为了避免循环删除多次sql操作，可以直接批量删除，用in语句 (多次SQL语句执行效率低)</span>    <span class="token comment">// 删除菜品数据</span>    <span class="token comment">// SQL: delete from dish where id in (1, 2, 3);</span>    dishMapper<span class="token punctuation">.</span><span class="token function">deleteBatchByIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 删除菜品关联的口味数据</span>    <span class="token comment">// SQL: delete from dish_flavor where dish_id in (1, 2, 3);</span>    dishFlavorMapper<span class="token punctuation">.</span><span class="token function">deleteBatchByDishIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询菜品 * @param id 菜品id * @return 菜品 */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from dish where id = #{id}"</span><span class="token punctuation">)</span><span class="token class-name">Dish</span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/** * 批量删除菜品 * @param ids 菜品id，多个id用逗号分隔 */</span><span class="token keyword">void</span> <span class="token function">deleteBatchByIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>delete</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>deleteBatchByIds<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    delete from dish    where id in    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ids<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        #{id}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>delete</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealDishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">SetmealDishMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 根据菜品id查询对应的套餐id     * @param dishIds 菜品id     * @return 套餐id     */</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> <span class="token function">getSetmealIdsByDishIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> dishIds<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealDishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8" ?></span><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">mapper</span> <span class="token name">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mapper</span> <span class="token attr-name">namespace</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.sky.mapper.SetmealDishMapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>getSetmealIdsByDishIds<span class="token punctuation">"</span></span> <span class="token attr-name">resultType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>java.lang.Long<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        select setmeal_id from setmeal_dish        where dish_id in        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dishIds<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dishId<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>            #{dishId}        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mapper</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishFlavorMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据菜品集合ids批量删除对应的口味数据 * @param dishIds 菜品id，多个id用逗号分隔 */</span><span class="token keyword">void</span> <span class="token function">deleteBatchByDishIds</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> dishIds<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishFlavorMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>delete</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>deleteBatchByDishIds<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    delete from dish_flavor    where dish_id in    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dishIds<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dishId<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        #{dishId}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>delete</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-3"><a href="#功能测试-3" class="headerlink" title="功能测试"></a>功能测试</h3><p>略</p><h2 id="5-修改菜品"><a href="#5-修改菜品" class="headerlink" title="5. 修改菜品"></a>5. 修改菜品</h2><h3 id="需求分析与接口设计-3"><a href="#需求分析与接口设计-3" class="headerlink" title="需求分析与接口设计"></a>需求分析与接口设计</h3><p><strong>修改菜品原型</strong><br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012183851728.png" alt="image-20251012183851728" style="zoom: 50%;"></p><p><strong>接口设计：</strong></p><ul><li>根据id查询菜品</li><li>根据类型查询分类(已实现)</li><li>文件上传(已实现)</li><li>修改菜品</li></ul><img src="/2025/10/12/sky-takeout-day03-note/image-20251012184023473.png" alt="image-20251012184023473" style="zoom: 50%;"><p>这里要注意修改菜品 ，<strong>id字段是必须的</strong><br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012184045407.png" alt="image-20251012184045407" style="zoom:50%;"></p><h3 id="代码编写-4"><a href="#代码编写-4" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>DishController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 修改菜品, 同时更新对应的口味数据 * @param dishDTO 菜品信息 * @return 操作结果 */</span><span class="token annotation punctuation">@PutMapping</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"修改菜品"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">DishDTO</span> dishDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"修改菜品: {}"</span><span class="token punctuation">,</span> dishDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    dishService<span class="token punctuation">.</span><span class="token function">updateWithFlavor</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 修改菜品，同时更新对应的口味数据 * @param dishDTO 菜品信息 */</span><span class="token annotation punctuation">@Override</span><span class="token annotation punctuation">@Transactional</span>  <span class="token comment">// 涉及多表操作，开启事务</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">updateWithFlavor</span><span class="token punctuation">(</span><span class="token class-name">DishDTO</span> dishDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 1. 更新菜品基本信息到菜品表dish</span>    <span class="token class-name">Dish</span> dish <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">,</span> dish<span class="token punctuation">)</span><span class="token punctuation">;</span>    dishMapper<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>dish<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids <span class="token operator">=</span> <span class="token class-name">List</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 删除原有口味数据</span>    dishFlavorMapper<span class="token punctuation">.</span><span class="token function">deleteBatchByDishIds</span><span class="token punctuation">(</span>ids<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 重新插入口味数据</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">DishFlavor</span><span class="token punctuation">></span></span> flavors <span class="token operator">=</span> dishDTO<span class="token punctuation">.</span><span class="token function">getFlavors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取口味数据</span>    <span class="token comment">// 需要判断flavors是否为空，不为空才进行插入</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>flavors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>flavors<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment">// 遍历口味数据，逐个设置口味对应的菜品id</span>        flavors<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>flavor <span class="token operator">-></span> flavor<span class="token punctuation">.</span><span class="token function">setDishId</span><span class="token punctuation">(</span>dishDTO<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 批量插入口味数据</span>        dishFlavorMapper<span class="token punctuation">.</span><span class="token function">insertBatch</span><span class="token punctuation">(</span>flavors<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**  * 修改菜品  * @param dish 菜品 */</span><span class="token annotation punctuation">@AutoFill</span><span class="token punctuation">(</span><span class="token class-name">OperationType</span><span class="token punctuation">.</span><span class="token constant">UPDATE</span><span class="token punctuation">)</span> <span class="token comment">// 修改操作，自动填充</span><span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">Dish</span> dish<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.xml</strong></p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>update</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>update<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    update dish    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>set</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>name = #{name},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>categoryId != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>category_id = #{categoryId},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>price != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>price = #{price},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>image = #{image},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>description = #{description},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>status = #{status},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>updateTime != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>update_time = #{updateTime},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>updateUser != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>update_user = #{updateUser}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>set</span><span class="token punctuation">></span></span>    where id = #{id}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>update</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishFlavorMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据菜品id查询对应的口味数据 * @param dishId 菜品id * @return 口味数据 */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from dish_flavor where dish_id = #{dishId}"</span><span class="token punctuation">)</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">DishFlavor</span><span class="token punctuation">></span></span> <span class="token function">getByDishId</span><span class="token punctuation">(</span><span class="token class-name">Long</span> dishId<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-4"><a href="#功能测试-4" class="headerlink" title="功能测试"></a>功能测试</h3><p>根据id查询菜品功能测试：</p><p>菜品信息及口味信息正常回显<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012185442072.png" alt="image-20251012185442072"></p><p>修改菜品信息功能测试：<br><img src="/2025/10/12/sky-takeout-day03-note/image-20251012195244620.png" alt="image-20251012195244620"></p><p><img src="/2025/10/12/sky-takeout-day03-note/image-20251012195254125.png" alt="image-20251012195254125"></p><h2 id="附：其他盲区知识补充"><a href="#附：其他盲区知识补充" class="headerlink" title="附：其他盲区知识补充"></a>附：其他盲区知识补充</h2><h3 id="在阿里云OSS实现文件上传时——关于属性类、工具类、配置类的疑问"><a href="#在阿里云OSS实现文件上传时——关于属性类、工具类、配置类的疑问" class="headerlink" title="在阿里云OSS实现文件上传时——关于属性类、工具类、配置类的疑问"></a>在阿里云OSS实现文件上传时——关于属性类、工具类、配置类的疑问</h3><p>在苍穹外卖项目中，“工具类属性为空，需通过<code>config</code>包下的配置类注入” 的设计既体现了<strong>软件设计的核心思路</strong>，也是 Spring Boot 开发的<strong>固定套路</strong>。以下从 “设计本质”“核心思路”“实际好处” 三个维度详细解释：</p><h4 id="一、本质：解决「配置硬编码」与「环境隔离」的问题"><a href="#一、本质：解决「配置硬编码」与「环境隔离」的问题" class="headerlink" title="一、本质：解决「配置硬编码」与「环境隔离」的问题"></a>一、本质：解决「配置硬编码」与「环境隔离」的问题</h4><p>阿里云 OSS 的配置（<code>endpoint</code>、<code>accessKeyId</code>、<code>accessKeySecret</code>、<code>bucketName</code>）属于<strong>环境敏感信息</strong>（开发 &#x2F; 测试 &#x2F; 生产环境的配置不同），如果直接写死在<code>AliOssUtil</code>工具类中，会导致以下问题：</p><ol><li><strong>硬编码风险</strong>：配置修改需重新编译代码、打包部署，运维成本高；</li><li><strong>安全隐患</strong>：密钥信息暴露在代码中，易被泄露；</li><li><strong>环境混乱</strong>：不同环境的配置混在代码里，易引发线上事故。</li></ol><h4 id="二、核心设计思路：「配置外置化-依赖注入-解耦」"><a href="#二、核心设计思路：「配置外置化-依赖注入-解耦」" class="headerlink" title="二、核心设计思路：「配置外置化 + 依赖注入 + 解耦」"></a>二、核心设计思路：「配置外置化 + 依赖注入 + 解耦」</h4><p>通过<code>config</code>包下的配置类管理 OSS 属性，本质是<strong>将 “配置逻辑” 与 “业务逻辑” 分离</strong>，遵循以下设计原则：</p><h5 id="1-配置外置化（把配置从代码中抽离）"><a href="#1-配置外置化（把配置从代码中抽离）" class="headerlink" title="1. 配置外置化（把配置从代码中抽离）"></a>1. 配置外置化（把配置从代码中抽离）</h5><ul><li><p>步骤</p><p>  ：将 OSS 配置写入application.yml （或 application-dev.yml &#x2F; application-prod.yml）：</p>  <pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">aliyun</span><span class="token punctuation">:</span>  <span class="token key atrule">oss</span><span class="token punctuation">:</span>    <span class="token key atrule">endpoint</span><span class="token punctuation">:</span> oss<span class="token punctuation">-</span>cn<span class="token punctuation">-</span>hangzhou.aliyuncs.com    <span class="token key atrule">access-key-id</span><span class="token punctuation">:</span> LTAI5t9xxxxxxxxx    <span class="token key atrule">access-key-secret</span><span class="token punctuation">:</span> 3dxxxxxxxxx    <span class="token key atrule">bucket-name</span><span class="token punctuation">:</span> sky<span class="token punctuation">-</span>take<span class="token punctuation">-</span>out<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p><strong>目的</strong>：配置与代码解耦，修改配置无需动代码，重启服务即可生效。</p></li></ul><h5 id="2-依赖注入（让-Spring-管理工具类的属性）"><a href="#2-依赖注入（让-Spring-管理工具类的属性）" class="headerlink" title="2. 依赖注入（让 Spring 管理工具类的属性）"></a>2. 依赖注入（让 Spring 管理工具类的属性）</h5><p><code>AliOssUtil</code>工具类本身是 “业务逻辑类”（负责文件上传），不应关心 “配置从哪来”。通过<strong>配置类 + <code>@ConfigurationProperties</code></strong> 实现 “配置注入”：</p><h6 id="（1）创建配置类（如OssConfiguration）"><a href="#（1）创建配置类（如OssConfiguration）" class="headerlink" title="（1）创建配置类（如OssConfiguration）"></a>（1）创建配置类（如<code>OssConfiguration</code>）</h6><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Configuration</span> <span class="token comment">// 标记为Spring配置类</span><span class="token annotation punctuation">@ConfigurationProperties</span><span class="token punctuation">(</span>prefix <span class="token operator">=</span> <span class="token string">"aliyun.oss"</span><span class="token punctuation">)</span> <span class="token comment">// 绑定application.yml中以aliyun.oss开头的配置</span><span class="token annotation punctuation">@Data</span> <span class="token comment">// 自动生成getter/setter</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OssProperties</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> endpoint<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> accessKeyId<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> accessKeySecret<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> bucketName<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h6 id="（2）将工具类注册为-Spring-Bean-并注入配置"><a href="#（2）将工具类注册为-Spring-Bean-并注入配置" class="headerlink" title="（2）将工具类注册为 Spring Bean 并注入配置"></a>（2）将工具类注册为 Spring Bean 并注入配置</h6><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Configuration</span> <span class="token comment">// 配置类</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OssConfiguration</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">OssProperties</span> ossProperties<span class="token punctuation">;</span> <span class="token comment">// 注入配置属性</span>    <span class="token annotation punctuation">@Bean</span> <span class="token comment">// 将AliOssUtil注册为Spring Bean</span>    <span class="token keyword">public</span> <span class="token class-name">AliOssUtil</span> <span class="token function">aliOssUtil</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">AliOssUtil</span> util <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AliOssUtil</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 给工具类的属性赋值</span>        util<span class="token punctuation">.</span><span class="token function">setEndpoint</span><span class="token punctuation">(</span>ossProperties<span class="token punctuation">.</span><span class="token function">getEndpoint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        util<span class="token punctuation">.</span><span class="token function">setAccessKeyId</span><span class="token punctuation">(</span>ossProperties<span class="token punctuation">.</span><span class="token function">getAccessKeyId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        util<span class="token punctuation">.</span><span class="token function">setAccessKeySecret</span><span class="token punctuation">(</span>ossProperties<span class="token punctuation">.</span><span class="token function">getAccessKeySecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        util<span class="token punctuation">.</span><span class="token function">setBucketName</span><span class="token punctuation">(</span>ossProperties<span class="token punctuation">.</span><span class="token function">getBucketName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> util<span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h6 id="（3）工具类定义（简洁化，只关注业务）"><a href="#（3）工具类定义（简洁化，只关注业务）" class="headerlink" title="（3）工具类定义（简洁化，只关注业务）"></a>（3）工具类定义（简洁化，只关注业务）</h6><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Component</span> <span class="token comment">// 让Spring管理</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AliOssUtil</span> <span class="token punctuation">{</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> endpoint<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> accessKeyId<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> accessKeySecret<span class="token punctuation">;</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> bucketName<span class="token punctuation">;</span>    <span class="token comment">// setter方法（供配置类注入）</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setEndpoint</span><span class="token punctuation">(</span><span class="token class-name">String</span> endpoint<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>endpoint <span class="token operator">=</span> endpoint<span class="token punctuation">;</span> <span class="token punctuation">}</span>    <span class="token comment">// 其他setter...</span>    <span class="token comment">// 业务方法：文件上传</span>    <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">upload</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes<span class="token punctuation">,</span> <span class="token class-name">String</span> objectName<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment">// 使用endpoint、accessKeyId等属性完成OSS上传</span>        <span class="token class-name">OSS</span> ossClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OSSClientBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span>endpoint<span class="token punctuation">,</span> accessKeyId<span class="token punctuation">,</span> accessKeySecret<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// ... 上传逻辑 ...</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="3-解耦（业务逻辑与配置逻辑分离）"><a href="#3-解耦（业务逻辑与配置逻辑分离）" class="headerlink" title="3. 解耦（业务逻辑与配置逻辑分离）"></a>3. 解耦（业务逻辑与配置逻辑分离）</h5><ul><li><strong>业务逻辑（<code>AliOssUtil</code>）</strong>：只负责 “文件上传”，不关心配置从哪来；</li><li><strong>配置逻辑（<code>OssConfiguration</code> + <code>OssProperties</code>）</strong>：只负责 “读取配置、注入属性”，不关心业务怎么用。</li></ul><h4 id="三、这是-Spring-Boot-的「固定套路」"><a href="#三、这是-Spring-Boot-的「固定套路」" class="headerlink" title="三、这是 Spring Boot 的「固定套路」"></a>三、这是 Spring Boot 的「固定套路」</h4><p>在 Spring Boot 开发中，对接 ** 第三方服务（如 OSS、Redis、数据库、消息队列）** 时，几乎都遵循 “配置类 + 工具类 &#x2F; 服务类” 的套路：</p><ol><li><strong>配置外置</strong>：把第三方服务的参数（如密钥、地址）写在<code>application.yml</code>；</li><li><strong>配置类封装</strong>：用<code>@ConfigurationProperties</code>将配置绑定到 Java 类（如<code>OssProperties</code>）；</li><li><strong>Bean 注册 + 注入</strong>：在配置类中创建第三方服务的工具类 Bean（如<code>AliOssUtil</code>），并注入配置属性；</li><li><strong>业务层调用</strong>：其他组件通过<code>@Autowired</code>注入工具类，直接使用。</li></ol><h4 id="四、实际好处（为什么必须这么做？）"><a href="#四、实际好处（为什么必须这么做？）" class="headerlink" title="四、实际好处（为什么必须这么做？）"></a>四、实际好处（为什么必须这么做？）</h4><ol><li><strong>环境隔离</strong>：开发环境用测试 OSS 桶，生产环境用正式桶，只需改配置文件；</li><li><strong>安全合规</strong>：密钥等敏感信息不进代码库，符合企业级安全规范；</li><li><strong>可维护性</strong>：配置集中管理，新人接手时只需看<code>application.yml</code>就知道系统依赖了哪些第三方服务；</li><li><strong>扩展性</strong>：若需切换 OSS 服务商（如从阿里云换成腾讯云），只需修改配置类和工具类，业务层无感知。</li></ol><p>总结：这种设计既是 <strong>“配置外置化 + 依赖注入” 的软件设计思路 <strong>（解耦、可维护），也是 Spring Boot 开发第三方服务的</strong>固定套路</strong>（约定优于配置）。通过 “配置类 + 工具类” 的分工，让代码更优雅、更易维护。</p><hr><blockquote><p><em><strong>以下是项目中的代码的解释</strong></em></p></blockquote><p>结合你提供的代码和之前的设计思路，这两个类（<code>AliOssProperties</code> 和 <code>AliOssUtil</code>）的分工与配合体现了 <strong>“配置与业务分离”</strong> 的核心思想，我们一步步拆解：</p><h3 id="一、AliOssProperties：配置属性的-“容器”"><a href="#一、AliOssProperties：配置属性的-“容器”" class="headerlink" title="一、AliOssProperties：配置属性的 “容器”"></a>一、<code>AliOssProperties</code>：配置属性的 “容器”</h3><p>这个类的核心作用是 <strong>“把配置文件中的 OSS 参数映射到 Java 对象”</strong>，相当于一个 “配置容器”。</p><h4 id="关键注解解析"><a href="#关键注解解析" class="headerlink" title="关键注解解析"></a>关键注解解析</h4><ol><li><p><strong><code>@Component</code></strong>：将该类标记为 Spring 的组件，让 Spring 容器管理它（自动创建实例）。</p></li><li><p><code>@ConfigurationProperties(prefix = &quot;sky.alioss&quot;)</code></p><ul><li><p>告诉 Spring：“去配置文件（如<code>application.yml</code>）中找所有以<code>sky.alioss</code>为前缀的配置项，把它们的值赋给当前类的同名字段”。</p></li><li><p>例如，配置文件中若有：</p>  <pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">sky</span><span class="token punctuation">:</span>  <span class="token key atrule">alioss</span><span class="token punctuation">:</span>    <span class="token key atrule">endpoint</span><span class="token punctuation">:</span> oss<span class="token punctuation">-</span>cn<span class="token punctuation">-</span>beijing.aliyuncs.com    <span class="token key atrule">access-key-id</span><span class="token punctuation">:</span> ABC123    <span class="token key atrule">access-key-secret</span><span class="token punctuation">:</span> XYZ456    <span class="token key atrule">bucket-name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>takeout<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>  Spring 会自动将这些值分别赋给AliOssProperties的endpoint、accessKeyId等字段。</p></li></ul></li><li><p><strong><code>@Data</code></strong>：Lombok 注解，自动生成<code>getter</code>、<code>setter</code>方法（Spring 注入配置时需要这些方法）。</p></li></ol><h4 id="为什么需要这个类？"><a href="#为什么需要这个类？" class="headerlink" title="为什么需要这个类？"></a>为什么需要这个类？</h4><p>如果没有<code>AliOssProperties</code>，要获取配置就得手动写代码读取<code>application.yml</code>（比如用<code>@Value</code>注解逐个注入），但当配置项较多时（如这里的 4 个参数），代码会很繁琐：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 不推荐：用@Value逐个注入，配置项多了会很冗余</span><span class="token annotation punctuation">@Component</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AliOssUtil</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${sky.alioss.endpoint}"</span><span class="token punctuation">)</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> endpoint<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${sky.alioss.access-key-id}"</span><span class="token punctuation">)</span>    <span class="token keyword">private</span> <span class="token class-name">String</span> accessKeyId<span class="token punctuation">;</span>    <span class="token comment">// ... 其他参数</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>而<code>AliOssProperties</code>通过<code>@ConfigurationProperties</code>一次性绑定所有配置，更简洁、更易维护。</p><h3 id="二、AliOssUtil：OSS-操作的-“业务工具”"><a href="#二、AliOssUtil：OSS-操作的-“业务工具”" class="headerlink" title="二、AliOssUtil：OSS 操作的 “业务工具”"></a>二、<code>AliOssUtil</code>：OSS 操作的 “业务工具”</h3><p>这个类是 <strong>“实际干活的工具”</strong>，负责调用阿里云 OSS 的 SDK 完成文件上传，它的核心是<code>upload</code>方法。</p><h4 id="关键设计解析"><a href="#关键设计解析" class="headerlink" title="关键设计解析"></a>关键设计解析</h4><ol><li><code>@AllArgsConstructor</code>：Lombok 注解，自动生成一个包含所有字段（endpoint、accessKeyId等）的构造函数。<ul><li>作用：方便通过 “构造函数注入” 的方式，将<code>AliOssProperties</code>中的配置参数传递给<code>AliOssUtil</code>（后面会讲如何配合）。</li></ul></li><li><strong>字段设计</strong>：<code>endpoint</code>、<code>accessKeyId</code>等字段与<code>AliOssProperties</code>完全对应，因为这些是 OSS 上传必须的参数。</li><li><strong><code>upload</code>方法</strong>：使用注入的参数创建 OSS 客户端（<code>OSS ossClient</code>），执行上传逻辑，最终返回文件的访问 URL。</li></ol><h4 id="为什么工具类不自己读取配置？"><a href="#为什么工具类不自己读取配置？" class="headerlink" title="为什么工具类不自己读取配置？"></a>为什么工具类不自己读取配置？</h4><p>如果<code>AliOssUtil</code>自己读取配置（比如硬编码或手动解析配置文件），会导致：</p><ul><li>工具类与配置强耦合：换环境（如开发→生产）时，需要修改工具类代码；</li><li>复用性差：如果另一个模块也需要 OSS 上传，得重复写读取配置的逻辑；</li><li>测试困难：单元测试时无法方便地替换为测试环境的配置。</li></ul><h3 id="三、两个类如何-“配合工作”？"><a href="#三、两个类如何-“配合工作”？" class="headerlink" title="三、两个类如何 “配合工作”？"></a>三、两个类如何 “配合工作”？</h3><p>目前你的代码中还缺少一个 <strong>“配置类”</strong>（如<code>OssConfig</code>），它是连接<code>AliOssProperties</code>和<code>AliOssUtil</code>的 “桥梁”。完整流程如下：</p><h4 id="步骤-1：配置类创建AliOssUtil实例并注入配置"><a href="#步骤-1：配置类创建AliOssUtil实例并注入配置" class="headerlink" title="步骤 1：配置类创建AliOssUtil实例并注入配置"></a>步骤 1：配置类创建<code>AliOssUtil</code>实例并注入配置</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Configuration</span> <span class="token comment">// 标记为Spring配置类</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OssConfig</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">AliOssProperties</span> aliOssProperties<span class="token punctuation">;</span> <span class="token comment">// 注入配置容器</span>    <span class="token annotation punctuation">@Bean</span> <span class="token comment">// 将AliOssUtil注册为Spring Bean，供其他组件使用</span>    <span class="token keyword">public</span> <span class="token class-name">AliOssUtil</span> <span class="token function">aliOssUtil</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment">// 调用AliOssUtil的全参构造函数，传入配置参数</span>        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">AliOssUtil</span><span class="token punctuation">(</span>            aliOssProperties<span class="token punctuation">.</span><span class="token function">getEndpoint</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>            aliOssProperties<span class="token punctuation">.</span><span class="token function">getAccessKeyId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>            aliOssProperties<span class="token punctuation">.</span><span class="token function">getAccessKeySecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>            aliOssProperties<span class="token punctuation">.</span><span class="token function">getBucketName</span><span class="token punctuation">(</span><span class="token punctuation">)</span>        <span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="步骤-2：业务代码中直接使用AliOssUtil"><a href="#步骤-2：业务代码中直接使用AliOssUtil" class="headerlink" title="步骤 2：业务代码中直接使用AliOssUtil"></a>步骤 2：业务代码中直接使用<code>AliOssUtil</code></h4><p>当需要上传文件时，直接注入<code>AliOssUtil</code>即可，无需关心配置细节：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Service</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FileUploadService</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">AliOssUtil</span> aliOssUtil<span class="token punctuation">;</span> <span class="token comment">// 注入工具类</span>    <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">uploadFile</span><span class="token punctuation">(</span><span class="token class-name">MultipartFile</span> file<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span>        <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> file<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> fileName <span class="token operator">=</span> <span class="token constant">UUID</span><span class="token punctuation">.</span><span class="token function">randomUUID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> file<span class="token punctuation">.</span><span class="token function">getOriginalFilename</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> aliOssUtil<span class="token punctuation">.</span><span class="token function">upload</span><span class="token punctuation">(</span>bytes<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 直接调用上传方法</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="四、这种设计的核心优势"><a href="#四、这种设计的核心优势" class="headerlink" title="四、这种设计的核心优势"></a>四、这种设计的核心优势</h3><ol><li><strong>配置集中管理</strong>：所有 OSS 参数都在<code>application.yml</code>中，改配置无需动代码。</li><li><strong>业务与配置解耦</strong>：<code>AliOssUtil</code>只负责上传，不关心配置从哪来；<code>AliOssProperties</code>只负责存配置，不关心怎么用。</li><li><strong>环境隔离</strong>：开发 &#x2F; 测试 &#x2F; 生产环境的配置可以写在不同的配置文件（如<code>application-dev.yml</code>、<code>application-prod.yml</code>），通过<code>spring.profiles.active</code>切换。</li><li><strong>可测试性</strong>：单元测试时，可以手动创建<code>AliOssUtil</code>实例，传入测试用的 OSS 配置（如本地 MinIO 服务），无需依赖真实阿里云环境。</li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li><code>AliOssProperties</code>：相当于 “配置的搬运工”，把<code>application.yml</code>中的 OSS 参数搬到 Java 对象里，方便 Spring 管理。</li><li><code>AliOssUtil</code>：相当于 “实际干活的工人”，拿着<code>AliOssProperties</code>提供的 “工具（配置）” 完成文件上传。</li><li>中间通过配置类（<code>OssConfig</code>）连接，形成 “配置→容器→工具→业务” 的完整链路，这是 Spring Boot 处理第三方服务（如 OSS、Redis、数据库）的标准套路，既规范又灵活。</li></ul><hr><h3 id="dishFlavor-的dishId-循环赋值的理解"><a href="#dishFlavor-的dishId-循环赋值的理解" class="headerlink" title="dishFlavor 的dishId 循环赋值的理解"></a>dishFlavor 的dishId 循环赋值的理解</h3><p>这段代码中循环给每个<code>dishFlavor</code>设置<code>dishId</code>，核心原因是<strong>建立菜品与口味之间的关联关系</strong>，具体可以从这几个角度理解：</p><h4 id="1-业务逻辑：一个菜品对应多个口味"><a href="#1-业务逻辑：一个菜品对应多个口味" class="headerlink" title="1. 业务逻辑：一个菜品对应多个口味"></a>1. 业务逻辑：一个菜品对应多个口味</h4><p>一道菜（<code>Dish</code>）可能有多个口味（比如 “麻辣烫” 可能有 “微辣”、”中辣”、”特辣” 等口味）。这些口味需要明确 “属于哪道菜”，否则数据库中就无法区分某个口味是哪个菜品的。</p><h4 id="2-数据库设计：通过外键关联"><a href="#2-数据库设计：通过外键关联" class="headerlink" title="2. 数据库设计：通过外键关联"></a>2. 数据库设计：通过外键关联</h4><p>在数据库中，<code>dish</code>表（菜品表）和<code>dish_flavor</code>表（口味表）是<strong>一对多</strong>的关系：</p><ul><li><code>dish</code>表的主键是<code>dish_id</code>（即代码中的<code>dishId</code>）</li><li><code>dish_flavor</code>表需要一个<code>dish_id</code>字段作为<strong>外键</strong>，关联到<code>dish</code>表的主键</li></ul><p>这样设计的目的是：通过<code>dish_id</code>可以查询到某道菜的所有口味（比如 “查询麻辣烫的所有口味”）。</p><h4 id="3-代码逻辑：批量设置外键"><a href="#3-代码逻辑：批量设置外键" class="headerlink" title="3. 代码逻辑：批量设置外键"></a>3. 代码逻辑：批量设置外键</h4><p>代码中：</p><ul><li>先插入菜品信息到<code>dish</code>表，获取数据库生成的主键<code>dishId</code>（这是当前菜品的唯一标识）</li><li><code>dishDTO.getFlavors()</code>获取的是当前菜品的所有口味列表（多个<code>DishFlavor</code>对象）</li><li>循环给每个<code>DishFlavor</code>设置<code>dishId</code>，就是给每个口味 “打上所属菜品的标签”</li><li>最后批量插入到<code>dish_flavor</code>表时，所有口味就能通过<code>dishId</code>正确关联到对应的菜品</li></ul><p>举个例子：如果新增一道 “宫保鸡丁”（<code>dishId=100</code>），它有 “微辣” 和 “不辣” 两个口味。循环设置后，两个口味的<code>dishId</code>都会被设为 100，插入到数据库后，就能明确这两个口味都属于 “宫保鸡丁” 这道菜了。</p>]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/12/sky-takeout-day03-note/</id>
    <link href="https://junnhwan.github.io/2025/10/12/sky-takeout-day03-note/"/>
    <published>2025-10-12T11:57:07.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day03"><a href="#Sky-takeout-Day03" class="headerlink" title="Sky-takeout  Day03"></a>Sky-takeout  Day03</h1><hr>
<blockquote>
<p>【前言】<br>Day03 的 Task 是 <strong>公共字段自动填充</strong> 与 <strong>菜品管理相关功能</strong> 的代码开发，主要的菜品管理部分也是基础 CRUD 的练习，另外就是公共字段自动填充这种偏向技术一点的开发技巧的学习。</p>
<p>公共字段自动填充主要涉及到 枚举、注解、Spring的 AOP 、反射 的知识，用<strong>自定义注解</strong>实现了当执行 <em><strong>insert新增* 与 *update更新</strong></em>  时在Mapper层对创建人、创建时间和修改人、修改时间的字段的自动填充，避免了在Service设置属性的冗余重复代码。</p>
<p>剩余的菜品管理就是 CRUD 练习了， 只是相比前面的员工管理模块，菜品管理会涉及到菜品Dish与分类Category、套餐Setmeal的<strong>多表的联系</strong>，分析起来会更比员工管理的单表更复杂一点，要考虑的比较多，但是这部分练习能锻炼到我们对<strong>多表的业务功能的分析能力</strong>。</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day03-note</title>
    <updated>2026-07-26T03:41:24.386Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day02"><a href="#Sky-takeout-Day02" class="headerlink" title="Sky-takeout  Day02"></a>Sky-takeout  Day02</h1><blockquote><p>【前言】<br>Day02 的 Task 是实现最基础的 CRUD 功能，流程：先做需求分析，再开始编写代码，然后进行功能测试（接口文档、前后端联调）以及代码完善。<br>首先，通过员工管理模块的新增、分页查询、启用禁用账号、编辑这四大功能，来了解简单的 CRUD 如何写。<br>其次，因为菜品分类管理模块的逻辑与员工管理的几乎一样，所以就直接导入了，想巩固的话可以自己手敲一遍，加深印象。</p></blockquote><span id="more"></span><h2 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h2><ul><li>新增员工</li><li>员工分页查询</li><li>启用禁用员工账号</li><li>编辑员工</li><li>导入菜品分类管理模块代码</li></ul><hr><h2 id="1-新增员工"><a href="#1-新增员工" class="headerlink" title="1. 新增员工"></a>1. 新增员工</h2><h3 id="Q1：用DTO还是Entity实体类？"><a href="#Q1：用DTO还是Entity实体类？" class="headerlink" title="Q1：用DTO还是Entity实体类？"></a>Q1：用DTO还是Entity实体类？</h3><p><strong>一般来说</strong></p><ul><li><p>当前端提交的数据和实体类中对应的<strong>属性差别比较大</strong>时，建议使用 DTO 来封装数据</p></li><li><p><strong>Entity 管数据库，DTO 管接口</strong>。通过分层隔离，Entity 聚焦业务模型和持久化，DTO 聚焦 API 契约和传输效率</p><table><thead><tr><th>维度</th><th>Entity（实体类）</th><th>DTO（数据传输对象）</th></tr></thead><tbody><tr><td><strong>核心职责</strong></td><td>映射数据库表结构，承载业务逻辑</td><td>封装接口参数 &#x2F; 响应，实现跨层数据传输</td></tr><tr><td><strong>所属层级</strong></td><td>持久层 &#x2F; 领域层（与数据库直接交互）</td><td>应用层 &#x2F; 表现层（Controller、Service 间传输）</td></tr><tr><td><strong>数据库关联</strong></td><td>强关联（字段与表结构一一对应）</td><td>无关联（结构由 API 需求决定）</td></tr><tr><td><strong>设计重点</strong></td><td>数据完整性、业务规则（如 ORM 注解、非空约束）</td><td>API 契约稳定性、传输性能（仅含必要字段）</td></tr></tbody></table>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010153653291.png" alt="image-20251010153653291" style="zoom:50%;"></li></ul><h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>EmployeeController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**     * 保存员工信息     *     * @param employeeDTO 员工信息DTO     * @return 返回统一的Result封装对象     */</span>    <span class="token annotation punctuation">@PostMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">"保存(新增)员工信息"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">EmployeeDTO</span> employeeDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"保存(新增)员工信息：{}"</span><span class="token punctuation">,</span> employeeDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">// 调用service层的方法来处理保存逻辑</span>        employeeService<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>employeeDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 保存员工信息 * * @param employeeDTO 员工信息 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token class-name">EmployeeDTO</span> employeeDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 先将 DTO 转换为实体对象, 再进行保存, 用spring中的BeanUtils的copyProperties方法</span>    <span class="token class-name">Employee</span> employee <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Employee</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>employeeDTO<span class="token punctuation">,</span> employee<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 再将实体类的剩余属性设置好</span>    employee<span class="token punctuation">.</span><span class="token function">setStatus</span><span class="token punctuation">(</span><span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">ENABLE</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置默认状态为启用</span>    employee<span class="token punctuation">.</span><span class="token function">setPassword</span><span class="token punctuation">(</span><span class="token class-name">DigestUtils</span><span class="token punctuation">.</span><span class="token function">md5DigestAsHex</span><span class="token punctuation">(</span><span class="token string">"123456"</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置初始密码123456, 需要进行MD5加密</span>    employee<span class="token punctuation">.</span><span class="token function">setCreateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    employee<span class="token punctuation">.</span><span class="token function">setUpdateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    employee<span class="token punctuation">.</span><span class="token function">setCreateUser</span><span class="token punctuation">(</span><span class="token number">10L</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// TODO: 这里先写死，后面改为当前登录用户的id</span>    employee<span class="token punctuation">.</span><span class="token function">setUpdateUser</span><span class="token punctuation">(</span><span class="token number">10L</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// TODO: 这里先写死，后面改为当前登录用户的id</span>    <span class="token comment">// 最后调用Mapper层的方法进行保存</span>    employeeMapper<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**     * 插入员工信息     * @param employee 员工对象     */</span>    <span class="token annotation punctuation">@Insert</span><span class="token punctuation">(</span><span class="token string">"insert into employee (username, password, name, phone, sex, id_number, status, create_time, update_time, create_user, update_user)"</span> <span class="token operator">+</span>            <span class="token string">"values"</span> <span class="token operator">+</span>            <span class="token string">"(#{username}, #{password}, #{name}, #{phone}, #{sex}, #{idNumber}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})"</span><span class="token punctuation">)</span>    <span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token class-name">Employee</span> employee<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><p>首先先设置一下全局token, 另外过期时间可以设置久一点(在application.yml中jwt部分设置), 避免总是手动设置.</p><img src="/2025/10/11/sky-takeout-day02-note/../../my-note/typora/my-sky-takeout-note/day02.assets/image-20251010161839592.png" alt="image-20251010161839592" style="zoom: 25%;"><img src="/2025/10/11/sky-takeout-day02-note/image-20251010161821040.png" alt="image-20251010161821040" style="zoom:25%;"><p>测试成功,返回201<br><img src="/2025/10/11/sky-takeout-day02-note/image-20251010162002733.png" alt="image-20251010162002733" style="zoom: 25%;"></p><h3 id="代码功能完善"><a href="#代码功能完善" class="headerlink" title="代码功能完善"></a>代码功能完善</h3><p>当前存在问题:</p><ul><li>用户名有unique约束, 若重复会抛异常, 异常还没处理</li><li>新增员工时的登录用户id还是定死的</li></ul><img src="/2025/10/11/sky-takeout-day02-note/image-20251010162153307.png" alt="image-20251010162153307" style="zoom:25%;"><h4 id="解决用户名重复异常的问题"><a href="#解决用户名重复异常的问题" class="headerlink" title="解决用户名重复异常的问题"></a>解决用户名重复异常的问题</h4><p><img src="/2025/10/11/sky-takeout-day02-note/image-20251010163541987.png" alt="image-20251010163541987"></p><p>要将抛出的 <strong>SQLIntegrityConstraintViolationException</strong> 异常,加入全局异常处理器中, 返回异常消息给前端, 保证友好性.<br>在GlobalExceptionHandler中添加: </p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 捕获SQL异常 * @param ex 捕获到的异常对象 * @return 返回封装的异常信息 */</span><span class="token annotation punctuation">@ExceptionHandler</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">exceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">SQLIntegrityConstraintViolationException</span> ex<span class="token punctuation">)</span><span class="token punctuation">{</span>    <span class="token comment">// Duplicate entry 'hwan1' for key 'employee.idx_username';</span>    <span class="token class-name">String</span> message <span class="token operator">=</span> ex<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span><span class="token punctuation">(</span>message<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string">"Duplicate entry"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>        <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> split <span class="token operator">=</span> message<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> username <span class="token operator">=</span> split<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>        <span class="token class-name">String</span> msg <span class="token operator">=</span> username <span class="token operator">+</span> <span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">ALREADY_EXIST</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">UNKNOWN_ERROR</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="解决设置当前用户id的问题"><a href="#解决设置当前用户id的问题" class="headerlink" title="解决设置当前用户id的问题"></a>解决设置当前用户id的问题</h4><ul><li><p>JWT 工作流程图</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010164604620.png" alt="image-20251010164604620" style="zoom: 50%;"></li><li><p>通过Jwt令牌中携带的用户id来<strong>获取</strong>新增员工时需要设置的当前用户属性</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010164716025.png" alt="image-20251010164716025" style="zoom:50%;"></li></ul><h4 id="Q2-如何传递拦截器获取的用户id给service"><a href="#Q2-如何传递拦截器获取的用户id给service" class="headerlink" title="Q2: 如何传递拦截器获取的用户id给service?"></a>Q2: 如何传递拦截器获取的用户id给service?</h4><ul><li><p>解析出登录员工 id 后，如何<strong>传递</strong>给 Service 的 save 方法？</p><p>  ​     &#x3D;&gt;使用<strong>ThreadLocal</strong></p><p>  <strong>介绍：</strong></p><p>  ThreadLocal 并不是一个 Thread，而是 Thread 的局部变量。<br>  ThreadLocal 为每个线程提供单独一份存储空间，具有线程隔离的效果，只有在线程内才能获取到对应的值，线程外则不能访问。</p><p>  <strong>常用方法：</strong></p><ul><li><p>public void set(T value)     设置当前线程的线程局部变量的值</p></li><li><p>public T get()         返回当前线程所对应的线程局部变量的值</p></li><li><p>public void remove()        移除当前线程的线程局部变量</p></li></ul></li><li><p>项目中在<strong>BaseContext工具类</strong>已经封装了对应方法</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010165529633.png" alt="image-20251010165529633" style="zoom: 67%;"></li><li><p>解决流程图</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010165222324.png" alt="image-20251010165222324" style="zoom:50%;"></li><li><p>在拦截器Interceptor, 将用户id存入ThreadLocal中</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010170118979.png" alt="image-20251010170118979" style="zoom: 25%;"></li><li><p>在Service里, 从ThreadLocal中获取当前登录用户的id</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251010170259654.png" alt="image-20251010170259654" style="zoom: 25%;"></li></ul><h2 id="2-员工分页查询"><a href="#2-员工分页查询" class="headerlink" title="2. 员工分页查询"></a>2. 员工分页查询</h2><h3 id="接口分析"><a href="#接口分析" class="headerlink" title="接口分析"></a>接口分析</h3><p><strong>接口设计</strong><br><img src="/2025/10/11/sky-takeout-day02-note/image-20251010184016854.png" alt="image-20251010184016854" style="zoom: 50%;"></p><p><strong>注意事项：</strong></p><ul><li>请求参数类型为 Query，不是 json 格式提交，在路径后直接拼接。&#x2F;admin&#x2F;employee&#x2F;page?name &#x3D; zhangsan</li><li>返回数据中 records 数组中使用 Employee 实体类对属性进行封装。</li></ul><h3 id="代码编写-1"><a href="#代码编写-1" class="headerlink" title="代码编写"></a>代码编写</h3><p>用到了<strong>分页插件PageHelper</strong></p><blockquote><p><strong>PageHelper 基本原理</strong></p><p>PageHelper 是基于 MyBatis 的分页插件，通过<strong>拦截 SQL 执行过程</strong>动态添加分页逻辑（如 MySQL 的 <code>LIMIT</code>），并自动统计总记录数。其核心优势是：</p><ul><li>无需修改 SQL，只需在代码中添加分页参数；</li><li>支持多数据库（MySQL、Oracle 等）；</li><li>通过 <code>ThreadLocal</code> 存储分页参数，保证线程安全。</li></ul></blockquote><p>定义<strong>分页查询DTO</strong>和<strong>分页结果封装</strong></p><img src="/2025/10/11/sky-takeout-day02-note/image-20251010195314831.png" alt="image-20251010195314831" style="zoom: 50%;"><p><strong>EmployeeController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 员工分页查询 * * @param employeePageQueryDTO 员工分页查询DTO * @return 返回统一的Result封装对象 */</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"员工分页查询"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/page"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">PageResult</span><span class="token punctuation">></span></span> <span class="token function">page</span><span class="token punctuation">(</span><span class="token class-name">EmployeePageQueryDTO</span> employeePageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"员工分页查询：参数{}"</span><span class="token punctuation">,</span> employeePageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">PageResult</span> pageResult <span class="token operator">=</span> employeeService<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>employeePageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>pageResult<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 员工分页查询 * * @param employeePageQueryDTO 分页查询信息 * @return 分页结果 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">PageResult</span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">EmployeePageQueryDTO</span> employeePageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">PageHelper</span><span class="token punctuation">.</span><span class="token function">startPage</span><span class="token punctuation">(</span>employeePageQueryDTO<span class="token punctuation">.</span><span class="token function">getPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> employeePageQueryDTO<span class="token punctuation">.</span><span class="token function">getPageSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Employee</span><span class="token punctuation">></span></span> page <span class="token operator">=</span> employeeMapper<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>employeePageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PageResult</span><span class="token punctuation">(</span>page<span class="token punctuation">.</span><span class="token function">getTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span><span class="token function">getResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeMapper.xml</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token operator">&lt;</span>select id<span class="token operator">=</span><span class="token string">"pageQuery"</span> resultType<span class="token operator">=</span><span class="token string">"com.sky.entity.Employee"</span><span class="token operator">></span>    select <span class="token operator">*</span> from employee    <span class="token generics"><span class="token punctuation">&lt;</span>where<span class="token punctuation">></span></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"name != null and name != ''"</span><span class="token operator">></span>            and name like <span class="token function">concat</span><span class="token punctuation">(</span><span class="token char">'%'</span><span class="token punctuation">,</span> #<span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token char">'%'</span><span class="token punctuation">)</span>        <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>    <span class="token operator">&lt;</span><span class="token operator">/</span>where<span class="token operator">></span>    order by update_time desc<span class="token operator">&lt;</span><span class="token operator">/</span>select<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="解决分页查询时间格式问题"><a href="#解决分页查询时间格式问题" class="headerlink" title="解决分页查询时间格式问题"></a>解决分页查询时间格式问题</h3><p>调试时发现时间格式有问题：<br><img src="/2025/10/11/sky-takeout-day02-note/image-20251010191630040.png" alt="image-20251010191630040" style="zoom:50%;"></p><p>有两种解决方案</p><ul><li><p>方式一：在属性加上注解，可行，但是需要在每个时间属性上都要加上该注解，使用较麻烦，不能全局处理。</p></li><li><p>方式二：在 WebMvcConfiguration 中扩展 SpringMVC 的消息转换器，统一对日期类型进行格式处理。</p></li></ul><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">extendMessageConverters</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">HttpMessageConverter</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> converters<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"扩展消息转换器..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 创建消息转换器对象</span>    <span class="token class-name">MappingJackson2HttpMessageConverter</span> converter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MappingJackson2HttpMessageConverter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 设置对象转换器，底层使用Jackson将Java对象转为json</span>    converter<span class="token punctuation">.</span><span class="token function">setObjectMapper</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">JacksonObjectMapper</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 将上面的消息转换器对象追加到mvc框架的转换器集合中</span>    <span class="token comment">// 这里前面的 0 是优先级，设置在最前面，优先使用我们定义的转换器</span>    converters<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> converter<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>JacksonObjectMapper</strong>中定义了<strong>时间格式</strong>：<br><img src="/2025/10/11/sky-takeout-day02-note/image-20251010194037109.png" alt="image-20251010194037109" style="zoom:33%;"></p><h2 id="3-启用禁用员工账号"><a href="#3-启用禁用员工账号" class="headerlink" title="3. 启用禁用员工账号"></a>3. 启用禁用员工账号</h2><h3 id="接口设计"><a href="#接口设计" class="headerlink" title="接口设计"></a>接口设计</h3><img src="/2025/10/11/sky-takeout-day02-note/image-20251010203227868.png" alt="image-20251010203227868" style="zoom: 50%;"><p>1). 路径参数携带状态值。</p><p>2). 同时，把 id 传递过去，明确对哪个用户进行操作。</p><p>3). 返回数据 code 状态是必须，其它是非必须。</p><h3 id="代码编写-2"><a href="#代码编写-2" class="headerlink" title="代码编写"></a>代码编写</h3><p><strong>EmployeeController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 启用禁用员工账号 * * @param status 状态 * @param id     员工id * @return 返回统一的Result封装对象 */</span><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/status/{status}"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"启用禁用员工账号"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"启用禁用员工账号：status={}, id={}"</span><span class="token punctuation">,</span> status<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    employeeService<span class="token punctuation">.</span><span class="token function">startOrStop</span><span class="token punctuation">(</span>status<span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 启用或禁用员工账号 * * @param status 状态 * @param id     员工id */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">Employee</span> employee <span class="token operator">=</span> <span class="token class-name">Employee</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span>status<span class="token punctuation">)</span>            <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    employeeMapper<span class="token punctuation">.</span><span class="token function">updateById</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeMapper.xml</strong></p><p>根据传入的<code>Employee</code>对象中的非空字段，选择性地更新<code>employee</code>表中的对应记录。它的核心特点是<strong>只更新需要修改的字段</strong>，避免不必要的数据库操作。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token operator">&lt;</span>update id<span class="token operator">=</span><span class="token string">"updateById"</span> parameterType<span class="token operator">=</span><span class="token string">"employee"</span><span class="token operator">></span>    update employee    <span class="token generics"><span class="token punctuation">&lt;</span>set<span class="token punctuation">></span></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"name != null"</span><span class="token operator">></span>name <span class="token operator">=</span> #<span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"username != null"</span><span class="token operator">></span>username <span class="token operator">=</span> #<span class="token punctuation">{</span>username<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"password != null"</span><span class="token operator">></span>password <span class="token operator">=</span> #<span class="token punctuation">{</span>password<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"phone != null"</span><span class="token operator">></span>phone <span class="token operator">=</span> #<span class="token punctuation">{</span>phone<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"sex != null"</span><span class="token operator">></span>sex <span class="token operator">=</span> #<span class="token punctuation">{</span>sex<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"idNumber != null"</span><span class="token operator">></span>id_Number <span class="token operator">=</span> #<span class="token punctuation">{</span>idNumber<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"updateTime != null"</span><span class="token operator">></span>update_Time <span class="token operator">=</span> #<span class="token punctuation">{</span>updateTime<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"updateUser != null"</span><span class="token operator">></span>update_User <span class="token operator">=</span> #<span class="token punctuation">{</span>updateUser<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"status != null"</span><span class="token operator">></span>status <span class="token operator">=</span> #<span class="token punctuation">{</span>status<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>    <span class="token operator">&lt;</span><span class="token operator">/</span>set<span class="token operator">></span>    where id <span class="token operator">=</span> #<span class="token punctuation">{</span>id<span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>update<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>这里这个功能的实现虽然只需要修改状态值，但是可以把其他需要根据主键动态修改的属性都加上，方便后续复用</p></blockquote><h2 id="4-编辑员工"><a href="#4-编辑员工" class="headerlink" title="4. 编辑员工"></a>4. 编辑员工</h2><h3 id="接口设计-1"><a href="#接口设计-1" class="headerlink" title="接口设计"></a>接口设计</h3><p><strong>编辑员工的页面原型</strong>：</p><p>在员工管理列表页面点击 “编辑” 按钮，跳转到编辑页面，在编辑页面回显员工信息并进行修改，最后点击 “保存” 按钮完成编辑操作。</p><p>注：点击修改时，数据应该正常回显到修改页面。<br><img src="/2025/10/11/sky-takeout-day02-note/image-20251011192703614.png" alt="image-20251011192703614" style="zoom:50%;"></p><p>根据上述原型图分析，编辑员工功能涉及到两个接口：</p><ul><li>根据 id 查询员工信息</li><li>编辑员工信息</li></ul><p>设计接口如下：<br><img src="/2025/10/11/sky-takeout-day02-note/../../my-note/typora/my-sky-takeout-note/day02.assets/image-20251011192924595.png" alt="image-20251011192924595" style="zoom:50%;"><br><img src="/2025/10/11/sky-takeout-day02-note/image-20251011192944602.png" alt="image-20251011192944602" style="zoom:50%;"></p><h3 id="代码编写-3"><a href="#代码编写-3" class="headerlink" title="代码编写"></a>代码编写</h3><p>以下代码包含了：根据id查询员工信息回显给前端、修改员工信息</p><ul><li>（在实现 <strong>启用禁用员工账号</strong> 功能时，已实现 employeeMapper.update(employee)，在此不需写 Mapper 层代码。）</li></ul><p><strong>EmployeeController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询员工信息 * * @param id 员工id * @return 返回统一的Result封装对象 */</span><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"根据id查询员工信息"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Employee</span><span class="token punctuation">></span></span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"根据id查询员工信息：id={}"</span><span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">Employee</span> employee <span class="token operator">=</span> employeeService<span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">/** * 修改员工信息 * * @param employeeDTO 员工信息DTO * @return 返回统一的Result封装对象 */</span><span class="token annotation punctuation">@PutMapping</span><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"修改员工信息"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">EmployeeDTO</span> employeeDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"修改员工信息：{}"</span><span class="token punctuation">,</span> employeeDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    employeeService<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>employeeDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询员工信息 * * @param id 员工id * @return 员工信息 */</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token class-name">Employee</span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token class-name">Employee</span> employee <span class="token operator">=</span> employeeMapper<span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>employee <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 如果没有查询到员工信息, 则抛出异常</span>        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AccountNotFoundException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">ACCOUNT_NOT_FOUND</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token keyword">return</span> employee<span class="token punctuation">;</span> <span class="token comment">// 返回员工信息</span><span class="token punctuation">}</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">EmployeeDTO</span> employeeDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>    <span class="token comment">// 传来的是DTO对象， 要将 DTO 转换为实体对象</span>    <span class="token class-name">Employee</span> employee <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Employee</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>employeeDTO<span class="token punctuation">,</span> employee<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 设置修改相关属性，修改时间和修改人</span>    employee<span class="token punctuation">.</span><span class="token function">setUpdateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    employee<span class="token punctuation">.</span><span class="token function">setUpdateUser</span><span class="token punctuation">(</span><span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    employeeMapper<span class="token punctuation">.</span><span class="token function">updateById</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 更新员工信息 (前面在写启用禁用员工账号时，在Mapper中已经配置了根据id动态更新)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>EmployeeMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** * 根据id查询员工信息 * @param id 员工id * @return 员工对象 */</span><span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select * from employee where id = #{id}"</span><span class="token punctuation">)</span><span class="token class-name">Employee</span> <span class="token function">getById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="功能测试-1"><a href="#功能测试-1" class="headerlink" title="功能测试"></a>功能测试</h3><ul><li><p>1）根据id查询员工并回显信息：返回员工信息json数据</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251011194957644.png" alt="image-20251011194957644" style="zoom: 25%;"></li><li><p>2）修改信息</p>  <img src="/2025/10/11/sky-takeout-day02-note/image-20251011200650746.png" alt="image-20251011200650746" style="zoom: 33%;"></li></ul><h2 id="5-导入菜品分类管理模块"><a href="#5-导入菜品分类管理模块" class="headerlink" title="5. 导入菜品分类管理模块"></a>5. 导入菜品分类管理模块</h2><h3 id="产品原型分析"><a href="#产品原型分析" class="headerlink" title="产品原型分析"></a>产品原型分析</h3><p>后台系统中可以管理<strong>分类信息</strong>，分类包括<strong>两种类型</strong>，分别是 <strong>菜品分类</strong> 和 <strong>套餐分类</strong> 。</p><p>下面以分析 <strong>菜品分类</strong> 相关功能来举例， 套餐的类似。</p><ul><li><p><strong>新增菜品分类：</strong> 当我们在后台系统中添加菜品时需要选择一个菜品分类，在移动端也会按照菜品分类来展示对应的菜品。</p></li><li><p><strong>菜品分类分页查询：</strong> 系统中的分类很多的时候，如果在一个页面中全部展示出来会显得比较乱，不便于查看，所以一般的系统中都会以分页的方式来展示列表数据。</p></li><li><p><strong>根据 id 删除菜品分类：</strong> 在分类管理列表页面，可以对某个分类进行删除操作。需要注意的是***<u>当分类关联了菜品或者套餐时，此分类不允许删除。</u>***</p></li><li><p><strong>修改菜品分类：</strong> 在分类管理列表页面点击修改按钮，弹出修改窗口，在修改窗口回显分类信息并进行修改，最后点击确定按钮完成修改操作。</p></li><li><p><strong>启用禁用菜品分类：</strong> 在分类管理列表页面，可以对某个分类进行启用或者禁用操作。</p></li><li><p><strong>分类类型查询：</strong> 当点击分类类型下拉框时，从数据库中查询所有的菜品分类数据进行展示。</p></li></ul><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ul><li>导入时如果想避免看到一堆红的报错，就按照Mapper &#x3D;》Servie &#x3D;》 Controller的顺序来导入；</li><li>因为在删除分类等分类相关业务时，会设计到 Setmeal 和 Dish ，要判断分类地下是否还挂有菜品和套餐，所以这里也需要导入SetmealMapper和DishMapper</li><li>其次，因为这些代码是我们直接拷贝进来的，可能不会自动编译，保险起见最好在 maven 那里 compile 手动编译一下，防止报错</li><li><strong>业务规则：</strong><ul><li>分类名称必须是唯一的</li><li>分类按照类型可以分为菜品分类和套餐分类</li><li>新添加的分类状态默认为“禁用”，<ul><li>为什么？&#x3D;》如果默认是启用的话，新增分类时，该分类下是空的，在用户端显示是空的，没用意义，为了避免这种情况，直接把默认状态设为禁用即可）</li></ul></li></ul></li></ul><h3 id="代码导入"><a href="#代码导入" class="headerlink" title="代码导入"></a>代码导入</h3><p><strong>CategoryController.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>controller<span class="token punctuation">.</span>admin</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">CategoryDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">CategoryPageQueryDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Category</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">PageResult</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">Result</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">CategoryService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Api</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">io<span class="token punctuation">.</span>swagger<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">ApiOperation</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token operator">*</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token comment">/** * 分类管理 */</span><span class="token annotation punctuation">@RestController</span><span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/admin/category"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Api</span><span class="token punctuation">(</span>tags <span class="token operator">=</span> <span class="token string">"分类相关接口"</span><span class="token punctuation">)</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CategoryController</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">CategoryService</span> categoryService<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增分类     * @param categoryDTO 分类信息     * @return 操作结果     */</span>    <span class="token annotation punctuation">@PostMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"新增分类"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">CategoryDTO</span> categoryDTO<span class="token punctuation">)</span><span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"新增分类：{}"</span><span class="token punctuation">,</span> categoryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        categoryService<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>categoryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 分类分页查询     * @param categoryPageQueryDTO  分页查询参数     * @return 分页查询结果     */</span>    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/page"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"分类分页查询"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">PageResult</span><span class="token punctuation">></span></span> <span class="token function">page</span><span class="token punctuation">(</span><span class="token class-name">CategoryPageQueryDTO</span> categoryPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"分页查询：{}"</span><span class="token punctuation">,</span> categoryPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">PageResult</span> pageResult <span class="token operator">=</span> categoryService<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>categoryPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>pageResult<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 删除分类     * @param id 分类id     * @return 操作结果     */</span>    <span class="token annotation punctuation">@DeleteMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"删除分类"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">deleteById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">{</span>        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"删除分类：{}"</span><span class="token punctuation">,</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>        categoryService<span class="token punctuation">.</span><span class="token function">deleteById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 修改分类     * @param categoryDTO 分类信息     * @return 操作结果     */</span>    <span class="token annotation punctuation">@PutMapping</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"修改分类"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">CategoryDTO</span> categoryDTO<span class="token punctuation">)</span><span class="token punctuation">{</span>        categoryService<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>categoryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 启用、禁用分类     * @param status 0-禁用，1-启用     * @param id 分类id     * @return 操作结果     */</span>    <span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/status/{status}"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"启用禁用分类"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span><span class="token punctuation">(</span><span class="token string">"status"</span><span class="token punctuation">)</span> <span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">{</span>        categoryService<span class="token punctuation">.</span><span class="token function">startOrStop</span><span class="token punctuation">(</span>status<span class="token punctuation">,</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 根据类型查询分类     * @param type 分类类型     * @return 分类列表     */</span>    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/list"</span><span class="token punctuation">)</span>    <span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span><span class="token string">"根据类型查询分类"</span><span class="token punctuation">)</span>    <span class="token keyword">public</span> <span class="token class-name">Result</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">List</span><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> type<span class="token punctuation">)</span><span class="token punctuation">{</span>        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span></span> list <span class="token operator">=</span> categoryService<span class="token punctuation">.</span><span class="token function">list</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>CategoryServiceImpl.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span>impl</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>github<span class="token punctuation">.</span>pagehelper<span class="token punctuation">.</span></span><span class="token class-name">Page</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>github<span class="token punctuation">.</span>pagehelper<span class="token punctuation">.</span></span><span class="token class-name">PageHelper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">MessageConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>constant<span class="token punctuation">.</span></span><span class="token class-name">StatusConstant</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">BaseContext</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">CategoryDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">CategoryPageQueryDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Category</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>exception<span class="token punctuation">.</span></span><span class="token class-name">DeletionNotAllowedException</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">CategoryMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">DishMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper<span class="token punctuation">.</span></span><span class="token class-name">SetmealMapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">PageResult</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>service<span class="token punctuation">.</span></span><span class="token class-name">CategoryService</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">lombok<span class="token punctuation">.</span>extern<span class="token punctuation">.</span>slf4j<span class="token punctuation">.</span></span><span class="token class-name">Slf4j</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span></span><span class="token class-name">BeanUtils</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Service</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>time<span class="token punctuation">.</span></span><span class="token class-name">LocalDateTime</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token comment">/** * 分类业务层 */</span><span class="token annotation punctuation">@Service</span><span class="token annotation punctuation">@Slf4j</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CategoryServiceImpl</span> <span class="token keyword">implements</span> <span class="token class-name">CategoryService</span> <span class="token punctuation">{</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">CategoryMapper</span> categoryMapper<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">DishMapper</span> dishMapper<span class="token punctuation">;</span>    <span class="token annotation punctuation">@Autowired</span>    <span class="token keyword">private</span> <span class="token class-name">SetmealMapper</span> setmealMapper<span class="token punctuation">;</span>    <span class="token comment">/**     * 新增分类     * @param categoryDTO 分类信息     */</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token class-name">CategoryDTO</span> categoryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Category</span> category <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Category</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//属性拷贝</span>        <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>categoryDTO<span class="token punctuation">,</span> category<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//分类状态默认为禁用状态0</span>        category<span class="token punctuation">.</span><span class="token function">setStatus</span><span class="token punctuation">(</span><span class="token class-name">StatusConstant</span><span class="token punctuation">.</span><span class="token constant">DISABLE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//设置创建时间、修改时间、创建人、修改人</span>        category<span class="token punctuation">.</span><span class="token function">setCreateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        category<span class="token punctuation">.</span><span class="token function">setUpdateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        category<span class="token punctuation">.</span><span class="token function">setCreateUser</span><span class="token punctuation">(</span><span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        category<span class="token punctuation">.</span><span class="token function">setUpdateUser</span><span class="token punctuation">(</span><span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        categoryMapper<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>category<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 分页查询     * @param categoryPageQueryDTO 分页查询参数     * @return 分页结果     */</span>    <span class="token keyword">public</span> <span class="token class-name">PageResult</span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">CategoryPageQueryDTO</span> categoryPageQueryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">PageHelper</span><span class="token punctuation">.</span><span class="token function">startPage</span><span class="token punctuation">(</span>categoryPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>categoryPageQueryDTO<span class="token punctuation">.</span><span class="token function">getPageSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//下一条sql进行分页，自动加入limit关键字分页</span>        <span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span></span> page <span class="token operator">=</span> categoryMapper<span class="token punctuation">.</span><span class="token function">pageQuery</span><span class="token punctuation">(</span>categoryPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">PageResult</span><span class="token punctuation">(</span>page<span class="token punctuation">.</span><span class="token function">getTotal</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> page<span class="token punctuation">.</span><span class="token function">getResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 根据id删除分类     * @param id 分类id     */</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token comment">//查询当前分类是否关联了菜品，如果关联了就抛出业务异常</span>        <span class="token class-name">Integer</span> count <span class="token operator">=</span> dishMapper<span class="token punctuation">.</span><span class="token function">countByCategoryId</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>count <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span>            <span class="token comment">//当前分类下有菜品，不能删除</span>            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DeletionNotAllowedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">CATEGORY_BE_RELATED_BY_DISH</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token comment">//查询当前分类是否关联了套餐，如果关联了就抛出业务异常</span>        count <span class="token operator">=</span> setmealMapper<span class="token punctuation">.</span><span class="token function">countByCategoryId</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>count <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span>            <span class="token comment">//当前分类下有菜品，不能删除</span>            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">DeletionNotAllowedException</span><span class="token punctuation">(</span><span class="token class-name">MessageConstant</span><span class="token punctuation">.</span><span class="token constant">CATEGORY_BE_RELATED_BY_SETMEAL</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">}</span>        <span class="token comment">//删除分类数据</span>        categoryMapper<span class="token punctuation">.</span><span class="token function">deleteById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 修改分类     * @param categoryDTO 分类信息     */</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">CategoryDTO</span> categoryDTO<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Category</span> category <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Category</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token class-name">BeanUtils</span><span class="token punctuation">.</span><span class="token function">copyProperties</span><span class="token punctuation">(</span>categoryDTO<span class="token punctuation">,</span>category<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token comment">//设置修改时间、修改人</span>        category<span class="token punctuation">.</span><span class="token function">setUpdateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        category<span class="token punctuation">.</span><span class="token function">setUpdateUser</span><span class="token punctuation">(</span><span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        categoryMapper<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>category<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 启用、禁用分类     * @param status 分类状态     * @param id 分类id     */</span>    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">startOrStop</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> status<span class="token punctuation">,</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token class-name">Category</span> category <span class="token operator">=</span> <span class="token class-name">Category</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span>status<span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">updateTime</span><span class="token punctuation">(</span><span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">updateUser</span><span class="token punctuation">(</span><span class="token class-name">BaseContext</span><span class="token punctuation">.</span><span class="token function">getCurrentId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        categoryMapper<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>category<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span>    <span class="token comment">/**     * 根据类型查询分类     * @param type 分类类型     * @return 分类列表     */</span>    <span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> type<span class="token punctuation">)</span> <span class="token punctuation">{</span>        <span class="token keyword">return</span> categoryMapper<span class="token punctuation">.</span><span class="token function">list</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>CategoryMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>github<span class="token punctuation">.</span>pagehelper<span class="token punctuation">.</span></span><span class="token class-name">Page</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>dto<span class="token punctuation">.</span></span><span class="token class-name">CategoryPageQueryDTO</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>entity<span class="token punctuation">.</span></span><span class="token class-name">Category</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Delete</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Insert</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">CategoryMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 插入数据     * @param category 分类     */</span>    <span class="token annotation punctuation">@Insert</span><span class="token punctuation">(</span><span class="token string">"insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)"</span> <span class="token operator">+</span>            <span class="token string">" VALUES"</span> <span class="token operator">+</span>            <span class="token string">" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})"</span><span class="token punctuation">)</span>    <span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token class-name">Category</span> category<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 分页查询     * @param categoryPageQueryDTO 分页查询参数     * @return 分页结果     */</span>    <span class="token class-name">Page</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span></span> <span class="token function">pageQuery</span><span class="token punctuation">(</span><span class="token class-name">CategoryPageQueryDTO</span> categoryPageQueryDTO<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 根据id删除分类     * @param id 分类id     */</span>    <span class="token annotation punctuation">@Delete</span><span class="token punctuation">(</span><span class="token string">"delete from category where id = #{id}"</span><span class="token punctuation">)</span>    <span class="token keyword">void</span> <span class="token function">deleteById</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 根据id修改分类     * @param category 分类     */</span>    <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token class-name">Category</span> category<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/**     * 根据类型查询分类     * @param type 分类类型     * @return 分类列表     */</span>    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Category</span><span class="token punctuation">></span></span> <span class="token function">list</span><span class="token punctuation">(</span><span class="token class-name">Integer</span> type<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>DishMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Select</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">DishMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 根据分类id查询菜品数量     * @param categoryId 分类Id     * @return 数量     */</span>    <span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select count(id) from dish where category_id = #{categoryId}"</span><span class="token punctuation">)</span>    <span class="token class-name">Integer</span> <span class="token function">countByCategoryId</span><span class="token punctuation">(</span><span class="token class-name">Long</span> categoryId<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>SetmealMapper.java</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">com<span class="token punctuation">.</span>sky<span class="token punctuation">.</span>mapper</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Mapper</span></span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>apache<span class="token punctuation">.</span>ibatis<span class="token punctuation">.</span>annotations<span class="token punctuation">.</span></span><span class="token class-name">Select</span></span><span class="token punctuation">;</span><span class="token annotation punctuation">@Mapper</span><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">SetmealMapper</span> <span class="token punctuation">{</span>    <span class="token comment">/**     * 根据分类id查询套餐的数量     * @param id 分类Id     * @return 数量     */</span>    <span class="token annotation punctuation">@Select</span><span class="token punctuation">(</span><span class="token string">"select count(id) from setmeal where category_id = #{categoryId}"</span><span class="token punctuation">)</span>    <span class="token class-name">Integer</span> <span class="token function">countByCategoryId</span><span class="token punctuation">(</span><span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>CategoryMapper.xml</strong></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token operator">&lt;</span><span class="token operator">?</span>xml version<span class="token operator">=</span><span class="token string">"1.0"</span> encoding<span class="token operator">=</span><span class="token string">"UTF-8"</span> <span class="token operator">?</span><span class="token operator">></span><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token constant">DOCTYPE</span> mapper <span class="token constant">PUBLIC</span> <span class="token string">"-//mybatis.org//DTD Mapper 3.0//EN"</span>        <span class="token string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span> <span class="token operator">></span><span class="token operator">&lt;</span>mapper namespace<span class="token operator">=</span><span class="token string">"com.sky.mapper.CategoryMapper"</span><span class="token operator">></span>    <span class="token operator">&lt;</span>select id<span class="token operator">=</span><span class="token string">"pageQuery"</span> resultType<span class="token operator">=</span><span class="token string">"com.sky.entity.Category"</span><span class="token operator">></span>        select <span class="token operator">*</span> from category        <span class="token generics"><span class="token punctuation">&lt;</span>where<span class="token punctuation">></span></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"name != null and name != ''"</span><span class="token operator">></span>                and name like <span class="token function">concat</span><span class="token punctuation">(</span><span class="token char">'%'</span><span class="token punctuation">,</span>#<span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token char">'%'</span><span class="token punctuation">)</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"type != null"</span><span class="token operator">></span>                and type <span class="token operator">=</span> #<span class="token punctuation">{</span>type<span class="token punctuation">}</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token operator">/</span>where<span class="token operator">></span>        order by sort asc <span class="token punctuation">,</span> create_time desc    <span class="token operator">&lt;</span><span class="token operator">/</span>select<span class="token operator">></span>    <span class="token operator">&lt;</span>update id<span class="token operator">=</span><span class="token string">"update"</span> parameterType<span class="token operator">=</span><span class="token string">"Category"</span><span class="token operator">></span>        update category        <span class="token generics"><span class="token punctuation">&lt;</span>set<span class="token punctuation">></span></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"type != null"</span><span class="token operator">></span>                type <span class="token operator">=</span> #<span class="token punctuation">{</span>type<span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"name != null"</span><span class="token operator">></span>                name <span class="token operator">=</span> #<span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"sort != null"</span><span class="token operator">></span>                sort <span class="token operator">=</span> #<span class="token punctuation">{</span>sort<span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"status != null"</span><span class="token operator">></span>                status <span class="token operator">=</span> #<span class="token punctuation">{</span>status<span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"updateTime != null"</span><span class="token operator">></span>                update_time <span class="token operator">=</span> #<span class="token punctuation">{</span>updateTime<span class="token punctuation">}</span><span class="token punctuation">,</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>            <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"updateUser != null"</span><span class="token operator">></span>                update_user <span class="token operator">=</span> #<span class="token punctuation">{</span>updateUser<span class="token punctuation">}</span>            <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        <span class="token operator">&lt;</span><span class="token operator">/</span>set<span class="token operator">></span>        where id <span class="token operator">=</span> #<span class="token punctuation">{</span>id<span class="token punctuation">}</span>    <span class="token operator">&lt;</span><span class="token operator">/</span>update<span class="token operator">></span>    <span class="token operator">&lt;</span>select id<span class="token operator">=</span><span class="token string">"list"</span> resultType<span class="token operator">=</span><span class="token string">"Category"</span><span class="token operator">></span>        select <span class="token operator">*</span> from category        where status <span class="token operator">=</span> <span class="token number">1</span>        <span class="token operator">&lt;</span><span class="token keyword">if</span> test<span class="token operator">=</span><span class="token string">"type != null"</span><span class="token operator">></span>            and type <span class="token operator">=</span> #<span class="token punctuation">{</span>type<span class="token punctuation">}</span>        <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token operator">></span>        order by sort <span class="token punctuation">,</span>create_time desc    <span class="token operator">&lt;</span><span class="token operator">/</span>select<span class="token operator">></span><span class="token operator">&lt;</span><span class="token operator">/</span>mapper<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="附：其他盲区知识补充"><a href="#附：其他盲区知识补充" class="headerlink" title="附：其他盲区知识补充"></a>附：其他盲区知识补充</h2><p>在 Spring MVC 中，<code>@RequestBody</code>注解的核心作用是<strong>将 HTTP 请求体中的数据绑定到控制器方法的参数上</strong>。是否需要添加该注解，主要取决于<strong>参数的数据来源</strong>和<strong>请求的内容格式</strong>。</p><h3 id="一、需要添加-RequestBody的场景"><a href="#一、需要添加-RequestBody的场景" class="headerlink" title="一、需要添加@RequestBody的场景"></a>一、需要添加<code>@RequestBody</code>的场景</h3><p>当参数的数据来自<strong>HTTP 请求体（Request Body）</strong>，且请求体格式为<code>application/json</code>、<code>application/xml</code>等非表单格式时，必须使用<code>@RequestBody</code>。</p><p>典型场景：</p><ol><li><p><strong>前端发送 JSON 格式数据</strong>当前端通过<code>POST</code>、<code>PUT</code>等请求，以<code>JSON</code>格式提交数据（<code>Content-Type: application/json</code>）时，后端需要用<code>@RequestBody</code>接收并将 JSON 转换为 Java 对象。</p><p> 示例：前端请求（JSON 格式）：</p> <pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token comment">// 请求体内容</span><span class="token punctuation">{</span>  <span class="token property">"username"</span><span class="token operator">:</span> <span class="token string">"zhangsan"</span><span class="token punctuation">,</span>  <span class="token property">"password"</span><span class="token operator">:</span> <span class="token string">"123456"</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p> 后端 Controller 接收：</p> <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/login"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">login</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">User</span> user<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 需要@RequestBody将JSON转换为User对象</span>    <span class="token comment">// 处理登录逻辑</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li><li><p><strong>接收复杂对象或数组</strong>当参数是复杂 Java 对象（包含多个字段）、集合（如<code>List</code>、<code>Map</code>）或数组时，即使请求体是其他格式（如 XML），也需要<code>@RequestBody</code>绑定数据。</p><p> 示例（接收数组）：</p> <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/batch-delete"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">batchDelete</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">></span></span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 接收JSON数组[1,2,3]</span>    <span class="token comment">// 批量删除逻辑</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li></ol><h3 id="二、不需要添加-RequestBody的场景"><a href="#二、不需要添加-RequestBody的场景" class="headerlink" title="二、不需要添加@RequestBody的场景"></a>二、不需要添加<code>@RequestBody</code>的场景</h3><p>当参数的数据来自<strong>URL 路径（PathVariable）</strong>、<strong>查询参数（Query String）</strong> 或<strong>表单提交（Form Data）</strong> 时，不需要<code>@RequestBody</code>。</p><p>典型场景：</p><ol><li><p>**参数来自 URL 路径（<code>@PathVariable</code>）**参数嵌在 URL 中（如<code>/user/123</code>中的<code>123</code>），用<code>@PathVariable</code>接收，无需<code>@RequestBody</code>。</p> <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/user/{id}"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">getUser</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 从URL路径获取id</span>    <span class="token comment">// 查询用户逻辑</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>**参数来自查询参数（<code>@RequestParam</code>）**参数在 URL 末尾（如<code>/search?name=zhangsan&amp;age=20</code>），用<code>@RequestParam</code>接收（简单类型可省略该注解）。</p> <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/search"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">search</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span> <span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token class-name">Integer</span> age<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 从查询参数获取</span>    <span class="token comment">// 搜索逻辑</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>**表单提交（<code>application/x-www-form-urlencoded</code>）**前端通过表单提交数据（<code>Content-Type: application/x-www-form-urlencoded</code>），此时参数在请求体中，但格式是键值对（非 JSON），无需<code>@RequestBody</code>，Spring 会自动绑定到对象。</p><p> 示例：前端表单：</p> <pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>post<span class="token punctuation">"</span></span> <span class="token attr-name">action</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/register<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>zhangsan<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>123456<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p> 后端接收：</p> <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">"/register"</span><span class="token punctuation">)</span><span class="token keyword">public</span> <span class="token class-name">Result</span> <span class="token function">register</span><span class="token punctuation">(</span><span class="token class-name">User</span> user<span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 无需@RequestBody，自动绑定表单字段</span>    <span class="token comment">// 注册逻辑</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li></ol><h3 id="三、关键判断依据"><a href="#三、关键判断依据" class="headerlink" title="三、关键判断依据"></a>三、关键判断依据</h3><ol><li><strong>数据位置</strong>：参数来自请求体（Body）还是 URL（路径 &#x2F; 查询参数）？<ul><li>来自请求体 → 可能需要<code>@RequestBody</code>（除非是表单键值对）。</li><li>来自 URL → 不需要。</li></ul></li><li><strong>请求格式</strong>：请求体是 JSON&#x2F;XML 还是表单键值对？<ul><li>JSON&#x2F;XML → 需要<code>@RequestBody</code>。</li><li>表单键值对（<code>application/x-www-form-urlencoded</code>） → 不需要（复杂对象也能自动绑定）。</li></ul></li><li><strong>参数类型</strong>：简单类型（String、int 等）还是复杂对象 &#x2F; 集合？<ul><li>复杂对象 &#x2F; 集合且来自请求体 → 需要<code>@RequestBody</code>。</li><li>简单类型且来自查询参数 &#x2F; 表单 → 不需要。</li></ul></li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p><code>@RequestBody</code>的核心是<strong>处理请求体中的非表单格式数据（如 JSON）</strong>，将其转换为 Java 对象。如果参数来自 URL 或表单提交的键值对，则无需添加该注解。</p><hr><h3 id="四、比较用-还是-Objects-equals-？"><a href="#四、比较用-还是-Objects-equals-？" class="headerlink" title="四、比较用 &#x3D;&#x3D; 还是 Objects.equals() ？"></a>四、比较用 &#x3D;&#x3D; 还是 Objects.equals() ？</h3><p><strong>比较数字包装类（如 <code>Integer</code>、<code>Long</code> 等）时，<code>==</code> 存在潜在问题，推荐用更安全的方式（如 <code>Objects.equals</code>）替代</strong>。具体原因和建议如下：</p><h4 id="1-的问题：比较-“引用地址”-而非-“值相等”"><a href="#1-的问题：比较-“引用地址”-而非-“值相等”" class="headerlink" title="1. == 的问题：比较 “引用地址” 而非 “值相等”"></a>1. <code>==</code> 的问题：比较 “引用地址” 而非 “值相等”</h4><p>包装类（如 <code>Integer</code>）是<strong>对象</strong>，<code>==</code> 比较的是<strong>对象的内存地址是否相同</strong>，而非 “值是否相等”。</p><ul><li><p>示例 1（缓存范围内，看似正常）：</p>  <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Integer</span> a <span class="token operator">=</span> <span class="token number">127</span><span class="token punctuation">;</span><span class="token class-name">Integer</span> b <span class="token operator">=</span> <span class="token number">127</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>a <span class="token operator">==</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// true（因为 Integer 缓存了 -128~127 的对象）</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></li><li><p>示例 2（超出缓存范围，逻辑错误）：</p>  <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Integer</span> c <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span><span class="token class-name">Integer</span> d <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>c <span class="token operator">==</span> d<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// false（超出缓存，是两个不同对象）</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>  此时 <code>c</code> 和 <code>d</code> 的<strong>值都是 128</strong>，但 <code>==</code> 结果为 <code>false</code>（因为是不同对象实例）。</p></li></ul><h4 id="2-无法安全处理-null"><a href="#2-无法安全处理-null" class="headerlink" title="2. == 无法安全处理 null"></a>2. <code>==</code> 无法安全处理 <code>null</code></h4><p>如果比较的对象包含 <code>null</code>，<code>==</code> 会直接抛出 <code>NullPointerException</code>：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Integer</span> a <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><span class="token class-name">Integer</span> b <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>a <span class="token operator">==</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 直接抛 NPE</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="推荐用法：Objects-equals"><a href="#推荐用法：Objects-equals" class="headerlink" title="推荐用法：Objects.equals(...)"></a>推荐用法：<code>Objects.equals(...)</code></h4><p><code>java.util.Objects.equals(Object a, Object b)</code> 是最安全的方式，原因：</p><ul><li>内部先判断 <code>a</code> 或 <code>b</code> 是否为 <code>null</code>，<strong>避免空指针异常</strong>；</li><li>再调用 <code>equals</code> 方法比较<strong>值是否相等</strong>（而非引用地址）。</li></ul><p>示例：</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Integer</span> a <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span><span class="token class-name">Integer</span> b <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// true（值相等）</span><span class="token class-name">Integer</span> c <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><span class="token class-name">Integer</span> d <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> d<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// false（安全，无 NPE）</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>总结：当需要比较 ** 数字包装类的 “值是否相等”** 时，应避免用 <code>==</code>，推荐使用 <code>Objects.equals(...)</code> 保证逻辑正确且避免空指针。</p><hr><h3 id="五、什么时候xml映射文件「可以不指定」parameterType？"><a href="#五、什么时候xml映射文件「可以不指定」parameterType？" class="headerlink" title="五、什么时候xml映射文件「可以不指定」parameterType？"></a>五、什么时候xml映射文件「可以不指定」<code>parameterType</code>？</h3><p>MyBatis 3.4.0 及以上版本（苍穹外卖用的是高版本，如 3.5.x），满足以下条件时，<code>parameterType</code> 可省略：</p><h4 id="1-参数是「JavaBean-类型」（如-Employee、EmployeeDTO）"><a href="#1-参数是「JavaBean-类型」（如-Employee、EmployeeDTO）" class="headerlink" title="1. 参数是「JavaBean 类型」（如 Employee、EmployeeDTO）"></a>1. 参数是「JavaBean 类型」（如 <code>Employee</code>、<code>EmployeeDTO</code>）</h4><p>高版本 MyBatis 会通过 <strong>反射</strong> 自动推断参数类型：</p><ul><li>若 Mapper 接口方法的参数是 <code>Employee</code>（如 <code>void update(Employee employee)</code>），MyBatis 会直接识别参数类型为 <code>Employee</code>，无需显式写 <code>parameterType=&quot;Employee&quot;</code>。</li></ul><p>例如：即使去掉 <code>parameterType</code>，以下 SQL 仍能正常运行：</p><pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>update</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>update<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- 省略 parameterType --></span>    update employee    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>set</span><span class="token punctuation">></span></span>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>if</span> <span class="token attr-name">test</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>status != null<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>status = #{status},<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>if</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>set</span><span class="token punctuation">></span></span>    where id = #{id}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>update</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="2-参数用-Param-注解显式标记"><a href="#2-参数用-Param-注解显式标记" class="headerlink" title="2. 参数用 @Param 注解显式标记"></a>2. 参数用 <code>@Param</code> 注解显式标记</h4><p>若 Mapper 接口方法用 <code>@Param</code> 指定参数名，MyBatis 会通过 <code>@Param</code> 识别参数，无需 <code>parameterType</code>：</p><ul><li><p>Mapper 接口：</p>  <pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">void</span> <span class="token function">updateStatus</span><span class="token punctuation">(</span><span class="token annotation punctuation">@Param</span><span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span> <span class="token class-name">Long</span> id<span class="token punctuation">,</span> <span class="token annotation punctuation">@Param</span><span class="token punctuation">(</span><span class="token string">"status"</span><span class="token punctuation">)</span> <span class="token class-name">Integer</span> status<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li><li><p>映射文件（无需parameterType）</p>  <pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>update</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>updateStatus<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    update employee set status = #{status} where id = #{id}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>update</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre></li></ul><h3 id="六、什么时候xml映射文件「必须指定」parameterType"><a href="#六、什么时候xml映射文件「必须指定」parameterType" class="headerlink" title="六、什么时候xml映射文件「必须指定」parameterType"></a>六、什么时候xml映射文件「必须指定」<code>parameterType</code></h3><p>只有两种场景下，<code>parameterType</code> 是必须显式声明的：</p><h4 id="参数类型是「集合-数组-Map」等非-JavaBean-类型"><a href="#参数类型是「集合-数组-Map」等非-JavaBean-类型" class="headerlink" title="参数类型是「集合 &#x2F; 数组 &#x2F; Map」等非 JavaBean 类型"></a>参数类型是「集合 &#x2F; 数组 &#x2F; Map」等非 JavaBean 类型</h4><p>当参数是 <code>List</code>、<code>Array</code>、<code>Map</code> 等特殊类型时，MyBatis 无法通过方法参数自动推断具体类型，必须显式指定 <code>parameterType</code>：</p><ul><li><p>示例 1：批量删除员工（参数是List<Long>)</Long></p>  <pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>delete</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>batchDelete<span class="token punctuation">"</span></span> <span class="token attr-name">parameterType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>java.util.List<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    delete from employee where id in    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>foreach</span> <span class="token attr-name">collection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span> <span class="token attr-name">item</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span> <span class="token attr-name">open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(<span class="token punctuation">"</span></span> <span class="token attr-name">separator</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>,<span class="token punctuation">"</span></span> <span class="token attr-name">close</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>        #{id}    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>foreach</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>delete</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>示例 2：用 Map 传多参数（参数是Map)</p>  <pre class="line-numbers language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>getByMap<span class="token punctuation">"</span></span> <span class="token attr-name">parameterType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>java.util.Map<span class="token punctuation">"</span></span> <span class="token attr-name">resultType</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Employee<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    select * from employee     where name = #{name} and status = #{status}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre></li></ul>]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/11/sky-takeout-day02-note/</id>
    <link href="https://junnhwan.github.io/2025/10/11/sky-takeout-day02-note/"/>
    <published>2025-10-11T13:03:02.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day02"><a href="#Sky-takeout-Day02" class="headerlink" title="Sky-takeout  Day02"></a>Sky-takeout  Day02</h1><blockquote>
<p>【前言】<br>Day02 的 Task 是实现最基础的 CRUD 功能，流程：先做需求分析，再开始编写代码，然后进行功能测试（接口文档、前后端联调）以及代码完善。<br>首先，通过员工管理模块的新增、分页查询、启用禁用账号、编辑这四大功能，来了解简单的 CRUD 如何写。<br>其次，因为菜品分类管理模块的逻辑与员工管理的几乎一样，所以就直接导入了，想巩固的话可以自己手敲一遍，加深印象。</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day02-note</title>
    <updated>2026-07-26T03:41:24.386Z</updated>
  </entry>
  <entry>
    <author>
      <name>junnhwan</name>
    </author>
    <category term="项目学习" scheme="https://junnhwan.github.io/categories/%E9%A1%B9%E7%9B%AE%E5%AD%A6%E4%B9%A0/"/>
    <category term="Java" scheme="https://junnhwan.github.io/tags/Java/"/>
    <content>
      <![CDATA[<h1 id="Sky-takeout-Day01"><a href="#Sky-takeout-Day01" class="headerlink" title="Sky-takeout  Day01"></a>Sky-takeout  Day01</h1><blockquote><p>【前言】</p><p>本节内容主要重点是搭建开发环境，重点是后端环境，会有一些坑（本文只提到了我遇到的）比如 Lombok 版本兼容问题、数据库账户密码设置问题。<br>前面两部分软件开发流程和项目介绍，都是黑马官方笔记 copy 来的，阅读了解一下即可，主要是一些概念。<br>其次关于 Nginx 的概念与作用、Swagger 的使用，个人认为最好熟悉一下。<br>此外，我在 contents 中提到的是官方课程目录，本文不一定全都有相关内容的覆盖，可自行前往课程了解学习。</p></blockquote><span id="more"></span><h3 id="Contents"><a href="#Contents" class="headerlink" title="Contents"></a>Contents</h3><ul><li><p>软件开发流程介绍</p><ul><li>开发流程</li><li>角色分工</li><li>三种软件环境</li></ul></li><li><p>项目整体介绍</p><ul><li>项目原型</li><li>技术选型</li></ul></li><li><p>前后端环境搭建流程（完善登录功能）</p><ul><li>熟悉项目结构</li><li>Git 版本控制</li><li>数据库环境搭建</li><li>前后端联调, 断点调试，熟悉代码结构</li><li>Nginx 反向代理、负载均衡相关知识</li><li>完善登录功能</li></ul></li><li><p>Swagger 接口文档介绍</p></li></ul><hr><h3 id="1-软件开发流程介绍"><a href="#1-软件开发流程介绍" class="headerlink" title="1. 软件开发流程介绍"></a>1. 软件开发流程介绍</h3><h4 id="开发流程"><a href="#开发流程" class="headerlink" title="开发流程"></a>开发流程</h4><img src="/2025/10/10/sky-takeout-day01-note/image-20251010113130473.png" alt="image-20251010113130473" style="zoom:50%;"><p><strong>1). 第 1 阶段: 需求分析</strong></p><p>完成需求规格说明书、产品原型编写。  </p><ul><li><p>需求规格说明书， 一般来说就是使用 Word 文档来描述当前项目的各个组成部分，如：系统定义、应用环境、功能规格、性能需求等，都会在文档中描述。</p></li><li><p>产品原型，一般是通过网页(html)的形式展示当前的页面展示什么样的数据, 页面的布局是什么样子的，点击某个菜单，打开什么页面，点击某个按钮，出现什么效果，都可以通过产品原型看到。</p></li></ul><p><strong>2). 第 2 阶段: 设计</strong></p><p>设计的内容包含 UI 设计、数据库设计、接口设计。</p><ul><li><p>UI 设计：用户界面的设计，主要设计项目的页面效果，小到一个按钮，大到一个页面布局，还有人机交互逻辑的体现。</p></li><li><p>数据库设计：需要设计当前项目中涉及到哪些数据库，每一个数据库里面包含哪些表，这些表结构之间的关系是什么样的，表结构中包含哪些字段。</p></li><li><p>接口设计：通过分析原型图，首先，粗粒度地分析每个页面有多少接口，然后，再细粒度地分析每个接口的传入参数，返回值参数，同时明确接口路径及请求方式。</p></li></ul><p><strong>3). 第 3 阶段: 编码</strong></p><p>编写项目代码、并完成单元测试。</p><ul><li><p>项目代码编写：软件开发工程师对项目的模块功能分析后，进行编码实现。</p></li><li><p>单元测试：编码实现完毕后，进行单元测试，单元测试通过后再进入到下一阶段。</p></li></ul><p><strong>4). 第 4 阶段: 测试</strong></p><p>测试人员, 对部署在测试环境的项目进行功能测试, 并出具测试报告。</p><p><strong>5). 第 5 阶段: 上线运维</strong></p><p>在项目上线之前， 会由运维人员准备服务器上的软件环境安装、配置， 配置完毕后， 再将我们开发好的项目，部署在服务器上运行。</p><h4 id="角色分工"><a href="#角色分工" class="headerlink" title="角色分工"></a>角色分工</h4><table><thead><tr><th align="left">岗位&#x2F;角色</th><th>对应阶段</th><th align="left">职责&#x2F;分工</th></tr></thead><tbody><tr><td align="left">项目经理</td><td>全阶段</td><td align="left">对整个项目负责，任务分配、把控进度</td></tr><tr><td align="left">产品经理</td><td>需求分析</td><td align="left">进行需求调研，输出需求调研文档、产品原型等</td></tr><tr><td align="left">UI 设计师</td><td>设计</td><td align="left">根据产品原型输出界面效果图</td></tr><tr><td align="left">架构师</td><td>设计</td><td align="left">项目整体架构设计、技术选型等</td></tr><tr><td align="left">开发工程师</td><td>编码</td><td align="left">功能代码实现</td></tr><tr><td align="left">测试工程师</td><td>测试</td><td align="left">编写测试用例，输出测试报告</td></tr><tr><td align="left">运维工程师</td><td>上线运维</td><td align="left">软件环境搭建、项目上线</td></tr></tbody></table><h4 id="软件环境"><a href="#软件环境" class="headerlink" title="软件环境"></a>软件环境</h4><p>开发环境(development)、测试环境(testing)、生产环境(production)。</p><p>首先，会在开发环境中进行项目开发，往往开发环境大多数都是本地的电脑环境和局域网内的环境，当开发完毕后，然后会把项目部署到测试环境，测试环境一般是一台独立测试服务器的环境，项目测试通过后，最终把项目部署到生产环境，生产环境可以是机房或者云服务器等线上环境。</p><h3 id="2-项目整体介绍"><a href="#2-项目整体介绍" class="headerlink" title="2. 项目整体介绍"></a>2. 项目整体介绍</h3><h4 id="架构图"><a href="#架构图" class="headerlink" title="架构图"></a>架构图</h4><img src="/2025/10/10/sky-takeout-day01-note/image-20251010124754515.png" alt="image-20251010124754515" style="zoom:50%;"><hr><h4 id="技术选型"><a href="#技术选型" class="headerlink" title="技术选型"></a>技术选型</h4><p><img src="/2025/10/10/sky-takeout-day01-note/image-20251010124845981.png" alt="image-20251010124845981"></p><p><strong>1). 用户层</strong></p><p>本项目中在构建系统管理后台的前端页面，我们会用到 H5、Vue.js、ElementUI、apache echarts(展示图表)等技术。而在构建移动端应用时，我们会使用到微信小程序。</p><p><strong>2). 网关层</strong></p><p>Nginx 是一个服务器，主要用来作为 Http 服务器，部署静态资源，访问性能高。在 Nginx 中还有两个比较重要的作用： 反向代理和负载均衡， 在进行项目部署时，要实现 Tomcat 的负载均衡，就可以通过 Nginx 来实现。</p><p><strong>3). 应用层</strong></p><p>SpringBoot： 快速构建 Spring 项目, 采用 “约定优于配置” 的思想, 简化 Spring 项目的配置开发。</p><p>SpringMVC：SpringMVC 是 spring 框架的一个模块，springmvc 和 spring 无需通过中间整合层进行整合，可以无缝集成。</p><p>Spring Task:  由 Spring 提供的定时任务框架。</p><p>httpclient:  主要实现了对 http 请求的发送。</p><p>Spring Cache:  由 Spring 提供的数据缓存框架</p><p>JWT:  用于对应用程序上的用户进行身份验证的标记。</p><p>阿里云 OSS:  对象存储服务，在项目中主要存储文件，如图片等。</p><p>Swagger： 可以自动的帮助开发人员生成接口文档，并对接口进行测试。</p><p>POI:  封装了对 Excel 表格的常用操作。</p><p>WebSocket: 一种通信网络协议，使客户端和服务器之间的数据交换更加简单，用于项目的来单、催单功能实现。</p><p><strong>4). 数据层</strong></p><p>MySQL： 关系型数据库, 本项目的核心业务数据都会采用 MySQL 进行存储。</p><p>Redis： 基于 key-value 格式存储的内存数据库, 访问速度快, 经常使用它做缓存。</p><p>Mybatis： 本项目持久层将会使用 Mybatis 开发。</p><p>pagehelper:  分页插件。</p><p>spring data redis:  简化 java 代码操作 Redis 的 API。</p><p><strong>5). 工具</strong></p><p>git: 版本控制工具, 在团队协作中, 使用该工具对项目中的代码进行管理。</p><p>maven: 项目构建工具。</p><p>junit：单元测试工具，开发人员功能实现完毕后，需要通过 junit 对功能进行单元测试。</p><p>postman:  接口测工具，模拟用户发起的各类 HTTP 请求，获取对应的响应结果。</p><hr><h4 id="产品原型"><a href="#产品原型" class="headerlink" title="产品原型"></a>产品原型</h4><p><strong>1). 管理端</strong></p><p>餐饮企业内部员工使用。 主要功能有: </p><table><thead><tr><th>模块</th><th>描述</th></tr></thead><tbody><tr><td>登录&#x2F;退出</td><td>内部员工必须登录后, 才可以访问系统管理后台</td></tr><tr><td>员工管理</td><td>管理员可以在系统后台对员工信息进行管理，包含查询、新增、编辑、禁用等功能</td></tr><tr><td>分类管理</td><td>主要对当前餐厅经营的 菜品分类 或 套餐分类 进行管理维护， 包含查询、新增、修改、删除等功能</td></tr><tr><td>菜品管理</td><td>主要维护各个分类下的菜品信息，包含查询、新增、修改、删除、启售、停售等功能</td></tr><tr><td>套餐管理</td><td>主要维护当前餐厅中的套餐信息，包含查询、新增、修改、删除、启售、停售等功能</td></tr><tr><td>订单管理</td><td>主要维护用户在移动端下的订单信息，包含查询、取消、派送、完成，以及订单报表下载等功能</td></tr><tr><td>数据统计</td><td>主要完成对餐厅的各类数据统计，如营业额、用户数量、订单等</td></tr></tbody></table><p><strong>2). 用户端</strong></p><p>移动端应用主要提供给消费者使用。主要功能有:</p><table><thead><tr><th>模块</th><th>描述</th></tr></thead><tbody><tr><td>登录&#x2F;退出</td><td>用户需要通过微信授权后登录使用小程序进行点餐</td></tr><tr><td>点餐-菜单</td><td>在点餐界面需要展示出菜品分类&#x2F;套餐分类, 并根据当前选择的分类加载其中的菜品信息, 供用户查询选择</td></tr><tr><td>点餐-购物车</td><td>用户选中的菜品就会加入用户的购物车, 主要包含 查询购物车、加入购物车、删除购物车、清空购物车等功能</td></tr><tr><td>订单支付</td><td>用户选完菜品&#x2F;套餐后, 可以对购物车菜品进行结算支付, 这时就需要进行订单的支付</td></tr><tr><td>个人信息</td><td>在个人中心页面中会展示当前用户的基本信息, 用户可以管理收货地址, 也可以查询历史订单数据</td></tr></tbody></table><h3 id="3-前端环境搭建"><a href="#3-前端环境搭建" class="headerlink" title="3. 前端环境搭建"></a>3. 前端环境搭建</h3><ul><li>直接在 heima 的 day01 资料中把 “前端运行环境” 中的 nginx-1.20.2 目录下的 nginx.exe 双击即可（注意 <strong>文件路径不能有中文</strong>）</li></ul><h3 id="4-后端环境搭建"><a href="#4-后端环境搭建" class="headerlink" title="4. 后端环境搭建"></a>4. 后端环境搭建</h3><h4 id="后端搭建环境注意事项"><a href="#后端搭建环境注意事项" class="headerlink" title="后端搭建环境注意事项"></a><strong>后端搭建环境注意事项</strong></h4><ul><li>注 1：lombok 版本改高一点，改成 1.18.30，如果版本低构建项目会报错，原因应该是 JDK 版本过高不兼容，比如我的是 JDK21，所以 1.18.30 才不会报错  <img src="/2025/10/10/sky-takeout-day01-note/Snipaste_2025-10-10_09-19-06.png" alt="Snipaste_2025-10-10_09-19-06" style="zoom:33%;"></li><li>注 2：数据库的账号密码要改成自己本机的 MySQL 的密码, 在 application-dev.yml 中修改, 否则会登录不上前端网站, 报数据库连接池连接不到的错误  <img src="/2025/10/10/sky-takeout-day01-note/Snipaste_2025-10-10_09-23-40.png" alt="Snipaste_2025-10-10_09-23-40" style="zoom:33%;"></li></ul><h4 id="数据库表说明"><a href="#数据库表说明" class="headerlink" title="数据库表说明"></a>数据库表说明</h4><table><thead><tr><th><strong>序号</strong></th><th><strong>表名</strong></th><th><strong>中文名</strong></th></tr></thead><tbody><tr><td>1</td><td>employee</td><td>员工表</td></tr><tr><td>2</td><td>category</td><td>分类表</td></tr><tr><td>3</td><td>dish</td><td>菜品表</td></tr><tr><td>4</td><td>dish_flavor</td><td>菜品口味表</td></tr><tr><td>5</td><td>setmeal</td><td>套餐表</td></tr><tr><td>6</td><td>setmeal_dish</td><td>套餐菜品关系表</td></tr><tr><td>7</td><td>user</td><td>用户表</td></tr><tr><td>8</td><td>address_book</td><td>地址表</td></tr><tr><td>9</td><td>shopping_cart</td><td>购物车表</td></tr><tr><td>10</td><td>orders</td><td>订单表</td></tr><tr><td>11</td><td>order_detail</td><td>订单明细表</td></tr></tbody></table><h4 id="前后端联调熟悉项目结构"><a href="#前后端联调熟悉项目结构" class="headerlink" title="前后端联调熟悉项目结构"></a><strong>前后端联调熟悉项目结构</strong></h4><ul><li>大体流程如下</li></ul><p><img src="/2025/10/10/sky-takeout-day01-note/image-20251010094303871.png" alt="image-20251010094303871"></p><h3 id="5-Nginx-反向代理介绍"><a href="#5-Nginx-反向代理介绍" class="headerlink" title="5. Nginx 反向代理介绍"></a>5. Nginx 反向代理介绍</h3><h4 id="反向代理好处："><a href="#反向代理好处：" class="headerlink" title="反向代理好处："></a>反向代理好处：</h4><img src="/2025/10/10/sky-takeout-day01-note/image-20251010100204967.png" alt="image-20251010100204967" style="zoom: 50%;"><ul><li><p>提高访问速度</p><p>  因为 nginx 本身可以进行缓存，如果访问的同一接口，并且做了数据缓存，nginx 就直接可把数据返回，不需要真正地访问服务端，从而提高访问速度。</p></li><li><p>进行负载均衡</p><p>  所谓负载均衡, 就是把大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器。</p></li><li><p>保证后端服务安全</p><p>  因为一般后台服务地址不会暴露，所以使用浏览器不能直接访问，可以把 nginx 作为请求访问的入口，请求到达 nginx 后转发到具体的服务中，从而保证后端服务的安全。</p></li></ul><h4 id="Nginx-的配置："><a href="#Nginx-的配置：" class="headerlink" title="Nginx 的配置："></a>Nginx 的配置：</h4><p><strong>proxy_pass：</strong> 该指令是用来设置代理服务器的地址，可以是主机名称，IP 地址加端口号等形式。</p><p>如上代码的含义是：监听 80 端口号， 然后当我们访问 <a href="http://localhost/">http://localhost</a>: 80&#x2F;api&#x2F;..&#x2F;..这样的接口的时候，它会通过 location &#x2F;api&#x2F; {} 这样的反向代理到 <a href="http://localhost/">http://localhost</a>: 8080&#x2F;admin&#x2F;上来。</p><ol><li>通常配置，在 nginx.conf 配置文件中，找到如下部分</li></ol><img src="/2025/10/10/sky-takeout-day01-note/image-20251010100742140.png" alt="image-20251010100742140" style="zoom: 50%;"><ol start="2"><li>在本项目中，负载均衡配置方式</li></ol><img src="/2025/10/10/sky-takeout-day01-note/image-20251010100820078.png" alt="image-20251010100820078" style="zoom:50%;"><h3 id="6-完善登录功能"><a href="#6-完善登录功能" class="headerlink" title="6. 完善登录功能"></a>6. 完善登录功能</h3><p>问题：明文存储，安全性低<br><img src="/2025/10/10/sky-takeout-day01-note/image-20251010101048960.png" alt="image-20251010101048960" style="zoom: 50%;"></p><p>解决：使用 spring 框架中的 MD5 加密算法把 password 加密，在工具类 DigestUtils 中</p><h3 id="7-Swagger-接口文档介绍"><a href="#7-Swagger-接口文档介绍" class="headerlink" title="7. Swagger 接口文档介绍"></a>7. Swagger 接口文档介绍</h3><h4 id="导入接口文档"><a href="#导入接口文档" class="headerlink" title="导入接口文档"></a>导入接口文档</h4><p>​将资料中的 json 文档文件导入接口平台如 Apifox 或 yapi, 选择 json 格式导入即可。</p><h4 id="Swagger-介绍和使用"><a href="#Swagger-介绍和使用" class="headerlink" title="Swagger 介绍和使用"></a>Swagger 介绍和使用</h4><h5 id="1）介绍："><a href="#1）介绍：" class="headerlink" title="1）介绍："></a>1）介绍：</h5><p>​<img src="/2025/10/10/sky-takeout-day01-note/image-20251010105223256.png" alt="image-20251010105223256" style="zoom: 80%;"></p><p>Swagger 是一个规范和完整的框架，用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(<a href="https://swagger.io/">https://swagger.io/</a>)。 它的主要作用是：</p><ol><li><p>使得前后端分离开发更加方便，有利于团队协作</p></li><li><p>接口的文档在线自动生成，降低后端开发人员编写接口文档的负担</p></li><li><p>功能测试 </p><p> Spring 已经将 Swagger 纳入自身的标准，建立了 Spring-swagger 项目，现在叫 Springfox。通过在项目中引入 Springfox ，即可非常简单快捷的使用 Swagger。</p></li></ol><p>knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案, 前身是 swagger-bootstrap-ui, 取名 kni4j 是希望它能像一把匕首一样小巧, 轻量, 并且功能强悍!</p><p>目前，一般都使用 knife4j 框架。</p><hr><h5 id="2）使用方式"><a href="#2）使用方式" class="headerlink" title="2）使用方式"></a>2）使用方式</h5><img src="/2025/10/10/sky-takeout-day01-note/image-20251010105441159.png" alt="image-20251010105441159" style="zoom: 50%;"><ul><li><p>导入 maven 坐标</p>  <img src="/2025/10/10/sky-takeout-day01-note/image-20251010105648143.png" alt="image-20251010105648143" style="zoom: 50%;"></li><li><p>配置类中加入 knife4j 配置代码</p>  <img src="/2025/10/10/sky-takeout-day01-note/image-20251010105708341.png" alt="image-20251010105708341" style="zoom:50%;"></li><li><p>设置静态资源映射</p>  <img src="/2025/10/10/sky-takeout-day01-note/image-20251010105731635.png" alt="image-20251010105731635" style="zoom: 67%;"></li><li><p>访问测试</p></li></ul><p>​接口文档访问路径为 <a href="http://localhost/">http://localhost</a>: 8080&#x2F;doc.html</p><h5 id="3）Swagger-常用注解"><a href="#3）Swagger-常用注解" class="headerlink" title="3）Swagger 常用注解"></a>3）Swagger 常用注解</h5><img src="/2025/10/10/sky-takeout-day01-note/image-20251010110634376.png" alt="image-20251010110634376" style="zoom: 67%;">]]>
    </content>
    <id>https://junnhwan.github.io/2025/10/10/sky-takeout-day01-note/</id>
    <link href="https://junnhwan.github.io/2025/10/10/sky-takeout-day01-note/"/>
    <published>2025-10-10T05:26:04.000Z</published>
    <summary>
      <![CDATA[<h1 id="Sky-takeout-Day01"><a href="#Sky-takeout-Day01" class="headerlink" title="Sky-takeout  Day01"></a>Sky-takeout  Day01</h1><blockquote>
<p>【前言】</p>
<p>本节内容主要重点是搭建开发环境，重点是后端环境，会有一些坑（本文只提到了我遇到的）比如 Lombok 版本兼容问题、数据库账户密码设置问题。<br>前面两部分软件开发流程和项目介绍，都是黑马官方笔记 copy 来的，阅读了解一下即可，主要是一些概念。<br>其次关于 Nginx 的概念与作用、Swagger 的使用，个人认为最好熟悉一下。<br>此外，我在 contents 中提到的是官方课程目录，本文不一定全都有相关内容的覆盖，可自行前往课程了解学习。</p>
</blockquote>]]>
    </summary>
    <title>sky-takeout-day01-note</title>
    <updated>2026-07-26T03:41:24.386Z</updated>
  </entry>
</feed>
