银河护胃队-beta冲刺
作业所属课程 | https://edu.cnblogs.com/campus/fzu/SE2024/ |
---|---|
作业要求 | https://edu.cnblogs.com/campus/fzu/SE2024/homework/13310 |
作业的目标 | beta冲刺总结 |
团队名称 | 银河护胃队 |
团队成员学号-名字 | 072208130-曹星才(组长) 052205144-张诗悦 102201120-陈康培 102201342-潘宇晴 102202108-王露洁 102202111-刘哲睿 102202128-林子豪 102202142-黄悦佳 102202149-詹镇壕 102202153-来再提·叶鲁别克 |
一、alpha冲刺后问题与解决
问题一
问题:
在小程序的部署过程中,我们只能在安卓手机端上运行,其他设备如苹果、电脑,暂时无法使用。
解决方法:
iOS设备调试:对于iOS设备,需要在开发者工具中设置特殊UserAgent让小程序代码知道是iOS平台,并且在iOS设备上拦截URL特殊处理,以便小程序代码在iOS平台上正常运行。
问题二
问题:
前后端对接未完成,部分后端接口设计在同一个端口,且flask默认是单线程的,当接受较多请求时,后端响应时速度过慢
探索思路:
可以通过提高硬件性能解决,但不现实,考虑能否实现多线程同步响应或者将部分接口调用端口分离,以提高响应速度。
解决过程:
对不同接口进行分离调用,如将大模型部分的接口与数据库数据管理的接口分别部署在不同的端口上,同时使用flask的并发接口,开启多线程处理请求,提高处理速度。
问题三
问题:
部分页面或组件在不同设备上显示不正常,可能是由于响应式设计不足或样式冲突,导致页面在不同尺寸的屏幕上无法自适应。
解决方案:
使用浏览器的开发者工具模拟不同设备,检查布局和样式,发现部分页面未使用合适的媒体查询来适配不同的屏幕尺寸。针对这个问题,增加了媒体查询来确保页面在各种设备上都能自适应显示,同时使用了 flexbox 和 grid 等 CSS 布局模块来优化布局的响应式效果,确保字体、图像等元素在各种屏幕上都能正确显示。
问题四
问题:
在前端开发中,遇到了一个影响用户体验的问题:食谱详情页面加载缓慢,尤其是在网络环境较差的情况下,用户需要等待较长时间才能看到食谱内容。
解决方案:
使用浏览器的开发者工具进行网络分析,发现每次加载食谱详情时,页面会发起多个 API 请求,而部分 API 请求的响应时间较长,尤其是获取食材的接口。所以进行了对API 请求的合并,即将多个相关的 API 请求合并为一个请求,减少网络请求次数。
问题五
问题:
在协助前端进行整合时,前端的代码无法调用后端的接口。
解决方案:
为了解决前端代码无法调用后端接口的问题,我们采取了以下措施:首先,我们在后端服务器上配置了CORS策略,允许前端域名进行跨域请求。但没用,后面我们研究后发现在服务器上的开放的端口太少,太多请求同时进行会占用端口导致无法调用。为了解决这个问题,我们在服务器上多开放了几个端口。这样可以确保即使在高并发的情况下,服务器也能够处理来自前端的请求,避免因端口不足而导致的调用失败。通过这些措施,我们成功解决了前端代码无法调用后端接口的问题,确保了项目的顺利进行。
问题六
问题:
前端和后端之间的数据传输格式(JSON、表单数据等)会出现问题。例如,前端发送的数据结构不符合后端的要求,或者后端返回的数据格式不符合前端的预期。前端和后端可能部署在不同的域上,这会导致跨域问题,浏览器阻止前端请求后端接口。
解决办法:
确保前后端的数据格式统一,使用 JSON 作为标准数据交换格式;前端发送请求时需要设置正确的 Content-Type,后端则要正确解析请求体中的 JSON 数据。
跨域问题(CORS)。使用 CORS(跨域资源共享)中间件解决跨域问题。在 Flask 中,可以通过安装 flask-cors 来轻松解决
问题七
问题:
调用AI接口并给其模板并合成成为一个大接口并进行调试输出时,返回的答案会是两次一摸一样的
解决方案:
为了解决这个问题,我在每次调用AI接口时增加了一个唯一的调用标识,时间戳以确保每次调用都是独立且可区分的。我仔细审查了接口的代码逻辑,确保没有错误导致重复调用或返回相同的结果。后面发现其实是AI接口输出了一次结果,后面又返回输出了一次。所以我们在处理接口返回数据时,增加了逻辑判断,只取第一次的返回结果进行输出,忽略后续的重复返回。这样就解决了问题
问题八
问题:
在前端页面加入logo背景图片会导致部分按钮失灵。
解决方案:
背景图片使用 position: absolute; 定位,这样它就不会占据任何文档流的空间,从而不会影响按钮的布局和点击区域。通过设置 z-index: 0,确保背景图片位于页面的最底层,这样其他元素(如按钮)就可以位于背景图片之上。背景图片的 opacity 设置为 0.1;,这意味着它是完全不透明的,这样就不会遮挡任何位于其上的元素。
问题九
问题:
关于体重记录的部分,每次记录完之后,记录的新体重就会取代初始体重,而且折线图上每次退出后重新进入也只会保留上次最新的一个记录,导致不好看出体重的变化趋势。
解决思路:
这涉及到后端返回的数据的格式以及数据库中表格的设计,前端需要的是用户体重的所有历史数据,所以相对应的后端从数据库中获取的体重数据应该是json类型,而不是目前的int类型,只要修改一下数据库表格的结构和接口中返回数据的逻辑,应该就能使得前端呈现出体重记录的变化
二、项目特色功能
食谱食物查询
可以查询有什么食谱推荐,点进去可以看到该食谱的详细数据,包括但不限于该食谱的餐厅位置、价格、营养成分、食材、过敏原。
还可以查询食物的数据
在食物红黑榜处可以看对应疾病、或者是减肥、增肌目标的推荐和谨慎食用的食物
随心配
不知道吃什么?可以到我们的随心配界面,“换一换”,挑选你心动的食物!
身体数据记录
身体数据记录,用于后面的健康计划生成和食物推荐
记录每天摄入的食物热量以及体重变化和一些其它的身体数据
AI小助手
普通询问
基于身体数据询问
生成一周计划
生成(保存)为健康计划
三、软件测试
后端测试
API测试
进行多线程测试(10轮次,10线程同时进行)
线程数量为10,此时响应速度较慢,平均请求耗时达到163毫秒,约为单线程次数的三倍
线程数量为20时,达到约260毫秒
Python代码测试
测试用户登录注册等功能
测试通过
前端测试
1. 单元测试(Unit Testing) 单元测试主要用于测试代码中每个独立功能的正确性。在本项目中,前端的每个组件都可以进行单元测试。
-
目标:确保各个组件的功能符合预期,特别是:
collectionItems
是否正确传递到组件并渲染。- 按钮是否按预期执行
goBack
方法,跳转到其他页面。 syncCollection
方法是否正确同步数据(尽管现在是占位方法,未来会实现从云端获取数据的功能)。
-
工具:Jest、Mocha等
-
测试案例:
- 验证
collectionItems
的数据是否在页面中正确渲染。 - 验证返回按钮是否触发页面跳转。
代码:
import { mount } from '@vue/test-utils'; import CollectionPage from '@/components/CollectionPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('CollectionPage.vue', () => { let wrapper; const mockPush = jest.fn(); // 模拟router.push方法 beforeEach(() => { // 创建一个mock路由实例 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); // 挂载组件 wrapper = mount(CollectionPage, { global: { plugins: [router], }, data() { return { collectionItems: [ { name: '水煮鸡胸肉', description: '低脂高蛋白的健身餐', image: '/path/to/image1.png' }, { name: '水煮虾', description: '低卡高蛋白的健康食物', image: '/path/to/image2.png' }, ], }; }, }); }); it('renders collection items correctly', async () => { // 测试菜品列表是否正确渲染 const items = wrapper.findAll('.collection-item'); expect(items.length).toBe(2); // 确保渲染了两项菜品 const firstItemTitle = items[0].find('.item-title'); expect(firstItemTitle.text()).toBe('水煮鸡胸肉'); // 检查第一个菜品的标题 const secondItemDescription = items[1].find('.item-description'); expect(secondItemDescription.text()).toBe('低卡高蛋白的健康食物'); // 检查第二个菜品的描述 }); it('fires goBack method correctly', async () => { // 测试返回按钮是否触发跳转 const backButton = wrapper.find('.back-button'); await backButton.trigger('click'); // 触发点击事件 expect(mockPush).toHaveBeenCalledWith('/home'); // 验证是否调用了路由跳转方法 }); it('handles empty collectionItems gracefully', async () => { // 测试如果 collectionItems 为空,页面是否没有崩溃 await wrapper.setData({ collectionItems: [] }); const items = wrapper.findAll('.collection-item'); expect(items.length).toBe(0); // 确保没有渲染任何菜品项 }); it('renders correct alt text for images', async () => { // 测试图片的alt属性是否正确 const images = wrapper.findAll('.item-image'); expect(images.at(0).attributes('alt')).toBe('加载中'); // 检查第一个图片的alt文本 }); });
- 验证
2. 验收测试(Acceptance Testing) 验收测试主要验证用户需求是否被满足。通过模拟用户行为,确保应用能够正确实现预期的业务需求。
-
目标:确保用户在“我的收藏”页面的各项操作都能顺利完成:
- 用户点击返回按钮后是否跳转到主页。
- 用户能看到菜品的图片、名称和描述,并且图片能正确加载。
-
工具:Selenium、Cypress等
-
测试案例:
- 用户进入页面时是否显示菜品列表。
- 用户点击返回按钮是否跳转到指定页面。
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('HealthReportPage.vue', () => { let wrapper; const mockPush = jest.fn(); // 模拟router.push方法 beforeEach(() => { // 创建一个mock路由实例 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); // 挂载组件 wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('renders health report data correctly', async () => { // 测试健康报告数据是否渲染 const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('健康状况:无慢性疾病,无过敏现象'); const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.text()).toBe('生活习惯:轻度运动,长期久坐'); const bloodSugar = wrapper.find('.blood-sugar'); expect(bloodSugar.text()).toBe('血糖水平:正常'); const bloodFat = wrapper.find('.blood-fat'); expect(bloodFat.text()).toBe('血脂水平:正常'); const otherIndicators = wrapper.find('.other-indicators'); expect(otherIndicators.text()).toBe('其他指标:正常'); }); it('fires goBack method correctly', async () => { // 测试返回按钮是否触发跳转 const backButton = wrapper.find('.back-button'); await backButton.trigger('click'); // 触发点击事件 expect(mockPush).toHaveBeenCalledWith('/home'); // 验证是否调用了路由跳转方法 }); it('displays health report sections correctly', async () => { // 测试报告页面的不同部分是否显示正确 const reportSections = wrapper.findAll('.report-section'); expect(reportSections.length).toBe(4); // 确保有4个报告部分显示 const reportTitle = wrapper.find('h1'); expect(reportTitle.text()).toBe('体检报告'); // 检查报告标题是否正确 }); it('handles missing data gracefully', async () => { // 测试如果缺少健康报告数据,页面是否正常显示 await wrapper.setData({ healthReport: { healthStatus: '', lifestyle: '', testResults: { bloodSugar: '', bloodFat: '', }, otherIndicators: '', }, }); const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('健康状况:无数据'); // 如果没有数据,应该显示“无数据” }); });
3. 可访问性测试(Accessibility Testing) 可访问性测试确保页面可以被所有用户使用,包括有视力或听力障碍的用户。
-
目标:确保页面符合WCAG(Web Content Accessibility Guidelines)标准。
- 页面元素是否可以通过键盘导航。
- 视力受限的用户是否可以通过屏幕阅读器理解页面内容。
-
工具:axe-core、Google Lighthouse等
-
测试案例
- 检查所有图片是否有合适的
alt
属性。 - 确保页面能够通过键盘进行导航。
- 检查所有图片是否有合适的
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; import { axe } from 'jest-axe'; describe('HealthReportPage.vue', () => { let wrapper; beforeEach(() => { const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('should have no accessibility violations', async () => { // 使用 axe-core 来检查页面的可访问性 const results = await axe(wrapper.element); // 验证没有可访问性问题 expect(results).toHaveNoViolations(); }); it('should have proper ARIA attributes', async () => { // 测试健康报告页面是否包含必要的ARIA属性 const healthStatus = wrapper.find('.health-status'); expect(healthStatus.attributes('aria-label')).toBe('健康状况'); // 确保有正确的ARIA标签 const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.attributes('aria-label')).toBe('生活习惯'); // 确保有正确的ARIA标签 }); it('should have proper tab order', async () => { // 测试页面上的可交互元素(如按钮、输入框等)是否有合适的tab顺序 const focusableElements = wrapper.findAll('button, a, input, select, textarea'); const tabIndexes = focusableElements.map((el) => el.element.tabIndex); // 确保tab顺序是正确的 expect(tabIndexes).toEqual([0, 0, 0]); // 根据你的页面内容调整预期的tabIndex值 }); it('should have proper alt text for images', async () => { // 测试页面中的所有图片是否有合适的alt文本 const images = wrapper.findAll('img'); images.wrappers.forEach((imageWrapper) => { const altText = imageWrapper.attributes('alt'); expect(altText).not.toBe(''); }); }); });
4. 回归测试(Regression Testing) 回归测试确保新修改或新增的代码不会破坏现有功能。
-
目标:确保在代码更新后,页面的各个功能依然能够正常运行。
- 在修改代码后,重新运行所有测试用例。
-
工具:Jest、Cypress等
-
测试案例
- 运行全部单元测试,确保组件功能不被破坏。
- 测试各个页面的跳转是否正常,确保返回按钮和其他功能无误。
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('HealthReportPage.vue', () => { let wrapper; beforeEach(() => { const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('should display correct health status', () => { const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('无慢性疾病,无过敏现象'); }); it('should display correct lifestyle habits', () => { const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.text()).toBe('轻度运动,长期久坐'); }); it('should display correct test results', () => { const bloodSugar = wrapper.find('.blood-sugar'); expect(bloodSugar.text()).toBe('血糖水平正常'); const bloodFat = wrapper.find('.blood-fat'); expect(bloodFat.text()).toBe('血脂水平正常'); }); it('should display correct other indicators', () => { const otherIndicators = wrapper.find('.other-indicators'); expect(otherIndicators.text()).toBe('正常'); }); it('should handle changes in health status', async () => { // 模拟更新健康状态 await wrapper.setData({ healthReport: { healthStatus: '有高血压', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }); const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('有高血压'); }); });
四、成员冲刺体会和收获
🌟曹星才
对于本次beta冲刺,相较于前面alpha冲刺的身体和心理同等权重折磨,这次beta冲刺的折磨更多是心理折磨。作为组长,虽然不用打代码,但是要做一些杂活(答辩、博客、ppt、项目文档等等),还得跟进每一个组员的进度,这边问问、那边问问,每得到一个版本又要这边改改、那边改改,像个老妈子一样,整天操心这个那个做没做好。因为每组员的能力不太一样,每次分配任务都是一个大难题,有时候分配给组员做的一些很简单的问题但是他们又没做好,我都恨不得我自己来做(无奈又无力(;′⌒`),心力憔悴),这条路不行那就找下一条路,这个方案不行那就找另外的解决方案,一步步走,终于是把这个软件给做出来了。
虽然咱们的“养食记”小软件看起来没有很出色,但是在我心里它就是所有组里面最厉害的!在冲刺的过程中,不断的改代码、沟通交流、一步步明确需求,一起把这个软件做好,和团队成员不断地进步,这也许就是这门抽象的课给我最好的收获了吧。
喜提“没事”人:
🥓王露洁
我们的项目已经接近尾声了,这实a在a是一场令人难忘的,伴随着各种喜怒哀乐的小组作业经历。这次beta冲刺主要任务就是进行前后端的对接工作,根据需要将后端的数据呈现在前端的界面上,这不是一件容易的事儿。我这一周一直在反复调试,遇到AI无法解决的问题时,我会求助同组的伙伴,他们会把自己知道的东西毫无保留地教给我,最终总能将问题迎刃而解。果然人最不可缺少的就是可靠的伙伴,有了可靠的伙伴,不管做几次这样的作业我都不怕(不过最好还是别做了)。
🍕詹镇壕
在小程序开发的beta冲刺阶段,我主要负责前后端的交接工作和接口bug的处理。这个阶段让我深刻认识到精确沟通和细致测试的重要性。通过与前端团队的紧密合作,我们确保了接口的兼容性和响应速度,同时快速定位并修复了多个关键bug,提升了系统的稳定性。这个过程锻炼了我的问题解决能力,并加深了我对前后端协同工作流程的理解。
🍩黄悦佳
在本次beta测试中,作为后端开发小组的一员,我深刻了解到后期的测试与优化对于一款软件开发的重要性。在实际体验自己开发的软件和程序测试的过程中,各种问题bug层出不穷,需要一步步修改完善。
团队软件开发不是一个人能够完成的,需要整个小组的共同努力,面对问题时,自己独自理解他人写的代码并修改是很困难的,要优先向代码的原作者进行询问了解情况,能过大大提高修改bug的效率。
🍟刘哲睿
在此次beta冲刺中,我作为负责AI接口的开发和调用,深刻体会到团队合作和个人技能提升的重要性。通过与其他后端队友的紧密协作,我们成功解决了多个技术难题,如生成健康计划的回答有误,帮助前端整合代码,这些经历让我认识到有效沟通和协调任务的重要性,并提升了我的问题解决和时间管理能力。同时,我也意识到持续学习新技术对于项目成功至关重要。在压力下,我学会了保持冷静和专注,确保在团队中能够尽我所能实现共同目标。这次冲刺让我更加明确了团队合作和个人的学习与工作能力对项目成功的影响。
🥚张诗悦
在本次项目开发过程中,我主要负责AI接口的开发和调用,深刻体会到了团队合作、沟通协调以及技术能力提升的重要性。特别是在处理图像识别和数据预处理方面,团队成员之间的紧密配合使得问题能够快速被发现和解决。例如,在处理训练数据时,尽管我遇到了多次数据预处理错误,通过与前端同学的沟通和及时的技术调整,我们找到了合适的解决方案,并有效避免了技术瓶颈的产生。这个过程中,我不仅提升了自己的问题解决能力,还学到了如何在高压下合理分配时间和资源。
此外,我也意识到持续学习和适应新技术对于项目的成功至关重要。无论是深度学习模型的微调还是数据处理方法的调整,都让我更加注重技术的深度和广度。在后期的开发中,我加强了对数据集的理解,尝试在不同场景下优化模型的表现,从而进一步提升了自己的专业能力。
这次项目让我更加明确了团队合作和个人学习能力的重要性,也让我更加坚定了在工作中不断积累经验和提升自己技术水平的决心。
🥙来再提
在这次 Beta 冲刺中,我主要负责首页的功能开发和优化。在解决首页食谱查询页面、食物排行榜以及食谱推荐的数据动态加载时,我与后端同学合作,实现了 API 接口。一开始在接接口的过程中,我遇到了很多难题,最后还是通过后端同学和小组长的协助下才完成了这些任务。Beta 冲刺让我提高了团队合作意识和用户体验意识。通过这次实践,我更加充分地理解了前后端是如何对接运行的,并积累了宝贵的经验。
🍠潘宇晴
在这次beta冲刺中,我负责将部分前端页面与后端相对接。由于之前一直做的是前端有关的工作,所以当我收到后端代码时有些不知所措,在同组组员和小组长的帮助下,我尝试将前端与后端对接运行,成功运行后我开始进行自己负责页面的对接。一开始总是对接失败,通过和后端同学的沟通交流,后端代码也不断修改完善成我需要的接口,我一次次试验修改bug,最终成功将前端代码与后端代码对接。当对接成功的一刻,前端页面加载出内容,我感到非常有成就感。这次实践让我更加充分地理解了前后端是如何对接运行,使我的实践操作能力进一步增强。
🍰陈康培
在这次项目开发过程中,我深刻体会到了前端开发中细节的重要性。首先,解决页面响应式设计的问题让我意识到,不同设备的适配不仅仅是调整布局那么简单,还需要考虑字体、图像等元素的自适应显示。通过合理使用 flexbox 和 grid 等现代布局技术,我成功提高了页面在各种设备上的兼容性。而状态管理问题则让我认识到,组件间的状态传递应该保持简洁和清晰。这次团队合作让我深刻感受到沟通与协作的重要性。每个人的贡献都至关重要,只有在共同努力下,才能解决复杂的问题。通过相互支持和分工协作,我们能够高效推进项目,达成共同目标。
🍭林子豪
在开发过程中,前后端分离架构带来了非常大的灵活性。前端和后端可以独立开发,且使用 API 进行通信,使得前后端的开发团队能够并行工作,减少了耦合度。
学会了如何设计清晰且易于维护的 API。良好的 API 设计可以大大提高系统的可扩展性和灵活性。例如,RESTful 风格的接口能够使得系统的扩展变得更加简单,而响应中的标准化错误信息和状态码也有助于快速定位问题。
在项目中深入了解了数据库设计的要点,包括如何设计表结构、选择合适的数据类型、使用索引等。在处理大量数据时,数据库优化尤为重要。通过合理的索引设计和查询优化,可以极大提高应用的性能。
五、软件体验
注册的时候验证码可以随便填
不想注册的可以直接用测试账号:手机号(123),密码(123)
使用手机扫码体验(注意只能Android用户使用,ios的开发者难申请而且比较贵):
六、源码
项目仓库:https://github.com/healthyFZU/beta
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)