-
一、概述1.1 案例介绍随着软件开发规模不断扩大,开发人员在编码过程中经常遇到大量异常问题,例如 Java 空指针异常、Spring Boot 启动失败、数据库连接异常、Python 类型错误等。传统解决方式通常依赖搜索引擎查询错误信息,开发者虽然能够快速找到解决方案,但往往无法理解错误产生原因,也难以形成长期可复用的知识积累。Bug 炼金工坊(BugAlchemy) 帮助开发者从“解决 Bug”进一步升级为“理解 Bug、沉淀 Bug”,将每一次程序错误视为可学习的数据资产,通过 AI 技术完成错误信息解析、异常原因解释、修复步骤生成、薄弱知识点归因、复习卡片生成、个人知识画像分析。本案例基于华为云码道(CodeArts)代码智能体,从需求设计、系统架构规划、前后端开发到功能调试,全流程辅助构建。代码仓库:cid:link_01.2 适用对象高校学生个人开发者企业开发者1.3 案例时间本案例总时长预计 60 分钟。1.4 案例流程说明:准备本地开发环境,创建项目目录;在 CodeArts 代码智能体中输入需求,智能体自动生成前后端完整代码;安装项目依赖,遇到原生模块编译问题时由智能体自动适配解决;启动前后端服务,验证系统核心功能。1.5 资源总览资源名称规格单价(元)华为云码道(CodeArts)代码智能体专业版优惠券覆盖范围 二、环境和资源准备2.1 准备云开发环境登录华为开发者空间,点击菜单 开发平台 > 云开发环境 > 容器,创建云开发环境容器版。2.2 安装基础工具在云开发环境中确认以下工具已安装:JDK 17+(java -version 验证)Node.js 18+(node -v 验证)MySQL 8.x(mysql --version 验证)Git(git --version 验证)2.3 下载项目源码通过 git 下载源码到本地:git clone cid:link_0 三、构建 BugAlchemy 应用3.1 项目结构说明项目采用前后端分离架构,目录结构如下:BugAlchemy/├── bugalchemy-frontend/ # 前端 Vue3 项目│ ├── src/│ │ ├── api/ # API 接口层(diagnosis.js, auth.js, request.js)│ │ ├── components/ # 组件(BugInputPanel, ErrorHighlight, ReviewFlipCard, ScanProgress, WeaknessChart)│ │ ├── views/ # 页面视图(Home, DiagnosisResult, CardLibrary, KnowledgeProfile, WeeklyReport, Login)│ │ ├── router/ # Vue Router 路由配置│ │ ├── mock/ # Mock 数据│ │ ├── style.css # 全局暗色主题 CSS 变量│ │ ├── App.vue # 根组件(导航栏)│ │ └── main.js # 入口文件│ ├── vite.config.js # Vite 配置(含 API 代理)│ └── package.json├── bugalchemy-backend/ # 后端 Spring Boot 项目│ ├── src/main/java/com/bugalchemy/│ │ ├── config/ # 配置类(CorsConfig, JwtAuthConfig, GlobalExceptionHandler, DataInitializer)│ │ ├── controller/ # 控制器(Diagnosis, ReviewCard, Profile, WeeklyReport, Auth, Health)│ │ ├── dto/ # 数据传输对象│ │ ├── entity/ # 实体类│ │ ├── mapper/ # MyBatis Mapper│ │ ├── service/ # 服务接口与实现│ │ │ └── impl/ # 服务实现(含 MockAiDiagnosisService)│ │ └── utils/ # 工具类(ErrorParser, JwtUtil)│ ├── src/main/resources/│ │ ├── sql/ # 建表 SQL + 初始化数据│ │ ├── application.properties # 运行时配置(已加入 .gitignore)│ │ └── application-example.properties # 配置模板│ └── pom.xml└── deploy/ # 部署配置(Nginx + 启动脚本)3.2 使用 CodeArts 生成 PRD 文档CodeArts 生成了完整的 PRD 文档,包括项目背景、用户痛点、创新点、8 大功能模块、用户操作流程、页面结构设计、系统架构设计、数据库表设计、API 接口设计。关键设计决策:炼金隐喻:将 Bug 诊断流程包装为"炼金工坊"体验——报错是原料、诊断是提纯、修复是冶炼、知识卡片是结晶、画像和周报是沉淀bug-diagnosis-skill 可替换架构:AI 诊断能力封装为独立 Skill 接口,当前用规则引擎实现,未来可无缝替换为真实大模型五步炼金流程:原料投入 → 线索提纯 → 修复冶炼 → 知识结晶 → 复习沉淀3.3 使用 CodeArts 生成前端项目CodeArts 的输出:一次性生成了完整的前端骨架,包括:5 个页面视图 + 5 个组件vue-router 路由配置Axios 封装 + 11 个 API 接口mock 数据(所有页面的兜底数据)深色科技风 CSS 变量体系(--bg-primary、--gold-primary 等)npm run build 一次通过关键代码讲解 — 暗色主题 CSS 变量体系:src/style.css 定义了完整的暗色主题变量,炼金主题金色作为强调色贯穿全局::root { --bg-primary: #0D1117; --bg-secondary: #161B22; --text-primary: #E6EDF3; --text-secondary: #8B949E; --gold-primary: #D4A843; --gold-glow: rgba(212, 168, 67, 0.3); --diagnosis-green: #3FB950; --warning-orange: #D29922; --error-red: #F85149; --info-blue: #58A6FF;}关键代码讲解 — BugInputPanel 自动识别逻辑:src/components/BugInputPanel.vue 实现了编程语言自动识别,默认选项为"自动识别",用户也可手动选择:const detectedLang = computed(() => { const text = errorText.value.toLowerCase() if (text.includes('java.lang.') || text.includes('.java:')) return 'JAVA' if (text.includes('traceback') || text.includes('importerror')) return 'PYTHON' if (text.includes('mysql') || text.includes('sql')) return 'SQL' if (text.includes('cannot find module') || text.includes('npm err')) return 'JAVASCRIPT' if (text.includes('error cs')) return 'CSHARP' if (text.includes('segmentation fault') || text.includes('gcc')) return 'C_CPP' if (text.includes('rustc') || text.includes('cargo')) return 'RUST' if (text.includes('goroutine') || text.includes('go.mod')) return 'GO' if (text.includes('spring') || text.includes('tomcat')) return 'SPRING_BOOT' return ''})const effectiveLang = computed(() => { return selectedLang.value === 'auto' ? detectedLang.value : selectedLang.value})3.4 使用 CodeArts 生成后端项CodeArts 的输出:生成了完整的后端骨架,包括:7 个 Entity + 6 个 Mapper + 5 个 ServiceErrorParser 工具类(9 种语言报错解析)统一 Result<T> 响应格式CorsConfig + GlobalExceptionHandler + MyBatisConfig建表 SQL(7 张表)+ 初始化数据 SQLmvn compile 一次通过过程中发现的问题及 CodeArts 辅助修复:问题修复方式Spring Boot 4.x 不自动注册 ObjectMapper BeanJwtAuthConfig 中手动 new ObjectMapper()ErrorParser port 关键词大小写不匹配改为 text.toLowerCase().contains("port")DiagnosisServiceImpl.getDetail 未填充 reviewCard 字段补充 reviewCard 查询和填充,无卡片时兜底空对象init-data.sql 重复执行主键冲突改用 INSERT IGNOREElement Plus prefix-icon 不接受字符串改为 :prefix-icon="User" 组件引用关键代码讲解 — ErrorParser 多语言解析:ErrorParser 是 bug-diagnosis-skill 的规则引擎核心,通过 parse(errorText, language) 入口分发到不同语言的解析方法:public static ParseResult parse(String errorText, String language) { String detectedLang = (language != null && !language.isBlank()) ? language : detectLanguage(errorText); return switch (detectedLang.toUpperCase()) { case "JAVA" -> parseJavaError(errorText); case "SPRING_BOOT" -> parseSpringBootError(errorText); case "PYTHON" -> parsePythonError(errorText); case "SQL" -> parseSqlError(errorText); case "JAVASCRIPT" -> parseJavaScriptError(errorText); case "CSHARP" -> parseCSharpError(errorText); case "C_CPP" -> parseCppError(errorText); case "RUST" -> parseRustError(errorText); case "GO" -> parseGoError(errorText); default -> parseGenericError(errorText); };}当前已覆盖 9 种编程语言、25+ 种异常类型的结构化解析:语言异常类型JavaNullPointerException、ClassNotFoundException、ClassCastException、通用 ExceptionSpring BootPortInUseException、BeanCreationExceptionPythonModuleNotFoundError、TypeError、IndexErrorSQLTable doesn't exist、Unknown column、Duplicate entryJavaScriptCannot find module、TypeError(undefined)、ECONNREFUSEDC#CS0234(命名空间)、CS1061(成员不存在)C/C++Segmentation Fault、Undefined ReferenceRustE0425(作用域)、E0308(类型不匹配)Godeclared but not used、runtime panic关键代码讲解 — AiDiagnosisService 可替换架构:public interface AiDiagnosisService { DiagnosisResultDTO diagnose(DiagnosisRequestDTO request);}当前实现 MockAiDiagnosisService,替换为真实 AI 时只需新建实现类并切换 @Service 注解,零改动业务层和前端。3.5 使用 CodeArts 生成单元测试CodeArts 的输出:生成了 15 个测试用例,全部通过:语言测试场景JavaNullPointerException、ClassNotFoundException、ClassCastException、通用 ExceptionSpring Boot端口占用(含大小写 Port 8080)、BeanCreationExceptionPythonModuleNotFoundError、TypeError、IndexError、通用 Python 错误SQL表不存在、字段不存在、唯一键冲突、通用 SQL 错误边界空字符串、null 输入、自动语言检测Tests run: 15, Failures: 0, Errors: 0, Skipped: 0BUILD SUCCESS3.6 运行调试1)初始化数据库mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS bugalchemy DEFAULT CHARSET utf8mb4;"mysql -u root -p bugalchemy < bugalchemy-backend/src/main/resources/sql/schema.sqlmysql -u root -p bugalchemy < bugalchemy-backend/src/main/resources/sql/init-data.sql2)配置后端复制配置模板并修改数据库密码:cp bugalchemy-backend/src/main/resources/application-example.properties bugalchemy-backend/src/main/resources/application.properties编辑 application.properties,将 YOUR_PASSWORD_HERE 替换为实际 MySQL 密码。3)启动后端cd bugalchemy-backendmvn spring-boot:run4)启动前端cd bugalchemy-frontendnpm installnpm run dev5)访问应用使用演示账号登录:字段值用户名demo密码1234566)测试诊断功能粘贴以下报错文本进行测试:样例输入预期异常类型Java NPEjava.lang.NullPointerException at com.example.Service.process(Service.java:42)NullPointerExceptionSpring 端口Web server failed to start. Port 8080 was already in use.PortInUseExceptionPython 模块ModuleNotFoundError: No module named 'flask'ModuleNotFoundErrorJS 模块Error: Cannot find module 'express'ModuleNotFoundErrorRust 作用域error[E0425]: cannot find value 'count' in this scopeE0425Go 未使用./main.go:5:2: count declared but not usedUnusedDeclError 四、系统架构设计4.1 整体架构4.2 技术栈层级技术说明前端Vue3 + Vite + Element Plus + ECharts深色科技风 + 炼金元素后端Spring Boot 4.x + MyBatisRESTful API,统一 Result<T> 响应数据库MySQL 8.x7 张业务表,JSON 字段存复杂结构AI 诊断bug-diagnosis-skill (MockAiDiagnosisService)当前规则引擎兜底,可替换为真实大模型鉴权JWT (jjwt) + BCrypt注册登录 + Token 校验 + 用户数据隔离4.3 数据库设计表结构总览表名说明关键字段t_user用户表username, password_hash, nicknamet_diagnosis_record诊断记录表user_id, error_text, exception_type, statust_knowledge_point知识点表name, category, descriptiont_diagnosis_knowledge_ref诊断-知识点关联表diagnosis_id, knowledge_id, severityt_knowledge_mastery知识掌握度表user_id, knowledge_id, error_count, fix_count, mastery_scoret_review_card复习卡片表user_id, diagnosis_id, front_content, back_content, mastery_level, next_review_att_weekly_report周报复盘表user_id, week_start, week_end, total_errors, fixed_errors 五、解决方案5.1 五步炼金流程BugAlchemy 将 Bug 诊断流程包装为"炼金工坊"体验,形成完整的知识闭环:步骤炼金隐喻对应功能说明1报错原料BugInputPanel用户粘贴原始报错文本,自动识别编程语言2线索提纯ErrorParser + ErrorHighlight从报错中提取异常类型、位置、端口等关键线索并高亮3修复冶炼诊断结果页生成人话解释和可执行修复步骤4知识结晶知识点归因 + ReviewFlipCard将报错映射到知识点,生成复习卡片5复习沉淀知识画像 + 周复盘间隔重复复习,统计薄弱趋势,沉淀为长期记忆5.2 功能模块炼金台(首页)报错输入面板(BugInputPanel):支持粘贴多行报错文本,自动检测编程语言最近诊断记录:展示最近 10 条诊断历史待复习卡片提醒:显示今日到期需复习的卡片数量诊断结果原始报错高亮展示(ErrorHighlight):片段级精准高亮,支持关键词匹配和偏移量匹配异常信息:类型 + 位置 + 简短描述人话根因解释:用初学者能懂的语言 + 生活比喻修复步骤:有序列表,每步含代码示例和验证方法知识点归因:标记严重程度(HIGH / MEDIUM / LOW)复习卡片预览:自动生成的问答卡片复习卡片库卡片列表:分页展示所有复习卡片3D 翻转卡片(ReviewFlipCard):正面问题 / 背面答案标记掌握:更新 masteryLevel,影响下次复习时间间隔重复算法:masteryLevel 0→1天 / 1→3天 / 2→7天 / 3→14天薄弱知识画像雷达图:6 维知识维度(Java 核心 / Spring / SQL / Python / 异常处理 / 设计模式)薄弱知识点排行:按 errorCount 降序,展示 Top 107 天趋势图:每日诊断数 vs 复习数折线图周复盘本周诊断总数 / 修复数 / 修复率Top 5 错误类型分布及趋势最薄弱知识点及学习建议AI 学习建议摘要5.3 API 接口模块方法路径说明用户POST/api/auth/register用户注册用户POST/api/auth/login用户登录诊断POST/api/diagnosis/analyze提交报错诊断诊断GET/api/diagnosis/list获取诊断历史列表诊断GET/api/diagnosis/{id}获取诊断详情卡片GET/api/card/list获取复习卡片列表卡片PUT/api/card/{id}/mastery更新卡片掌握程度卡片GET/api/card/due获取待复习卡片画像GET/api/profile/radar获取知识维度雷达图数据画像GET/api/profile/weak-ranking获取薄弱知识点排行画像GET/api/profile/trend获取修复趋势数据周报GET/api/weekly/current获取本周复盘健康检查GET/api/health服务健康检查 六、核心技术难点与解决思路6.1 ErrorParser 多语言报错解析难点:不同编程语言的报错格式差异巨大——Java 用堆栈跟踪、Python 用 Traceback、SQL 用错误码、Rust 用 error[E0425] 格式。需要设计一个统一的解析框架,同时保持每种语言的解析精度。解决思路:先检测后分发:detectLanguage() 基于关键词自动识别语言,用户也可手动指定每种异常独立解析:NullPointerException 和 TypeError 有完全不同的修复步骤和知识点归因正则提取关键信息:如 at com.example.Service.process(Service.java:42) 提取出 Service.java:42兜底机制:每种语言都有 parseGeneric*Error 方法,确保未知异常也能给出基本诊断大小写兼容:Port 8080 和 port 8080 都能匹配6.2 ErrorHighlight 片段级精准高亮难点:后端返回的 startOffset/endOffset 偏移量可能与前端实际渲染位置不一致(尤其是换行符、空格处理差异),导致高亮位置偏移。解决思路:双模式高亮:value 模式(关键词匹配)和 offset 模式(偏移量匹配)互为补充自动模式提取:从报错文本中提取异常类型、文件行号、端口号、模块名、SQL 表名并自动高亮片段级处理:将报错文本按行拆分,逐片段匹配高亮,避免跨行偏移问题6.3 前后端字段不一致难点:后端 Java 命名习惯(topWeakPoints、frontContent)与前端期望(topErrors、front)不一致,直接导致前端 JS 报错或数据丢失。解决思路:后端 VO 层映射:WeeklyReportVO.convertToVO 中显式映射字段名前端 normalize 函数:对后端返回数据做兼容处理,确保关键字段存在且格式正确API 拦截器兜底:请求失败时回退到 mock 数据,保证页面可渲染6.4 Spring Boot 4.x 兼容性问题难点:Spring Boot 4.x 基于 Spring Framework 7,部分自动配置行为发生变化,如 ObjectMapper 不再自动注册为 Bean,导致 JwtAuthFilter 中 JSON 序列化失败。解决思路:手动实例化:在 JwtAuthConfig 中 new ObjectMapper() 替代自动注入统一异常处理:GlobalExceptionHandler 覆盖 MethodArgumentNotValidException 和 HttpMessageNotReadableException401 响应修复:JwtAuthFilter 返回 401 时 data 字段使用 EMPTY_MAP 而非字符串 "null"6.5 知识点归因与掌握度更新难点:诊断时需要自动将报错映射到知识点体系,并更新掌握度。涉及多表关联操作(知识点查找/创建、关联记录创建、掌握度更新),需要保证数据一致性。解决思路:遍历诊断结果中的 knowledgePoints,通过 knowledgePointMapper.selectByName 查找已有知识点,不存在则新建创建 DiagnosisKnowledgeRef 关联记录查找或创建 KnowledgeMastery 记录:首次出现:初始 mastery_score = 40,error_count = 1再次出现:error_count + 1,mastery_score - 5(最低为 0)复习卡片标记掌握时:fix_count + 1,mastery_score + 10(最高为 100)6.6 JWT 用户数据隔离难点:多用户场景下,每个用户只能看到自己的诊断记录、复习卡片和知识画像,需要确保数据隔离。解决思路:JwtAuthFilter 拦截所有非白名单请求,从 token 中提取 userId 存入 request attribute所有 Controller 统一 requireUserId() 方法获取 userId 并做空值检查Service 层所有查询都带 userId 条件,更新操作校验数据归属DiagnosisRequestDTO 移除 userId 字段,由 Controller 从 token 注入,防止伪造
-
CampusSpace - 校园场地智能预约管理系统应用构建案例一、概述1.1 案例介绍CampusSpace是一个基于Django框架开发的校园场地智能预约管理系统,旨在解决高校场地预约管理中的痛点问题。系统集成了智能推荐算法、冲突自动检测、批量审批等核心功能,支持教室、实验室、会议室等多种场地类型的预约管理,为校园场地资源的高效利用提供了一站式解决方案。本案例将指导开发者从零开始构建一个功能完整的校园场地预约系统,涵盖用户管理、场地管理、预约审批、智能推荐、数据统计等核心模块,并集成华为云OBS对象存储、Redis缓存等技术,提升系统性能和用户体验。1.2 适用对象高校学生(学习Web开发、系统设计)个人开发者(构建校园应用)企业开发者(了解Django框架、华为云服务集成)1.3 案例时间本案例总时长预计90分钟,包括环境准备(15分钟)、项目构建(50分钟)、测试验证(25分钟)。1.4 案例流程说明:本地安装华为云码道(CodeArts)代码智能体;通过码道开发校园场地智能预约管理系统,并在浏览器中体验。1.5 资源总览本案例预计花费0元(使用免费资源和开发环境)。体验完成后请及时释放资源,避免产生多余的费用。资源名称规格单价(元)华为云对象存储服务OBS标准存储 5GB免费(按需付费)Redis缓存服务本地Redis或云服务免费华为云码道(CodeArts)代码智能体体验版(专业版)免费(按需付费)二、环境和资源准备2.1 下载安装CodeArts代码智能体参考《AI IDE华为云码道(CodeArts)代码智能体安装部署》,下载安装IDE:2.2 开通华为云码道体验版访问专属开通链接,免费开通华为云码道(CodeArts)代码智能体体验版:2.2 登录CodeArts代码智能体安装完成之后,点击打开文件夹或新建项目,用于存放项目文件:登录CodeArts代码智能体:注意:如果已经登录华为账号,直接跳转至登录授权页面,否则,直接拉起华为账号登录界面。自动拉起华为账号登录界面,输入账号和密码:跳转至登录授权页面,点击确认授权:CodeArts代码智能体登录成功:登录成功之后,返回CodeArts代码智能体,即可体验使用。三、通过码道分阶段搭建校园场地智能预约管理系统3.1 需求规格设计在码道对话框输入以下提示词,让码道进行需求规格说明书的创建:你是一名资深产品经理、Django架构师和测试工程师。 我要开发 CampusSpace 智约——校园教室与会议室智能预约及冲突优化平台。 项目目标: 学生或教师输入使用时间、人数和设备需求,系统自动推荐合适场地; 支持固定课表、维修停用、临时预约、冲突检测、审批、取消和利用率统计。 用户角色: 1. 普通用户:查询场地、获取推荐、提交预约、查看和取消预约。 2. 管理员:管理场地、固定课表、维修时间、审批预约和查看统计。 技术栈: Python、Django、Bootstrap、FullCalendar、Chart.js。 本地使用SQLite,部署时可切换MySQL或PostgreSQL。 现在先不要写代码,请依次输出: 1. 需求规格说明书 2. 功能优先级P0/P1/P2 3. 用户故事与验收标准 4. 页面清单 5. 数据表设计 6. 预约状态机 7. 冲突检测规则 8. 推荐算法 9. 项目目录结构 10. 分阶段开发任务清单 请将结果分别保存到docs目录中的Markdown文件。 存在模糊或矛盾的业务规则时先列出问题,不要自行假设。此时,码道会根据步骤创建多个开发文档。这时,我们查看00-业务规则待确认问题.md,并确认业务规则,将修改后的文件发送给码道,码道会根据业务规则完善关键文档。3.2 生成项目结构和配置文件在码道对话框输入以下提示词,让码道生成项目结构和配置文件:请根据docs目录中的需求和设计文件,创建Django项目骨架。 本阶段只完成: 1. 项目初始化 2. 用户登录与退出 3. 管理员和普通用户权限 4. Building与Room数据模型 5. Django管理后台 6. 基础导航和首页 7. 初始化演示数据命令 8. README启动说明3.3 安装Python开发环境步骤1:安装Python 3.9访问Python官网下载并安装Python 3.9版本,确保pip包管理工具可用。步骤2:创建虚拟环境在项目目录下创建虚拟环境,隔离项目依赖:# Windows python -m venv CampusSpace CampusSpace\Scripts\activate # Linux/Mac python3 -m venv CampusSpace source CampusSpace/bin/activate步骤3:安装Django框架安装Django 3.2 LTS版本及项目依赖:pip install Django==3.2.* pip install django-crispy-forms==1.14.0 pip install crispy-bootstrap5==0.7 pip install Pillow==9.5.* pip install python-dateutil==2.8.*3.4 完善核心功能模块在码道对话框输入以下提示词,继续完善核心功能模块。接下来请根据docs目录中的需求和设计文件,实现: 1. 教学楼管理 2. 教室管理 3. 设备条件 4. 固定占用 5. 维修停用3.5 结合Skill实现预约系统3.5.1 实现预约功能在码道对话框输入以下提示词,继续完善核心功能模块。由于预约功能涉及规则较多,需要进行确认后再实现。请调用booking-domain Skill,实现预约核心功能。 要求: 1. 用户选择场地、日期、开始时间和结束时间。 2. 校验开始时间早于结束时间。 3. 校验人数不超过场地容量。 4. 校验设备要求。 5. 校验固定课表冲突。 6. 校验维修时间冲突。 7. 校验已批准预约冲突。 8. 校验同一用户的时间冲突。 9. 冲突时不写入预约数据。 10. 返回明确的冲突原因。 11. 无冲突时创建待审批预约。 12. 为所有规则编写单元测试。 请先说明实现方案和涉及文件,等待我确认后再修改代码。确认所有问题后,码道会根据完整的冲突检测规则实现预约功能。3.5.2 实现场地推荐功能在码道对话框输入以下提示词,加入场地推荐功能。在现有预约系统中实现可解释的场地推荐。 推荐流程: 1. 根据时间冲突、容量、设备和场地状态进行硬性筛选。 2. 对剩余场地计算100分推荐分。 3. 容量匹配35分、设备匹配25分、建筑偏好15分、 空闲连续性15分、节能匹配10分。 4. 返回排名前三的场地。 5. 每个结果必须提供推荐理由和扣分原因。 6. 无合适场地时,推荐最近的可用时间或替代场地。 7. 推荐逻辑与视图层分离。 8. 为排序、并列和无结果场景编写测试。3.5.3 实现审批流程和日历视图功能在码道对话框输入以下提示词,实现审批流程和日历视图功能。实现: - 管理员审批列表 - 同意和拒绝 - 拒绝原因 - 用户个人预约 - 取消预约 - 周日历和月日历 - 不同状态颜色显示3.5.4 实现统计分析功能在码道对话框输入以下提示词,采用服务层设计模式,实现统计分析功能。先生成 SQL/ORM 设计,再生成图表接口,避免统计逻辑散落在页面里。 并验证: - 没有数据时页面不报错 - 只有一条数据时图表正常 - 已取消预约不计入有效利用率 - 固定课表和临时预约是否分别统计3.6 添加华为云OBS和Redis集成3.6.1 配置华为云OBS服务步骤1:开通华为云OBS服务登录华为云控制台,开通对象存储服务OBS。步骤2:创建OBS桶在OBS控制台创建存储桶,用于存储系统上传的文件:桶名称:campusspace-files区域:华北-北京四存储类别:标准存储桶访问权限:公共读步骤3:获取访问密钥在"我的凭证"页面获取访问密钥(AK/SK),用于程序访问OBS服务:步骤4:安装OBS SDK安装华为云OBS Python SDK:pip install esdk-obs-python步骤5:配置环境变量设置OBS访问密钥环境变量:# Windows (PowerShell) $env:HUAWEI_ACCESS_KEY="你的AK" $env:HUAWEI_SECRET_KEY="你的SK" # Linux/Mac export HUAWEI_ACCESS_KEY="你的AK" export HUAWEI_SECRET_KEY="你的SK" 3.6.2 安装Redis缓存服务步骤1:安装RedisWindows用户下载Redis Windows版本,Linux/Mac用户使用包管理器安装:# Ubuntu/Debian sudo apt-get install redis-server # CentOS/RHEL sudo yum install redis # Mac brew install redis步骤2:启动Redis服务# Windows redis-server.exe # Linux/Mac redis-server步骤3:安装Django Redispip install django-redis==5.4.0 pip install redis==5.0.13.6.3 配置项目设置编辑config/settings.py,配置项目设置:# 华为云OBS配置 HUAWEI_OBS_CONFIG = { 'access_key': os.environ.get('HUAWEI_ACCESS_KEY'), 'secret_key': os.environ.get('HUAWEI_SECRET_KEY'), 'server': 'obs.cn-north-4.myhuaweicloud.com', 'bucket_name': 'campusspace-files' } # Redis缓存配置(开发环境使用本地内存) if DEBUG: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } else: CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', }, 'KEY_PREFIX': 'campusspace', 'TIMEOUT': 300, } } 3.6.4 实现华为云 OBS 与 Redis 缓存功能在码道对话框输入以下提示词,实现技术提升。添加以下重要功能和技术提升: 使用华为云 OBS 添加 Redis 缓存 创建 API 文档四、启动项目并反馈可能出现的问题在编码任务完成后,根据启动说明启动项目,检查是否正常运行。注意:在启动项目中或项目运行中可能会出现一些错误,遇到问题的时候我们通过自然语言描述或者截图的方式把错误直接反馈给码道,让码道帮我们解决就可以了。也可以按照个人习惯增加其他的功能,比如批量审批、数据导出、站内消息等,让系统更加完善。注意:由于本应用是由AI创建,每次创建的结果可能不一致,如果想体验上图中的案例,可在本项目源码处下载并体验。五、反馈改进建议如您在案例实操过程中遇到问题或有改进建议,可以到开发者论坛评论区反馈,我们会及时响应处理,谢谢!六、附录项目地址 https://github.com/NanfengCC66/CampusSpace演示视频 https://github.com/NanfengCC66/CampusSpace/blob/main/演示视频.mp4
-
套餐内额度太少了,一个开发者深度使用一天可能就是大几千万的tokens,能不能增加一下套餐内的配额
-
代码及视频demo所在仓库地址:https://gitcode.com/W20402001/madao基于华为云码道(CodeArts)的应用构建案例——校园场地智能预约系统一、概述1.1 案例介绍本案例采用华为云码道(CodeArts)代码智能体作为核心开发工具,从零构建一个校园场地智能预约管理系统。系统面向普通用户(USER)、管理员(ADMIN)、审批员(APPROVER)三类角色,实现了场地信息管理、智能查询推荐、预约申请与审批、时间冲突检测、签到核验、运营数据统计等核心功能。开发过程采用"V1快速原型→V2功能完善→V3界面升级"的三轮迭代模式,全程使用码道代码智能体辅助开发,充分展示了码道在项目骨架生成、代码续写、智能问答、系统性界面重构等场景下的实践应用。1.2 案例流程环境准备 → V1快速原型 → V2功能完善 → V3界面升级说明:环境准备:安装华为云码道(CodeArts)代码智能体、Node.js、JDK 17;V1快速原型:使用码道生成项目骨架,完成核心数据模型+基础CRUD+简单场地查询;V2功能完善:实现基于内存Token的三角色认证、智能推荐算法、时间冲突检测、预约状态机、审批流程、运营统计;V3界面升级:使用码道系统性重构前端界面,建立设计系统,统一布局与组件,优化交互体验。1.3 资源总览资源名称规格单价(元)华为云码道(CodeArts)代码智能体专业版代金券支付Node.jsv24.18.0(本案例实测)免费JDK17免费Apache Maven3.9.16(本案例实测)免费二、环境和资源准备2.1 安装华为云码道(CodeArts)代码智能体访问华为云码道下载页面,下载并安装码道代码智能体。安装完成后,使用华为云账号登录。2.2 安装Node.js开发环境访问Node.js官网下载并安装兼容版本。本案例实际使用Node.js v24.18.0、npm 11.16.0。安装完成后,在终端验证:node --version npm --version 2.3 安装JDK 17访问Oracle或Adoptium官网下载并安装JDK 17。安装完成后验证:java -version javac -version 本案例实际使用Eclipse Adoptium Temurin 17.0.19。2.4 安装Apache Maven后端使用Maven构建。本案例实际使用Apache Maven 3.9.16。安装后验证:mvn -version 2.5 完成码道CodeArts实战速成考试访问码道CodeArts实战速成考试,完成在线学习并通过考试,获取通过证书。2.6 本案例实际验证环境项目实际环境操作系统Windows 11 x64JDKEclipse Adoptium Temurin 17.0.19MavenApache Maven 3.9.16Node.jsv24.18.0npm11.16.0后端端口8080前端开发端口5173前端API代理/api → http://localhost:8080说明:开发工具需要在系统环境变量更新后重新启动,才能继承最新的JAVA_HOME和PATH。本案例验证时JDK安装于D:\tools\jdk17,Maven安装于D:\tools\maven\apache-maven-3.9.16,Node.js安装于D:\tools\nodejs。三、系统架构设计3.1 技术选型层级技术栈说明前端框架Vue 3 + TypeScript + Vite响应式组合式API,类型安全UI组件库Element Plus企业级Vue3组件库状态管理Pinia轻量级状态管理路由Vue Router 4支持路由守卫与角色权限HTTP客户端Axios请求拦截、Token注入后端框架Spring Boot 3.2.5Java 17,Maven构建认证方案内存Token + 拦截器Bearer Token仅保存在进程内存中,不是JWT数据存储内存RepositoryConcurrentHashMap模拟持久化定时任务Spring @Scheduled预约状态自动流转3.2 系统架构图3.3 数据模型设计系统核心数据模型如下:UserAccount (用户) ├── id, username, password, realName ├── role: USER | ADMIN | APPROVER └── status: ACTIVE | DISABLED Venue (场地) ├── id, code, name, building ├── type: CLASSROOM | MEETING_ROOM | SPORTS_FIELD ├── capacity, equipment[], openTime, closeTime └── status: ACTIVE | INACTIVE Booking (预约) ├── id, bookingNo, applicantId, venueId ├── purpose, attendeeCount, requiredEquipment[] ├── startAt, endAt ├── status: PENDING_APPROVAL → APPROVED → CHECKED_IN → COMPLETED │ ↘ REJECTED ↘ CANCELLED ↘ EXPIRED └── checkInCode, createdAt, updatedAt FixedSchedule (固定排课) ├── id, venueId, validFrom, validTo ├── dayOfWeek, startTime, endTime └── description ApprovalRecord (审批记录) ├── id, bookingId, approverId ├── action: APPROVE | REJECT └── comment, operatedAt3.4 预约状态流转3.5 项目结构campus-venue-booking/ ├── server/ # 后端(Spring Boot) │ ├── pom.xml # Maven依赖配置 │ └── src/main/java/com/example/venuebooking/ │ ├── VenueBookingApplication.java # 应用入口(@EnableScheduling) │ ├── config/ │ │ ├── WebConfig.java # 跨域+拦截器注册 │ │ └── MockDataInitializer.java # 初始化示例数据 │ ├── auth/ │ │ ├── AuthService.java # 登录/注册/Token管理 │ │ ├── AuthController.java # 认证接口 │ │ ├── AuthInterceptor.java # Token校验+当前用户注入 │ │ ├── SkipAuth.java # 跳过鉴权注解 │ │ └── CurrentUser.java # 当前用户注解 │ ├── venue/ │ │ ├── VenueController.java # 场地查询接口 │ │ ├── VenueService.java # 场地查询+日程 │ │ ├── VenueAdminController.java# 管理端场地接口 │ │ └── VenueAdminService.java # 场地CRUD+状态管理 │ ├── booking/ │ │ ├── BookingController.java # 预约接口 │ │ ├── BookingService.java # 预约核心逻辑 │ │ └── BookingStatusScheduler.java # 定时状态流转 │ ├── approval/ │ │ ├── ApprovalController.java # 审批接口 │ │ └── ApprovalService.java # 审批逻辑+签到码 │ ├── schedule/ │ │ ├── FixedScheduleController.java # 固定排课接口 │ │ └── FixedScheduleService.java # 固定排课管理 │ ├── statistics/ │ │ ├── StatisticsController.java # 统计接口 │ │ └── StatisticsService.java # 统计计算 │ ├── algorithm/ │ │ ├── VenueRecommendationService.java # 智能推荐算法 │ │ ├── VenueAvailabilityService.java # 可用性检测 │ │ ├── TimeConflictChecker.java # 时间冲突检测 │ │ ├── RecommendationScore.java # 推荐评分模型 │ │ └── ConflictResult.java # 冲突结果模型 │ ├── domain/ # 领域模型 │ │ ├── Venue.java, Booking.java, UserAccount.java │ │ ├── FixedSchedule.java, ApprovalRecord.java │ │ └── enums/ (BookingStatus, VenueType, UserRole等) │ ├── repository/ # 数据仓储接口 │ │ ├── VenueRepository.java, BookingRepository.java │ │ ├── UserRepository.java, FixedScheduleRepository.java │ │ ├── ApprovalRecordRepository.java │ │ └── memory/ # 内存实现 │ └── common/ # 通用组件 │ ├── ApiResponse.java, BusinessException.java │ ├── ErrorCode.java, GlobalExceptionHandler.java │ └── CustomErrorController.java │ └── web/ # 前端(Vue3 + Vite) ├── package.json ├── vite.config.ts # Vite配置+API代理 └── src/ ├── main.ts # Vue3入口 ├── App.vue # 根组件(路由过渡) ├── styles/ │ ├── variables.css # CSS设计变量系统 │ └── global.css # 全局通用样式 ├── types/index.ts # TypeScript类型定义 ├── utils/ │ ├── request.ts # Axios封装+Bearer Token注入 │ └── format.ts # 公共格式化函数 ├── composables/useClock.ts # 时钟Hook ├── api/ │ ├── auth.ts # 认证接口 │ ├── venue.ts # 场地接口 │ ├── booking.ts # 预约接口 │ ├── approval.ts # 审批接口 │ └── admin.ts # 管理端接口 ├── stores/user.ts # Pinia用户状态 ├── router/index.ts # 路由+权限守卫 ├── components/ # 公共组件 │ ├── AppLayout.vue # 统一布局(侧边栏+顶栏+内容) │ ├── AppPageHeader.vue # 页面标题 │ ├── AppStatCard.vue # 统计卡片 │ ├── AppSectionCard.vue # 内容卡片 │ ├── AppFilterBar.vue # 筛选栏 │ ├── BookingStatusTag.vue # 预约状态标签 │ ├── VenueTypeTag.vue # 场地类型标签 │ ├── VenueCard.vue # 场地卡片 │ ├── VenueTimeBar.vue # 时间条(可拖拽选择) │ ├── EmptyState.vue # 空状态 │ └── UserAvatarMenu.vue # 用户头像菜单 ├── layouts/ # 角色布局 │ ├── UserLayout.vue │ ├── AdminLayout.vue │ └── ApproverLayout.vue └── views/ # 页面视图 ├── Login.vue # 登录/注册 ├── 403.vue, 404.vue # 错误页 ├── user/ # 用户端6个页面 ├── admin/ # 管理端5个页面 └── approver/ # 审批端2个页面四、使用华为云码道(CodeArts)代码智能体辅助完成代码开发及调试4.1 V1快速原型:搭建核心链路4.1.1 使用码道创建项目骨架在码道IDE中,打开终端,创建项目目录并初始化后端:mkdir campus-venue-booking && cd campus-venue-booking mkdir server web cd server使用码道智能问答功能,输入项目要求文件和提示词:“我们要完成一个网页端的校园场地只能预约系统,这是初步的设计文件。首先请根据文件规划代码开发顺序,我们将逐步完成整个项目的代码开发工作。”码道会自动生成初步实现代码,我们在此基础上进行修改和完善。4.1.2 V1交付标准V1阶段实现:场地增删改查、用户登录注册、基础预约创建、简单场地列表查询。4.1.3 V1验证# 后端 cd server && mvn spring-boot:run # 前端 cd web && npm install && npm run dev4.2 V2功能完善:满足全部硬性要求4.2.1 内存Token三角色认证与权限隔离使用码道代码续写功能,快速实现认证中间件。系统定义三类角色:角色说明功能范围USER普通用户预约申请、我的预约、查询推荐ADMIN管理员场地管理、固定排课、用户管理、统计看板APPROVER审批员待审批列表、审批操作、审批历史后端通过AuthInterceptor拦截请求、校验Token并注入当前用户;具体角色权限由各Controller校验;前端通过路由守卫实现页面级权限控制:4.2.2 智能推荐算法使用码道辅助设计多维度加权推荐算法,核心评分逻辑如下:// 容量得分(满分50分):人数越接近容量得分越高 capacityScore = 50.0 * attendeeCount / venue.getCapacity(); // 楼宇偏好得分(满分20分):匹配偏好楼宇得20分 buildingScore = preferredBuilding.equals(venue.getBuilding()) ? 20.0 : 0.0; // 设备匹配得分(满分15分):按可选设备匹配比例 equipmentScore = 15.0 * matchedCount / totalOptionalCount; // 负载均衡得分(满分15分):利用率越低得分越高 balanceScore = 15.0 * (1 - utilization); totalScore = capacityScore + buildingScore + equipmentScore + balanceScore; 排序规则:按总分降序 → 容量升序(小场地优先) → 场地编码升序。4.2.3 时间冲突检测实现双层冲突检测:固定排课冲突 + 预约冲突:@Component public class TimeConflictChecker { public boolean overlaps(LocalDateTime startA, LocalDateTime endA, LocalDateTime startB, LocalDateTime endB) { return startA.isBefore(endB) && startB.isBefore(endA); } } 场地可用性判断需满足7个条件:状态ACTIVE、类型匹配、容量足够、设备齐全、在开放时间内、无固定排课冲突、无预约冲突。4.2.4 预约状态机与定时调度预约7种状态通过BookingStatusScheduler每60秒自动流转:@Scheduled(fixedRate = 60000) public void updateStatuses() { // APPROVED且开始时间已过15分钟 → EXPIRED(爽约) // CHECKED_IN且已过结束时间 → COMPLETED(已完成) } 4.2.5 V2验证V2交付标准:三角色内存Token认证与权限隔离、多维度智能推荐算法、时间冲突检测、预约7状态流转、审批流程+签到码、运营统计接口。4.3 V3界面升级:系统性前端重构V3阶段是本案例的核心亮点,使用码道代码智能体完成了一次系统性的前端界面升级。这一阶段充分展示了码道在大型重构任务中的能力——理解完整代码库、制定分阶段方案、逐模块实施并保证类型安全。4.3.1 使用码道进行全量代码分析向码道输入以下提示词:“请阅读 campus-venue-booking/web 下的全部前端代码,在保持现有业务功能、接口协议、路由结构和权限体系不变的前提下,对’校园场地智能预约系统’进行系统性的前端界面升级。”码道自动完成了以下分析工作:完整读取所有源文件:30+个Vue组件、5个API模块、类型定义、路由配置、Store、工具函数识别现有问题:统计值硬编码为0、大量行内样式、类型缺失、API封装不完整、状态映射重复等多项问题输出实施方案:设计系统定义、公共组件规划、修改文件清单、6阶段实施顺序4.3.2 建立设计系统使用码道一次性生成CSS设计变量体系,定义了完整的视觉规范::root { --color-primary: #2563EB; /* 主色:校园科技蓝 */ --color-nav-bg: #172B4D; /* 深色导航 */ --color-accent: #14B8A6; /* 智能推荐强调色 */ --color-page-bg: #F5F7FA; /* 页面背景 */ --radius-md: 10px; /* 卡片圆角 */ --shadow-sm: 0 1px 2px rgba(0,0,0,0.05); /* 轻盈阴影 */ --sidebar-width: 240px; /* 侧边栏宽度 */ --transition-normal: 250ms; /* 过渡时长 */ } 4.3.3 提取11个公共组件通过码道代码续写功能,将各页面重复的样式和逻辑提取为11个可复用组件:组件用途替代的重复代码AppLayout统一三角色布局3套Layout的重复侧边栏+顶栏代码AppPageHeader页面标题+操作区每个页面重复的标题区AppStatCard统计数字卡片Dashboard和Home的统计卡片AppSectionCard统一内容卡片页面内容区的卡片标题与间距BookingStatusTag预约状态标签5个页面重复的statusTagType函数VenueTypeTag场地类型标签3个页面重复的类型映射AppFilterBar搜索筛选区域各列表页重复的筛选区样式VenueCard场地卡片场地列表的表格行样式VenueTimeBar场地日程与时段选择可视化占用时段并拖拽选择预约时间EmptyState无数据状态分散在各页面的空数据判断UserAvatarMenu用户头像菜单3套Layout重复的退出按钮同时将日期格式化、状态映射等公共逻辑提取到utils/format.ts,消除了6处重复的statusTagType、formatTime、venueTypeMap定义。4.3.4 统一三套Layout为AppLayout原来三套Layout存在大量重复代码,重构为一个AppLayout组件并通过props区分角色菜单;当前三个角色Layout文件分别约12~15行:<!-- UserLayout.vue - 当前约15行 --> <template> <AppLayout :menu-items="menuItems" role-key="USER" /> </template> <script setup lang="ts"> import AppLayout from '@/components/AppLayout.vue' const menuItems = [ { path: '/user/home', label: '首页', icon: 'HomeFilled' }, { path: '/user/venues', label: '场地列表', icon: 'OfficeBuilding' }, // ... ] </script> AppLayout实现了:侧边栏折叠/展开、窄屏抽屉式导航、面包屑导航、用户头像下拉菜单(含退出确认)。4.3.5 登录页重新设计使用码道生成左右分栏品牌登录页:左侧:深蓝渐变背景,展示系统名称、副标题和4个功能特性(智能查询推荐、冲突自动检测、便捷审批流程、数据统计分析)右侧:简洁登录/注册表单,演示账号卡片式快捷入口响应式:768px以下隐藏左侧装饰区4.3.6 用户首页接入真实数据原来首页三个统计卡片值硬编码为0,重构后从getMyBookings接口获取真实数据计算:const todayCount = computed(() => bookings.value.filter(b => b.startAt.startsWith(todayStr)).length) const pendingCount = computed(() => bookings.value.filter(b => b.status === 'PENDING_APPROVAL').length) const approvedCount = computed(() => bookings.value.filter(b => b.status === 'APPROVED').length) 新增欢迎区域(含问候语、快捷操作入口)和近期预约列表。4.3.7 查询推荐页评分可视化推荐结果从简单表格升级为卡片式布局,每个推荐卡片包含:排名标识(前3名使用强调色)分项评分进度条(容量/楼宇/设备/均衡四维度)推荐理由标签"立即预约"操作按钮4.3.8 管理端统计看板使用纯CSS柱状图替代固定比例模拟数据,基于接口返回数据动态计算:<div class="bar-fill" :style="{ width: maxDaily > 0 ? (item.count / maxDaily * 100) + '%' : '0%' }"></div> 新增"运营概览"和"需要关注"区域,展示开放场地占比、审批率和待处理事项。当前页面中开放场地占比的标签仍写作“场地利用率”,语义边界见“7.4 当前实现边界”。4.3.10 V3验证npm run build:执行vue-tsc -b && vite build,0错误通过2026-07-23复核构建:转换1730个模块,约0.73秒完成构建存在单个产物超过500 kB的性能提示,但不影响构建成功三角色路由、核心页面及主要接口均已实现当前已知接口和交互边界见“7.4 当前实现边界”4.4 码道核心使用场景总结使用场景具体操作效果项目骨架生成输入技术栈和模型描述,自动生成项目结构减少重复的初始化工作代码续写编写组件开头,码道自动补全模板和逻辑提高常规组件编码效率智能问答询问算法设计、状态机实现方案快速获得最佳实践全量代码分析一次读取30+文件,识别多项问题快速形成结构化改造清单系统性重构6阶段分步实施,保持核心业务与路由结构不变当前版本可通过前端构建类型修复识别未使用导入和缺失类型当前npm run build通过五、解决方案5.1 前端设计系统方案建立以CSS变量为核心的设计系统,覆盖颜色、圆角、阴影、间距、字体、动画6个维度,共定义40+个设计Token。所有组件和页面统一引用变量,实现全局一致的视觉风格。配色方案:用途色值说明主色#2563EB校园科技蓝,按钮/链接/激活态深色导航#172B4D侧边栏背景智能推荐#14B8A6推荐结果强调色页面背景#F5F7FA内容区背景卡片背景#FFFFFF卡片/弹窗背景主文字#1F2937标题/正文次要文字#64748B说明/辅助边框#E2E8F0分割线/卡片边框5.2 公共组件复用方案通过提取11个公共组件,并将状态、角色、设备及日期格式化逻辑集中到utils/format.ts,减少了各页面的重复实现和行内样式。组件设计遵循单一职责原则,并通过Props定义明确的接口类型。5.3 统一布局方案三套Layout合并为AppLayout组件,通过menuItems和roleKey两个props区分角色。布局支持:侧边栏折叠/展开(桌面端)抽屉式导航(移动端 < 768px)面包屑导航(自动根据路由生成)用户头像下拉菜单(含退出确认)5.4 智能推荐可视化方案推荐结果使用卡片式布局,每个推荐项包含分项评分进度条,直观展示容量、楼宇、设备、均衡四维匹配情况。推荐场地与普通可用场地区分展示,推荐区域使用强调色标识。5.5 管理端数据看板方案统计看板使用纯CSS实现轻量柱状图,不引入额外图表库。基于接口返回数据动态计算最大值和百分比,通过CSS过渡动画实现数据加载效果。新增"运营概览"和"需要关注"区域,帮助管理员快速掌握系统运营状态。其中当前“场地利用率”数值实际为开放场地数除以场地总数。六、核心技术难点与解决思路6.1 难点一:多维度场地推荐算法设计问题:如何从多个维度(容量、楼宇、设备、利用率)综合评估场地适配度,给出合理的推荐排序?解决思路:采用加权评分法,为每个维度分配权重:容量得分(50分):参会人数与场地容量的比值,鼓励选择刚好够用的场地,避免浪费楼宇偏好(20分):用户指定偏好楼宇时精确匹配设备匹配(15分):按可选设备匹配比例计算,支持部分匹配负载均衡(15分):利用率越低得分越高,鼓励分散使用场地排序时先按总分降序,同分时优先推荐小容量场地(更匹配需求),再按场地编码排序保证稳定性。6.2 难点二:双层时间冲突检测问题:场地预约需要同时检测固定排课冲突和已有预约冲突,且需考虑预约状态(仅PENDING_APPROVAL、APPROVED、CHECKED_IN状态占用场地)。解决思路:实现VenueAvailabilityService统一封装可用性判断,内部分两步检测:固定排课冲突:根据场地ID和预约时间,查询该场地在对应星期是否有固定排课与预约时段重叠预约冲突:查询该场地所有占用状态(PENDING_APPROVAL、APPROVED、CHECKED_IN)的预约,逐一判断时间重叠底层使用TimeConflictChecker的区间重叠算法:startA < endB && startB < endA。6.3 难点三:预约并发控制问题:多个用户可能同时预约同一场地的同一时段,如何防止超卖?解决思路:采用双重防护:请求幂等:前端提交时携带requestId(时间戳),后端使用ConcurrentHashMap.newKeySet()记录已处理的requestId,防止重复提交进程内互斥锁:BookingService使用synchronized(bookingLock)对创建预约操作加锁,确保当前应用进程内同一时刻只有一个预约创建请求进入临界区。这不是数据库乐观锁,也不适用于多实例部署private final Object bookingLock = new Object(); private final Set<String> processedRequestIds = ConcurrentHashMap.newKeySet(); public Booking createBooking(...) { if (requestId != null && !processedRequestIds.add(requestId)) { throw new BusinessException(ErrorCode.DUPLICATE_REQUEST); } synchronized (bookingLock) { // 在临界区内检查固定占用冲突和预约冲突 // 创建预约 } } 七、系统测试7.1 演示账号角色用户名密码功能范围普通用户user01123456首页、场地列表、查询推荐、预约申请、我的预约管理员admin01123456统计看板、场地管理、固定排课、全部预约、用户管理审批员approver01123456待审批、审批历史7.2 功能测试用例以下用例为人工验收清单,不等同于项目现有的自动化测试数量。编号测试功能操作步骤预期结果T01用户登录输入user01/123456,点击登录跳转到用户首页,显示欢迎信息和今日预约统计T02场地列表点击"场地列表"显示10个场地卡片,支持按名称/类型/状态筛选T03查看日程在场地卡片点击"查看日程"展开VenueTimeBar时间条,可拖拽选择时段T04智能查询点击"查询推荐",设置条件后查询显示推荐卡片,含分项评分进度条和推荐理由T05预约申请选择场地,填写信息,提交提交成功,跳转到我的预约列表T06取消预约在我的预约中点击取消确认后预约状态变为已取消T07审批员登录输入approver01/123456跳转到待审批页面,显示待审批列表T08批准预约点击批准,填写审批意见预约状态变为已批准,生成签到码T09驳回预约点击驳回,输入原因预约状态变为已驳回T10管理员登录输入admin01/123456跳转到统计看板,显示6个统计卡片和柱状图T11场地管理新增/编辑/停用场地操作成功,列表刷新T12固定排课新增固定占用,选择场地下拉保存成功,星期显示中文T13用户管理停用/启用用户操作成功,不能操作自己的账号T14角色权限隔离用户账号尝试访问/admin路径跳转到/403,无权限页面不可访问T15响应式适配缩小浏览器窗口至768px以下侧边栏变为抽屉式导航,表格可横向滚动T16用户注册在登录页切换到注册页并提交合法信息注册成功、自动登录并按所选角色跳转7.4 当前实现边界注册角色由前端选择:当前注册接口允许传入USER、ADMIN或APPROVER。这便于案例演示,但生产系统应限制公开注册只能创建普通用户,并由管理员分配高权限角色。固定占用编辑尚未接入前端:后端已提供PUT /api/admin/fixed-schedules/{id},当前管理页面只实现查询、新增和删除。统计看板跨角色入口需调整:“需要关注”区域中的“去审批”按钮跳转到/approver/pending,但管理员角色会被前端路由守卫转到/403。数据存储为内存实现:所有场地、预约、用户、固定占用和审批记录均保存在ConcurrentHashMap中,适合案例演示,不提供跨重启持久化和多实例一致性。推荐设备偏好尚未完全暴露到界面:后端推荐模型支持optionalEquipment并据此计算设备匹配分,当前查询页只提交requiredEquipment,未提供可选设备偏好的独立输入项。八、扩展资料说明华为云码道(CodeArts)代码智能体:https://codearts.huaweicloud.comVue3官方文档:https://cn.vuejs.orgElement Plus组件库:https://element-plus.org/zh-CNSpring Boot官方文档:https://spring.io/projects/spring-bootTypeScript官方文档:https://www.typescriptlang.orgVite构建工具:https://cn.vitejs.devPinia状态管理:https://pinia.vuejs.org/zh代码及视频demo所在仓库地址:https://gitcode.com/W20402001/madao
-
基于华为云码道(CodeArts)代码智能体的运动场馆管理系统1、案例介绍1.1 案例介绍校园运动场馆资源分散、借用流程混乱、时间冲突频发?本案例基于 Vue3 + Express 全栈架构,借助华为云码道(CodeArts)代码智能体,从零到一构建一套运动场馆智能管理系统。系统支持课表固定占用与临时借用双模式,提供基于时间段的可用场馆智能推荐,实现场馆资源的高效调度与零冲突管理。1.2 适用对象高校学生个人开发者企业开发者1.3 案例时间本案例总时长预计60分钟。1.4 案例流程┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │ 1 本地环 │───▶│ 2.CodeArts智能体 │───▶│ 3.依赖安装与 │───▶│ 4.运行调试 │ │ 境准备 │ │ 生成代码 │ │ 环境适配 │ │ 与验证 │ └──────────────┘ └──────────────────┘ └──────────────────┘ └──────────────┘说明:准备本地开发环境,安装 Node.js,创建项目目录;在 CodeArts 代码智能体中输入需求,智能体自动生成前后端完整代码;安装项目依赖,遇到原生模块编译问题时由智能体自动适配解决;启动前后端服务,验证系统核心功能。1.5 资源总览本案例预计花费0元。资源名称规格单价(元)华为云码道(CodeArts)代码智能体通用体验版免费Node.jsv20+免费2、环境和资源准备2.1 安装 Node.js本案例前端和后端均基于 Node.js 运行,需提前安装 Node.js v20 及以上版本。下载地址:https://nodejs.org/安装完成后,在终端验证:node --version npm --version 2.2 开通华为云码道(CodeArts)代码智能体登录华为云控制台,搜索"CodeArts",进入 CodeArts 服务页面,开通代码智能体通用体验版(免费)。开通后即可在 CodeArts IDE 中使用 AI 辅助编程功能。3、构建运动场馆管理系统3.1 创建项目目录在终端中创建项目目录:mkdir yundongchangguan cd yundongchangguan3.2 使用 CodeArts 代码智能体生成项目代码在 CodeArts IDE 中打开代码智能体对话窗口,输入需求:实现这个运动场馆管理系统:某学校有各类运动场馆若干,包括足球场、篮球场、羽毛球场等。运动场馆可以按照课表设置为一段时间固定时间占用,非固定占用时间可以临时借用。借运动场馆时可根据时间段需求系统提供可用场地推荐,也可通过场馆列表挑选借用;实现借用情况查询、取消借用功能。CodeArts 代码智能体将自动完成以下工作:识别任务复杂度,创建多步骤 Todo 清单进行任务管理一次性生成完整项目结构,包括 package.json、vite.config.js、index.html、路由配置、API封装自动选择技术栈:Vue3 + Element Plus + Express + SQLite,无需人工指定生成数据库模型与种子数据,预置10个场馆和12条课表,开箱即用实现所有 API 接口,含时间冲突检测与智能推荐逻辑生成前端页面组件,包括登录页、数据看板、场馆管理、课表管理、借用场馆、借用查询6个页面1)项目结构说明yundongchangguan/ ├── package.json # 项目依赖与脚本配置 ├── vite.config.js # Vite构建配置(含API代理) ├── index.html # 前端入口HTML ├── server/ # 后端服务 │ ├── index.js # Express服务入口 │ ├── database.js # 数据库初始化、工具函数、种子数据 │ ├── utils.js # 错误码定义、参数校验工具函数 │ ├── routes/ │ │ ├── auth.js # 登录认证与权限中间件 │ │ ├── dashboard.js # 数据看板API │ │ ├── venues.js # 场馆CRUD API │ │ ├── schedules.js # 课表管理API(含冲突检测) │ │ └── borrowings.js # 借用管理API(含推荐、取消、时间限制) │ └── tests/ │ ├── test.js # 工具函数与数据库单元测试 │ └── api-test.js # API集成测试 ├── src/ # 前端源码 │ ├── main.js # Vue应用入口 │ ├── App.vue # 主布局(侧边导航+用户信息) │ ├── router/ │ │ └── index.js # 路由配置(6个页面+登录守卫) │ ├── api/ │ │ └── index.js # Axios API封装层(含token拦截器) │ └── views/ │ ├── Login.vue # 登录页面 │ ├── Dashboard.vue # 数据看板页面 │ ├── VenueList.vue # 场馆管理页面 │ ├── ScheduleManage.vue # 课表管理页面 │ ├── BorrowVenue.vue # 借用场馆页面(推荐+列表双模式) │ └── BorrowingQuery.vue # 借用查询与取消页面 └── dist/ # 构建产物(部署用)2)关键代码讲解(一)用户登录与权限控制系统采用 Token 认证机制,登录后返回 token,后续请求携带 token 进行身份验证。管理员可增删改场馆和课表,普通用户只能借用和查询。// server/routes/auth.js const tokens = new Map() router.post('/login', async (req, res) => { const { username, password } = req.body const user = queryGet(db, 'SELECT * FROM users WHERE username = ? AND password = ?', [username, password]) if (!user) return res.json(fail(ERROR_CODES.AUTH_FAILED)) const token = `tk_${user.id}_${Date.now()}_${Math.random().toString(36).slice(2)}` tokens.set(token, { id: user.id, username: user.username, role: user.role, display_name: user.display_name }) res.json(success({ token, user: { id: user.id, username: user.username, role: user.role, display_name: user.display_name } })) }) function authMiddleware(req, res, next) { const token = req.headers.authorization?.replace('Bearer ', '') if (!token || !tokens.has(token)) return res.json(fail(ERROR_CODES.AUTH_TOKEN_EXPIRED)) req.user = tokens.get(token) next() } function adminMiddleware(req, res, next) { if (!req.user || req.user.role !== 'admin') return res.json(fail(ERROR_CODES.AUTH_FORBIDDEN)) next() } (二)时间冲突检测 — 系统核心业务逻辑场馆占用涉及课表(按星期循环)和借用(按具体日期)两种时间维度。系统采用区间重叠判定法统一处理:// server/routes/borrowings.js // 两个时间段重叠的充要条件:A_start < B_end AND A_end > B_start // 检测与课表的冲突(将借用日期转为星期几后比对) const date = new Date(borrow_date) const dayOfWeek = date.getDay() === 0 ? 7 : date.getDay() const scheduleConflict = queryGet(db, 'SELECT * FROM schedules WHERE venue_id = ? AND day_of_week = ? AND (start_time < ? AND end_time > ?)', [venue_id, dayOfWeek, end_time, start_time] ) // 检测与已有借用的冲突 const borrowConflict = queryGet(db, "SELECT * FROM borrowings WHERE venue_id = ? AND borrow_date = ? AND status = 'active' AND (start_time < ? AND end_time > ?)", [venue_id, borrow_date, end_time, start_time] ) (三)借用时间限制校验后端统一校验单次借用不超过2小时、不能借用过去日期:// server/utils.js function validateTimeRange(start_time, end_time, maxMinutes = 120) { const [sh, sm] = start_time.split(':').map(Number) const [eh, em] = end_time.split(':').map(Number) const startMin = sh * 60 + sm const endMin = eh * 60 + em if (endMin <= startMin) return { valid: false, message: '结束时间必须晚于开始时间' } if (endMin - startMin > maxMinutes) return { valid: false, message: `单次借用时长不能超过${maxMinutes / 60}小时` } return { valid: true } } function validateDateNotPast(dateStr) { const today = new Date(); today.setHours(0, 0, 0, 0) if (new Date(dateStr) < today) return { valid: false, message: '不能借用过去的日期' } return { valid: true } } (四)API错误码规范化// server/utils.js const ERROR_CODES = { SUCCESS: 0, PARAM_MISSING: 10001, // 缺少必要参数 PARAM_INVALID: 10002, // 参数格式不正确 AUTH_FAILED: 20001, // 用户名或密码错误 AUTH_TOKEN_EXPIRED: 20002, // 登录已过期 AUTH_FORBIDDEN: 20003, // 无权限 NOT_FOUND: 30001, // 资源不存在 CONFLICT: 40001, // 资源冲突 TIME_LIMIT_EXCEEDED: 40002, // 超出时间限制 SERVER_ERROR: 50001 // 服务器内部错误 } (五)数据库事务保护// server/database.js function runTransaction(db, fn) { db.run('BEGIN TRANSACTION') try { fn(db) db.run('COMMIT') saveDB() } catch (e) { db.run('ROLLBACK') throw e } } // 删除场馆时事务性删除关联数据 router.delete('/:id', authMiddleware, adminMiddleware, async (req, res) => { runTransaction(db, (db) => { db.run('DELETE FROM borrowings WHERE venue_id = ?', [id]) db.run('DELETE FROM schedules WHERE venue_id = ?', [id]) db.run('DELETE FROM venues WHERE id = ?', [id]) }) }) (六)前端表单校验与时间限制提示<!-- src/views/BorrowVenue.vue --> <el-alert type="info" :closable="false">单次借用时长不超过2小时,需提前1天预约</el-alert> <el-form ref="borrowFormRef" :model="borrowForm" :rules="borrowRules"> <el-form-item label="借用人" prop="borrower_name"> <el-input v-model="borrowForm.borrower_name" /> </el-form-item> </el-form-item> </el-form> <script> const borrowRules = { borrower_name: [{ required: true, message: '请输入姓名', trigger: 'blur' }], borrower_dept: [{ required: true, message: '请输入部门/班级', trigger: 'blur' }], end_time: [{ required: true, message: '请选择结束时间', trigger: 'change' }, { validator: validateTimeLimit, trigger: 'change' }] } </script> 3.3 安装依赖与环境适配1)安装项目依赖npm install 2)遇到的问题:原生模块编译失败安装过程中 better-sqlite3 因需要原生编译而失败,报错信息:npm error command failed npm error command C:\WINDOWS\system32\cmd.exe /d /s /c prebuild-install || node-gyp rebuild --release npm error 'node' 不是内部或外部命令CodeArts 代码智能体自动处理过程:识别根因为 node-gyp 子进程找不到 node 命令,属于原生模块编译依赖问题自主将 package.json 中的 better-sqlite3 替换为 sql.js(纯JS实现,无需原生编译)重写 server/database.js,适配 sql.js 的异步初始化模式(initSqlJs())增加 saveDB() 函数,在每次写操作后手动持久化到文件(sql.js 默认在内存中运行)同步更新所有路由文件(venues.js、schedules.js、borrowings.js)为 async/await 模式重新执行 npm install 成功这一过程体现了 CodeArts 代码智能体的问题诊断与自主修复能力,无需人工介入即可完成技术方案切换。3.4 运行调试与功能验证1)启动后端服务新开一个终端窗口,执行:cd yundongchangguan node server/index.js看到以下输出表示后端启动成功:服务端运行在 http://localhost:3000 2)启动前端开发服务器再开一个终端窗口,执行:cd yundongchangguan npx vite看到以下输出表示前端启动成功: VITE v5.x.x ready in xxx ms ➜ Local: http://localhost:5173/ 3)登录系统浏览器访问 http://localhost:5173,进入登录页面。使用预置账号登录:角色用户名密码管理员adminadmin123普通用户user11234564)数据看板登录后进入数据看板页面,展示场馆总数、可用场馆数、今日借用数、有效借用数等统计信息,以及场馆利用率排行和最近借用记录。5)场馆管理在场馆管理页面查看10个预置场馆的卡片展示,管理员可新增、编辑、删除场馆。6)课表管理在课表管理页面查看12条预置课表,管理员可新增课表(含冲突检测)、编辑、删除。7)智能推荐借用在借用场馆页面,切换到"智能推荐"标签,选择日期和时间段后点击"查找可用场馆",系统自动排除有课表占用和已有借用的场馆。8)场馆列表借用切换到"场馆列表借用"标签,直接从列表中选择场馆,填写借用信息提交。表单校验会检查必填项和2小时时间限制。9)借用查询与取消在借用查询页面,按场馆/姓名/日期/状态筛选借用记录,可取消有效借用。10)单元测试运行单元测试和API集成测试:npm test 测试覆盖内容:测试类型测试项数量工具函数success/fail/validateRequired/validateTimeRange/validateDateNotPast10数据库预置数据/管理员用户/事务回滚5API集成登录/看板/分页/时间限制/过去日期/未登录拒绝/权限控制811)构建前端产物npx vite build构建成功输出:4、核心技术难点与解决思路难点一:时间段冲突检测的准确性问题: 场馆占用涉及课表(按星期循环)和借用(按具体日期)两种不同维度的时间表示,需准确判定冲突。解决思路:课表以 day_of_week(1-7)表示周期性占用,借用以 borrow_date(具体日期)表示一次性占用检测借用冲突时,先将借用日期转换为星期几(new Date(borrow_date).getDay()),再与课表比对统一使用区间重叠公式 start_time < end AND end_time > start 进行冲突判定,避免边界条件遗漏难点二:原生模块编译失败的环境适配问题: 初始选用 better-sqlite3 作为SQLite驱动,但在Windows环境下因 node 不在系统PATH中,导致 node-gyp 编译失败。解决思路:CodeArts 代码智能体自动识别根因为原生模块编译依赖问题,而非代码逻辑错误自主将 better-sqlite3(需原生编译)替换为 sql.js(纯JS实现,WASM运行)重写数据库操作层,适配 sql.js 的异步初始化模式(initSqlJs())增加 saveDB() 函数,在每次写操作后手动持久化到文件(sql.js 默认在内存中运行)所有路由处理函数改为 async/await 模式以适配异步数据库初始化难点三:用户权限与接口安全问题: 系统需区分管理员和普通用户角色,管理员可管理场馆和课表,普通用户只能借用和查询,所有接口需鉴权保护。解决思路:登录成功后生成内存级 Token,前端存储在 localStorage 并通过 Axios 拦截器自动携带后端通过 authMiddleware 校验 Token 有效性,adminMiddleware 校验管理员权限前端路由守卫拦截未登录访问,自动跳转登录页Token 过期时后端返回 20002 错误码,前端自动清除本地存储并跳转登录难点四:前后端数据一致性保障问题: 借用操作涉及冲突检测和数据写入两步,若写入过程中断可能导致数据不一致。解决思路:引入 runTransaction() 函数,在删除场馆等涉及多表操作的场景使用事务保护删除场馆时事务性删除关联的借用记录和课表记录,保证数据完整性事务失败时自动 ROLLBACK,避免脏数据附录项目代码、文档及演示视频:项目代码及演示视频
-
1 、概述1.1 案例介绍企业日常会议产生大量记录文本,但缺乏高效的后续跟进手段:摘要靠人工整理、任务靠口头传达、风险靠经验判断。本案例将带你从零构建一款"会议智脑"应用——上传会议记录后,自动调用华为云 MaaS 大模型(DeepSeek V4 Flash)生成摘要、提取关键决策和任务清单,并通过可视化看板、甘特图、日历等多维度视图进行任务追踪与风险预警。1.2 适用对象企业开发者个人开发者1.3 案例时间 本案例总时长预计60分钟。1.4 案例流程 1. 领取华为云 MaaS 平台大模型 Tokens,获取 API Key 和模型接入地址; 2. 配置 .env 环境变量,将 MaaS API Key 等信息写入配置; 3. 初始化数据库并启动后端服务,验证 API 文档可访问; 4. 启动前端开发服务器,登录系统并上传会议记录,体验 AI 自动生成摘要与任务提取; 5. 在任务看板、甘特图、日历等视图中查看和管理提取的任务,触发风险检测。1.5 资源总览 本案例使用的华为云服务均为按需付费,预计花费不超过50元(MaaS Tokens 代金券可覆盖)。体验完成后请及时释放资源,避免产生多余的费用。资源名称规格单价(元) 华为云 MaaSDeepSeek V4 Flash 大模型推理服务代金券可覆盖华为云码道(CodeArts)代码智能体通用体验版免费2 、环境和资源准备2.1 领取华为云MaaS平台大模型Tokens福利 方式一: 登录华为开发者空间,参考案例《华为开发者空间 - ModelArts Studio大模型通用代金券领取使用指导》中的“二、 开通MaaS平台大模型”章节内容领取代金券,获取到模型的API地址、模型名称和API Key。 方式二: 登录华为开发者空间,参考案例《华为云MaaS平台大模型Tokens领取使用指导》中的“二、 领取MaaS平台大模型Tokens”章节内容,领取MaaS平台DeepSeek V3系列大模型Tokens代金券,购买ModelArts Studio DeepSeek Tokens套餐包,开通模型服务,最后获取到模型的API地址、模型名称和API Key。 2.2 安装本地开发环境本案例需要以下开发工具: 工具版本要求用途Python≥ 3.11后端运行时Node.js≥ 18.0前端构建npm≥ 9.0前端包管理Git≥ 2.30版本控制3 、构建会议智脑应用3.1 部署项目代码1)项目结构说明:ai-meeting/├── app/│ ├── core/ # 核心基础设施│ │ ├── config.py # Pydantic Settings 配置管理│ │ ├── database.py # 异步数据库引擎 + 会话工厂│ │ ├── exceptions.py # 统一异常处理器│ │ └── logging.py # 日志配置│ ├── models/ # SQLAlchemy ORM 模型│ │ ├── user.py # 用户模型│ │ ├── meeting.py # 会议模型│ │ ├── task.py # 任务模型│ │ └── risk_alert.py # 风险预警模型│ ├── schemas/ # Pydantic 请求/响应 Schema│ ├── api/ # FastAPI 路由│ │ ├── auth.py # 认证 + 用户管理 API│ │ ├── meetings.py # 会议 CRUD + 搜索 + 导出│ │ ├── tasks.py # 任务列表 + 更新 + 排序│ │ └── skill.py # 风险检测 API│ ├── services/ # 业务逻辑层│ │ ├── auth_service.py # 注册/登录/JWT/密码哈希│ │ ├── meeting_service.py # 会议业务逻辑│ │ ├── task_service.py # 任务业务逻辑│ │ └── maas_service.py # MaaS API + 本地摘要引擎│ └── main.py # FastAPI 应用入口├── frontend/│ ├── package.json # 前端依赖│ ├── vite.config.js # Vite 配置(API 代理)│ └── src/│ ├── main.js # 入口(ElementPlus 中文 locale)│ ├── App.vue # 布局(侧边栏 + 路由 + 登录状态)│ └── components/ # 12 个功能组件├── .env # 环境变量├── requirements.txt # Python 依赖└── init_db.py # 数据库初始化脚本 2)下载源码 通过git下载源码到本地(含demo演示),代码仓地址:ai-meeting - AtomGitgit clone https://gitcode.com/ gcw_Xpooy7x3/ai-meeting.gitcd ai-meeting 3)关键代码讲解 3.1 配置管理——从 .env 加载 MaaS API Key 使用 Pydantic Settings 从 .env 文件加载配置,extra: "ignore" 允许旧变量不报错,@lru_cache 实现全局单例:from pydantic_settings import BaseSettingsfrom functools import lru_cacheclass AppSettings(BaseSettings): DATABASE_URL: str = "sqlite+aiosqlite:///./ai_meeting.db" REDIS_URL: str = "redis://localhost:6379/0" MAAS_API_KEY: str = "" MAAS_API_URL: str = "https://api.modelarts-maas.com/v2/chat/completions" MAAS_MODEL: str = "deepseek-v4-flash" APP_NAME: str = "会议智脑" DEBUG: bool = False JWT_SECRET: str = "change-me-in-production" JWT_ALGORITHM: str = "HS256" JWT_EXPIRE_MINUTES: int = 1440 model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "extra": "ignore", }@lru_cache()def get_settings() -> AppSettings: return AppSettings() 在项目根目录创建 .env 文件,将 MaaS 的 API Key、API 地址和模型名称填入:DATABASE_URL=sqlite+aiosqlite:///./ai_meeting.dbREDIS_URL=redis://localhost:6379/0MAAS_API_KEY=<你的华为云MaaS API Key>MAAS_API_URL=https://api.modelarts-maas.com/v2/chat/completionsMAAS_MODEL=deepseek-v4-flashAPP_NAME=会议智脑DEBUG=trueJWT_SECRET=meeting-brain-jwt-secret-2026 3.2 核心逻辑——调用华为云 MaaS 大模型生成摘要 这是本案例的核心代码。process_meeting 函数实现多级降级策略:优先调用 MaaS API,失败时降级到本地规则引擎。同时支持 Redis 缓存(可选,连接失败自动跳过)。 System Prompt 设计——明确指定英文字段名和 JSON 输出格式,避免模型返回中文键名:SYSTEM_PROMPT = """你是一个严谨的会议纪要专家。请处理输入的会议记录并输出JSON。规则:- summary:不超过150字,仅包含最终结论,不重复会议过程- key_decisions:只提取有明确结论或投票通过的事项,最多5条- tasks:仅当原文明确提及"某人负责某事"或"需要在某时间前完成"时才提取,严禁臆造- 日期格式统一为 YYYY-MM-DD,如果原文没有年份则默认为当前年份- 如果原文信息不足,对应字段返回空列表或空字符串,不要编造输出必须是合法JSON,严格使用以下英文字段名(禁止使用中文字段名):{ "summary": "一句话摘要", "key_decisions": ["决策1", "决策2"], "tasks": [ {"description": "任务描述", "assignee": "责任人", "deadline": "YYYY-MM-DD", "priority": "high/mid/low"} ]}""" MaaS API 调用——使用 httpx.AsyncClient 异步调用,指数退避重试(最多3次,仅对超时/连接错误重试),超时时间90秒:async def _call_maas_api(record_text: str) -> dict: settings = get_settings() headers = { "Authorization": f"Bearer {settings.MAAS_API_KEY}", "Content-Type": "application/json", } body = _build_request_body(record_text) for attempt in range(1, MAX_RETRIES + 1): try: async with httpx.AsyncClient(timeout=90) as client: resp = await client.post(settings.MAAS_API_URL, headers=headers, json=body) if resp.status_code != 200: raise AppException(502, f"MaaS 接口返回错误码: {resp.status_code}") content = resp.json()["choices"][0]["message"]["content"] start, end = content.find("{"), content.rfind("}") + 1 parsed = json.loads(content[start:end]) # 兼容中文键名 if "摘要" in parsed and "summary" not in parsed: parsed["summary"] = parsed.pop("摘要") if "关键决策" in parsed and "key_decisions" not in parsed: parsed["key_decisions"] = parsed.pop("关键决策") if "任务清单" in parsed and "tasks" not in parsed: parsed["tasks"] = parsed.pop("任务清单") raw_tasks = parsed.get("tasks") or parsed.get("task_list") or parsed.get("action_items") or [] normalized_tasks = [] for t in raw_tasks: if isinstance(t, dict): nt = { "description": t.get("description") or t.get("task") or t.get("任务") or "", "assignee": t.get("assignee") or t.get("person") or t.get("负责人") or "", "deadline": t.get("deadline") or t.get("due_date") or t.get("截止日期") or "", "priority": t.get("priority") or "mid", } normalized_tasks.append(nt) elif isinstance(t, str): m = re.match(r"^([\u4e00-\u9fa5]{2,4})[::]\s*(.+)$", t) if m: assignee, desc = m.group(1), m.group(2) normalized_tasks.append({"description": desc, "assignee": assignee, "deadline": "", "priority": "mid"}) parsed["tasks"] = normalized_tasks return parsed except (httpx.TimeoutException, httpx.ConnectError): wait = 2 ** attempt await asyncio.sleep(wait) raise AppException(502, "MaaS 调用失败") 多级降级与缓存——完整的 process_meeting 流程:async def process_meeting(record_text: str) -> dict: if not record_text or not record_text.strip(): return {"summary": "", "key_decisions": [], "tasks": []} settings = get_settings() if not settings.MAAS_API_KEY: return _local_summarize(record_text) redis_client = await _get_redis() try: if redis_client: cached = await redis_client.get(_cache_key(record_text)) if cached: return json.loads(cached) try: result = await _call_maas_api(record_text) except Exception: result = _fallback_result(record_text) if redis_client: await redis_client.set(_cache_key(record_text), json.dumps(result, ensure_ascii=False), ex=7*24*3600) return result finally: if redis_client: await redis_client.close()3.3 会议上传与 AI 处理联动 会议上传 API 在创建记录后,自动调用 process_meeting 进行 AI 处理,将生成的摘要、决策写入 Meeting 记录,并将提取的任务批量创建为 Task 记录:class MeetingService: def __init__(self, db: AsyncSession): self.db = db async def upload_meeting(self, title, record_text, tags=None): meeting = Meeting(title=title, record_text=record_text, status=MeetingStatus.pending, tags=tags) self.db.add(meeting) await self.db.flush() if record_text and record_text.strip(): await self._process_meeting_content(meeting) return meeting.id async def _process_meeting_content(self, meeting): try: result = await process_meeting(meeting.record_text) meeting.summary = result.get("summary", "") meeting.key_decisions = result.get("key_decisions", []) meeting.status = MeetingStatus.processed for task_data in result.get("tasks", []): task = Task( meeting_id=meeting.id, description=task_data.get("description", ""), assignee=task_data.get("assignee", ""), deadline=self._parse_date(task_data.get("deadline")), priority=self._parse_priority(task_data.get("priority")), status=TaskStatus.todo, ) self.db.add(task) await self.db.flush() except Exception as exc: logger.error(f"会议处理失败,保持pending状态: {exc}")3.4 数据库会话管理——SQLite 异步适配 SQLite 适配关键点:WAL 模式支持并发读、外键约束、不使用连接池。get_db() 通过 yield 实现请求级会话,自动 commit/rollback:from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSessionfrom sqlalchemy import eventsettings = get_settings()_is_sqlite = settings.DATABASE_URL.startswith("sqlite")engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)if _is_sqlite: @event.listens_for(engine.sync_engine, "connect") def _set_sqlite_pragma(dbapi_conn, connection_record): cursor = dbapi_conn.cursor() cursor.execute("PRAGMA journal_mode=WAL") cursor.execute("PRAGMA foreign_keys=ON") cursor.close()AsyncSessionLocal = async_sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)async def get_db() -> AsyncSession: async with AsyncSessionLocal() as session: try: yield session await session.commit() except Exception: await session.rollback() raise3.5 前端 Vite 代理配置 开发环境将 /api 请求代理到后端 8000 端口:import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true }, '/health': { target: 'http://localhost:8000', changeOrigin: true }, }, },})4)运行调试 步骤1:安装后端依赖pip install -r requirements.txt 步骤2:安装前端依赖cd frontendnpm installcd .. 步骤3:初始化数据库python init_db.py 执行成功后输出:数据库表创建完成示例数据初始化完成!2个用户 + 3个会议 + 10项任务 管理员: admin / admin123 普通用户: demo / demo123 步骤4:启动后端服务uvicorn app.main:app --reload --port 8000 启动后访问 http://localhost:8000/docs 可查看自动生成的 OpenAPI 交互式文档: 步骤5:启动前端开发服务器cd frontendnpm run dev 启动后访问 http://localhost:5173,进入登录页面: 步骤6:登录并体验完整功能 使用 admin / admin123 登录,进入数据总览页面: 点击左侧菜单"上传会议",粘贴会议记录文本,点击"提交会议记录": 上传成功后点击"查看详情",查看 AI 自动生成的摘要、关键决策和任务清单: 切换到"任务看板"页面,拖拽卡片切换任务状态: 切换到"任务甘特图"页面,查看任务时间线: 切换到"风险检测"页面,点击"立即检测": 4 、释放资源本案例使用本地 SQLite 数据库和可选 Redis,不涉及华为云付费资源的持续占用。如需释放:• 删除本地 ai_meeting.db 文件即可清除所有数据• 如使用了华为云 MaaS Tokens 代金券,代金券到期后自动失效,无需手动释放5 、扩展资料说明• 想了解更多关于华为云 MaaS 大模型服务的可以访问:https://support.huaweicloud.com/productdesc-maas/maas_01_0001.html• 想了解更多关于 FastAPI 框架的可以访问:https://fastapi.tiangolo.com/• 想了解更多关于 Vue 3 组合式 API 的可以访问:https://cn.vuejs.org/guide/introduction.html• 想了解更多关于 Element Plus 组件库的可以访问:https://element-plus.org/zh-CN/
-
1 、概述1.1 案例介绍商品管理后台Web应用——GoodsManager。该系统采用 Python Flask + SQLite + Bootstrap 5 技术栈,实现了商品全生命周期管理、库存预警、分类管理、图片本地托管、操作记录追踪与防篡改、数据加密导入导出、撤回重做、首页仪表盘等核心功能,并采用毛玻璃主题设计,背景图动态提取主色调实现浅色/深色自适应。通过本案例,开发者将体验如何利用CodeArts代码智能体的Spec-Driven Development(规格驱动开发)工作流,从需求规格定义、实现方案设计、编码任务规划到代码实现的完整过程,高效交付一个功能完备、代码质量高、架构模块化的Web应用。1.2 适用对象个人开发者高校学生1.3 案例时间本案例总时长预计180分钟。1.4 案例流程 说明:1. AI IDE华为云码道(CodeArts)代码智能体安装部署;2.使用CodeArts代码智能体,通过自然语言描述需求,自动生成需求规格文档(spec.md);3.基于需求规格文档,自动生成实现方案文档(design.md),包含架构设计、接口设计、数据模型等;4.基于实现方案文档,自动生成编码任务规划(tasks.md),将需求拆解为可执行的编码任务;5.根据编码任务规划,逐步实现各功能模块代码,包括用户认证、商品管理、分类管理、图片托管、操作记录、数据导入导出、撤回重做、仪表盘等;6.运行start.bat一键启动应用,在浏览器中验证所有功能。1.5 资源总览本案例预计花费240元。体验完成后请及时释放资源,避免产生多余的费用。实际实践过程中的费用可能较下表更少,示例费用是分多次对话、添加新功能以及多次修复bug所用的。资源名称描述/规格价格Python 3.11运行环境免费Flask / Flask-SQLAlchemy / WerkzeugPython Web框架及ORM免费华为云码道(CodeArts)代码智能体专业版139元/月华为云码道(CodeArts)代码智能体按需计费约100元 2 、环境和资源准备2.1 AI IDE华为云码道安装部署参考案例《AI IDE华为云码道(CodeArts)代码智能体安装部署》完成Windows版AI IDE华为云码道(CodeArts)代码智能体安装部署。打开CodeArts代码智能体(IDE内置的AI助手),准备开始通过自然语言描述需求来生成项目代码。 注:本案例中项目所创建的本地目录为E:/CodeArts/PGAMEGoodsManager;本案例使用码道智能体模式,模型选择GLM-5.1,开发模式为Spec-Driven Development(规范开发)。3 、构建商品管理应用3.1 描述项目需求在CodeArts代码智能体的对话框中,输入项目需求描述。本案例的需求描述如下:开发一个名为"PGAMEGoodsManager"(PGAME可以更换为其他名称)的游戏周边商品管理后台Web应用,使用Python Flask + SQLite + Bootstrap技术栈。要求:后端Python Flask + SQLite,前端HTML + CSS (Bootstrap 5) + JavaScript;代码必须模块化(Flask Blueprint),以便后续增加新功能;库存预警≤3红色,4-5黄色,≥6默认颜色;管理员账号只能由现有admin派发,禁止直接注册成管理员;折扣价与售价不同用灰色背景标出,相同用"-"占位;数据导入导出使用简单加密过的JSON,密钥写在文件开头;品牌名显示为"P-GAME"(同样可以更改),点击以跳转仪表盘;操作类型备注应自动检测(对比新旧数据),不要求手动选择;全站采用毛玻璃(glass)主题,背景图从backgrounds/目录读取,动态提取主色调。有部分具体要求在后续3.2-3.4有详细罗列。 CodeArts代码智能体将根据需求描述,自动创建项目目录结构并生成规格驱动开发(SDD)文档目录:.codeartsdoer/specs/pgame_goods_mgr/├── spec.md # 需求规格文档├── design.md # 实现方案文档└── tasks.md # 编码任务规划3.2 审阅需求规格文档(spec.md)CodeArts代码智能体自动生成了需求规格文档spec.md,包含以下核心内容:• 组件定位:核心职责、核心输入/输出、职责边界• 领域术语:游戏周边商品、分类、成本、折扣价、库存预警、数据快照、加密JSON等• 角色与边界:admin管理员、普通用户、审计员• 核心能力(5.1-5.18):用户认证、商品管理、分类管理、库存预警、图片托管、示例数据初始化、商品搜索、一键启动、操作记录、导航交互、数据导出与导入、首页仪表盘、商品详情页、撤回与重做、操作审计与防篡改、数据合并导入、主题色彩系统重构、其他Bug修复• 数据约束(6.1-6.15):商品/分类/用户账号/商品图片/搜索条件/操作记录/排序/快照/导出文件/仪表盘统计/签名/审计日志/审计员账号/主题色彩字典 如图为spec.md部分内容,开发者可审阅spec.md内容,如有修改意见可告知CodeArts代码智能体进行修改。确认无误后,进入下一阶段。3.3 审阅实现方案文档(design.md)CodeArts代码智能体基于spec.md自动生成了实现方案文档design.md,包含以下核心内容:3.3.1 需求与存量功能关系分析design.md首先分析了需求功能与存量功能的关系,将功能分为三类:已实现功能、需要扩展的功能(需在现有代码上扩展)、需要新增的功能或接口(全新设计)。其中首次构建项目时后面的两点应该没有内容。3.3.2 实现模型包含上下文视图(单体Flask Web应用架构)、服务/组件总体架构(9个Blueprint模块)、实现设计文档(15个流程图,覆盖用户认证、商品操作、排序、详情页、分类删除、数据导出/导入/撤回、删除撤回/重做、管理员派发、仪表盘、操作记录、图片资源池管理、系统初始化、HMAC签名链、签名校验、审计日志、审计员权限控制、管理员权限撤回、数据合并导入、主题色彩系统重构、折扣价联动修复等)。3.3.3 接口设计 共设计了42个接口,分为认证接口组(5个)账号管理接口组(5个)商品管理接口组(8个)分类管理接口组(3个)操作记录接口组(3个)图片托管接口组(1个)数据管理接口组(3个)撤回重做接口组(2个)仪表盘接口组(1个)增量接口组(7个)合并导入接口组(1个)审计日志接口组(1个)其他Bug修复接口组(2个)3.3.4 数据模型定义了7个数据模型:User(含is_admin、is_auditor)、Category、Goods(含discount_price、updated_at)、OperationLog(含change_type、signature)、DataSnapshot、DeletedItem(含status、get_summary())、AuditLog。开发者可审阅design.md内容,如有修改意见可告知CodeArts代码智能体进行修改。确认无误后,进入下一阶段。3.4 审阅编码任务规划(tasks.md)CodeArts代码智能体基于design.md自动生成了编码任务规划tasks.md,将整个项目拆解为22个编码/验证节:第1节:项目基础设施搭建(目录结构、依赖、数据库模型、Blueprint注册)第2节:用户认证功能实现(注册/登录/退出/装饰器/页面模板)第3节:图片托管功能实现(上传/展示/清理/替换/批量读写) 第4节:分类管理功能实现(列表/搜索/新增/删除/页面模板)第5节:商品管理功能实现(列表/搜索/排序/新增/编辑/删除/详情/页面模板第6节:账号管理功能实现(设置页/修改密码/删除账号/创建管理员)第7节:操作记录功能实现(自动记录/查看/删除/页面模板)第8节:数据导入导出功能实现(导出/导入/页面模板)第9节:撤回与重做功能实现(导入撤回/删除撤回/重做)第10节:首页仪表盘功能实现(统计/记录/快捷按钮)第11-13节:导航更新/系统初始化/一键启动第14节:集成测试与验证(20个子节覆盖所有功能验证)接下来的内容应当也是第一轮对话就写入的第15-16节:操作审计与防篡改功能实现与验证(增量)第17-18节:数据合并导入功能实现与验证(增量)第19-20节:主题色彩系统重构功能实现与验证(增量)第21-22节:其他Bug修复与优化功能实现与验证(增量)开发者可审阅tasks.md内容,如有修改意见可告知CodeArts代码智能体进行修改。确认无误后,进入下一阶段。3.5 代码实现确认tasks.md后,CodeArts代码智能体将根据编码任务规划逐步实现各功能模块代码。以下是各模块的实现要点:3.5.1 应用入口与数据库初始化(app.py)app.py是应用的核心入口,负责:• 创建Flask应用工厂函数create_app(),配置SECRET_KEY、SQLALCHEMY_DATABASE_URI等• 注册9个Blueprint(auth/account/goods/category/image/oplog/data/dashboard/undo/audit)• 数据库自动初始化:首次启动时创建表结构和示例数据(1个admin + 1个分类 + 1个商品)• Schema迁移检测:通过inspect检测新增字段(signature/is_auditor等),缺失时触发数据库重建• 背景图主色调提取:extract_bg_colors()扫描backgrounds/目录,提取RGB平均值,计算亮度luma,预计算40个CSS颜色字符串• 全局context_processor:注入theme字典(40键)至所有模板• 背景图服务路由:/bg/<filename>提供图片服务,含Cache-Control: max-age=86400缓存头 3.5.2 数据模型层(models.py)定义了7个数据模型:模型关键字段说明Userid, username, password_hash, is_admin, is_auditor, created_at用户账号,支持三种角色Categoryid, name商品分类,名称唯一Goodsid, name, game, category_id, cost, price, discount_price, stock, image_path, created_at, updated_at商品,含折扣价和修改时间OperationLogid, operator, action, target_type, target_name, change_type, signature, detail, created_at操作记录,含HMAC签名DataSnapshotid, snapshot_type, snapshot_data, related_operation, created_at数据快照,支持撤回DeletedItemid, item_type, item_data, status(deleted/undone), created_at删除暂存,支持撤回/重做AuditLogid, audit_time, operator, operation_type, deleted_summary, deleted_count审计日志,不可修改/删除 3.5.3 用户认证模块(auth.py)实现了三个权限装饰器:• login_required:检查Session中user_id,未登录重定向至登录页;• admin_or_auditor_required:仅admin和审计员可访问(用于审计日志页面);• not_auditor_required:审计员禁止访问(用于商品/分类/数据管理路由)。注册路由禁止创建管理员(User默认is_admin=False)。登录成功后Session写入user_id、username、is_admin、is_auditor。 3.5.4 商品管理模块(goods.py)商品管理是系统的核心模块,实现了以下功能:• 商品列表:支持5种搜索条件(keyword/game/category_id/stock_status/sort)组合筛选,6种排序方式; 图:六种排序方式• 库存预警:≤3红色(danger)、4-5黄色(warning)、≥6默认颜色• 折扣价显示:≠售价时灰色背景+显示原售价,=售价时显示"-"占位符• 商品新增:折扣价默认等于售价,新增时修改售价折扣价自动联动(使用prevPrice变量追踪)• 商品编辑:折扣价独立不联动,系统自动检测change_type(对比新旧数据差异)• 商品删除:删除前保存数据到DeletedItem(status=deleted),不立即删除图片文件• 商品详情页:上方展示信息+最近5条修改记录+库存变动记录,下方编辑区 3.5.5 操作记录与防篡改模块(oplog.py)这是本案例最具特色的功能模块,实现了操作记录的HMAC-SHA256签名链防篡改机制:签名链生成:每条操作记录创建时,基于operator|action|target_type|target_name|change_type|created_at_str|prev_signature七个字段生成HMAC-SHA256签名。首条记录的prev_signature为预设常量"GENESIS",后续记录依赖前一条记录的签名值,形成哈希链。 签名校验:每次访问操作记录页面时,按id升序遍历所有记录,使用hmac.compare_digest常量时间比较验证签名链完整性。校验通过显示绿色✓标记,校验失败显示红色✗"校验失败"警告。签名链重建:admin删除/清空操作记录后,自动调用_rebuild_signature_chain()按id升序重新生成所有记录的签名,保持签名链完整性。审计日志:admin删除操作记录时,自动将删除行为记录到AuditLog表(不可修改/删除),包含审计时间、操作账号、操作类型、被删除记录摘要、删除数量。 3.5.6 数据导入导出模块(data.py)数据导出:收集商品/分类/操作记录/用户账号/image_pool/图片文件 → 序列化为JSON → base64编码+密钥加密 → 生成下载文件(第1行密钥,第2行起加密数据)。数据导入(合并模式):保留当前用户账号,分类按名称去重(建立旧ID→新ID映射),商品按name+game+category_id三元组去重更新,操作日志追加,图片增量导入。合并导入后自动重建签名链。 3.5.7 撤回重做模块(undo.py)删除撤回:从DeletedItem恢复被删除的数据,将status从deleted改为undone(而非删除记录),恢复时不强制指定原始id(让数据库自动分配)。删除重做:查找status=undone的最近记录,重新删除对应数据并删除图片文件,记录"重做删除"操作日志。重做按钮独立显示,显示将被重做的商品名称和成本。保留最近10次删除记录。 3.5.8 账号管理模块(account.py)修改密码:验证旧密码+确认新密码(两次输入一致),成功后清除Session要求重新登录。创建管理员/审计员:仅admin可创建,校验用户名唯一性和密码强度(≥6位)。撤回管理员权限:admin可将其他管理员降级为普通用户(不可撤回自身),撤回操作自动记录到操作日志。 3.5.9 毛玻璃主题与动态色调(base.html)系统启动时通过extract_bg_colors()提取背景图RGB主色调,计算亮度luma = 0.299*R + 0.587*G + 0.114*B。luma>140为浅色系(降低毛玻璃明度+黑色字体),≤140为深色系(提高毛玻璃明度+白色字体)。所有CSS颜色值在Python端预计算为完整rgba()字符串,通过CSS自定义属性传递(base.html中style#theme-vars块的:root仅此处使用Jinja2赋值CSS变量),主CSS只引用var(--xxx),主CSS和JS中零Jinja2引用。提示框(.alert)字体始终黑色。粒子效果:35个粒子+连线动画,颜色通过HTML属性data-pr/data-pg/data-pb传递,降帧至30fps。导航滑块指示器与页面切换动画:sessionStorage存储上一页面位置,cubic-bezier缓动曲线0.3s平滑滑动。opacity+transform淡入淡出0.35s。 3.5.10 一键启动(start.bat)start.bat脚本自动完成以下操作:• 使用@echo off关闭命令回显;• 输出启动提示信息"正在启动 P-GAME GoodsManager...";• 检查并创建Python虚拟环境(venv);• 安装项目依赖(pip install -r requirements.txt);• 启动Flask应用并等待3秒;• 自动打开默认浏览器访问。• 注意:不要显示默认管理员账号信息,防止敏感信息泄露3.6 运行调试3.6.1 使用一键启动脚本双击项目根目录下的start.bat文件,系统将自动创建Python虚拟环境、安装依赖、启动Flask应用并打开浏览器访问图中地址(localhost) 3.6.2 手动启动在项目根目录下打开终端,依次执行以下命令:python -m venv venvvenv\Scripts\activatepip install -r requirements.txtpython app.py 3.6.3 登录系统启动后在浏览器中访问 http://localhost:5000,使用默认管理员账号登录:• 用户名:admin• 密码:admin123 3.7 功能验证3.7.1 仪表盘验证登录成功后自动跳转至仪表盘页面,验证以下内容:• 4个统计卡片:总商品数、总分类数、总用户数、库存预警商品数• 最近5条操作记录(含操作时间、操作账号、操作内容、操作类型备注)• 4个快捷按钮:新增商品、新增分类、商品列表、数据管理3.7.2 商品管理验证点击"商品管理"页签,验证以下功能:• 搜索功能:输入商品名称关键词、所属游戏关键词、选择分类和库存状态• 排序功能:选择6种排序方式(最新创建升降序、最新修改升降序、售价升降序)• 库存预警:库存≤3显示红色,4-5显示黄色,≥6默认颜色• 折扣价显示:折扣价≠售价时灰色背景+显示原售价,=售价时显示"-"• 新增商品:填写完整信息,折扣价默认等于售价并联动• 编辑商品:修改信息后系统自动检测操作类型• 删除商品:确认后删除,显示撤回按钮• 商品详情:点击商品名称进入详情页,查看修改记录和库存变动 图:商品列表页面——搜索+排序+库存预警颜色+折扣价灰色背景 图:新增商品页面——竖向布局,折扣价联动 图:商品详情页面——上方信息+修改记录,下方编辑区 3.7.3 分类管理验证点击"分类管理"页签,验证以下功能:• 新增分类:填写分类名称创建• 搜索功能:输入分类名称关键词模糊搜索• 删除分类:无关联商品时可删除,有关联商品时拒绝• 删除撤回:删除后点击撤回恢复 图:分类管理页面——搜索+新增+删除+撤回按钮 3.7.4 数据导入导出验证点击"数据管理"页签,验证以下功能:• 数据导出:点击导出按钮,下载加密JSON文件(第1行密钥,第2行起加密数据)• 数据导入(合并模式):上传加密JSON文件并输入密钥,合并导入数据(保留当前用户、合并分类/商品/日志/图片)• 导入撤回:点击撤回按钮恢复到导入前状态• 密钥错误:输入错误密钥,提示"密钥错误,无法解密数据" 图:数据管理页面——导出按钮+导入表单+撤回按钮 3.7.5 操作记录与签名校验验证点击"操作记录"页签,验证以下功能:• 签名校验列:所有记录显示✓绿色标记(校验通过)• admin删除记录:删除后签名链自动重建,剩余记录校验仍全部通过• 审计日志:删除操作记录后,审计日志页面自动新增一条记录 图:操作记录页面——签名校验✓/✗标记 图:审计日志页面——删除操作记录的审计追踪 3.7.6 账号管理验证点击"账号设置"页签,验证以下功能:• 修改密码:输入旧密码和新密码(两次输入一致),成功后需重新登录• 创建管理员:admin可创建新管理员账号• 创建审计员:admin可创建审计员账号• 撤回管理员权限:admin可将其他管理员降级为普通用户 • 审计员登录:审计员导航仅显示仪表盘/操作记录/审计日志/账号设置 3.7.7 毛玻璃主题验证验证以下主题效果:• 背景图动态色调:系统根据背景图亮度自动切换浅色/深色主题• 浅色背景:毛玻璃明度降低,字体黑色,页面文字清晰可读• 深色背景:毛玻璃明度提高,字体白色,页面文字清晰可读• 提示框字体始终黑色• 粒子效果:35个粒子• 导航滑块:页签切换时滑块平滑滑动• 页面切换动画:内容区淡入淡出过渡 3.7.8 演示视频https://atomgit.com/MingMond/GoodsManagerDemo4 、释放资源本案例使用开发者空间资源,可以选择释放。如需清理项目文件,删除PGAMEGoodsManager目录即可。如需删除数据库文件,删除instance/目录下的goods.db文件即可。5 、扩展资料说明想了解更多关于华为云码道(CodeArts)代码智能体的可以访问:https://developer.huaweicloud.com/space/home
-
插件初始化遇到问题,部分功能可能受限。如插件不可用请联系技术支撑。错误细节:Error: Server process start failed with exit code 1, signal null, error output: [91m[1mError: [0mUnexpected error, check log file at c:\Users\Jason\.codeartsdoer\codearts-data\log\kernel-codeartsdoer-incognito-2026-07-23T102558-39932-0.log for more details Failed to start server on port 50870at L (c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:192:77)at async dQ0 (c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:192:5103)at async u.startServer (c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:9851:16628)at async c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:9851:8945at async u.retryAsync (c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:9851:6735)at async c:\Program Files\CodeArts Agent\resources\app\extensions\vscode-codebot\out\extension.js:9851:8874
yd_298117959
发表于2026-07-23 10:28:26
2026-07-23 10:28:26
最后回复
CodeArts小助手-蚂蚁
2026-07-24 09:28:47
57 2 -
在 idea 2026.2 中无法使用,啥时候能支持
yd_256118923
发表于2026-07-23 09:08:26
2026-07-23 09:08:26
最后回复
CodeArts小助手-蚂蚁
2026-07-27 15:37:00
71 4 -
一、概述1.1 案例介绍本案例展示了如何使用华为云码道(CodeArts)代码智能体快速开发一个完整的食堂菜品评价管理系统(FCEMS - Food Court Evaluation Management System)。通过自然语言对话方式,从需求分析、系统设计、代码生成到功能迭代,完整体验AI辅助开发的强大能力。系统包含:顾客端:浏览菜品、提交评价、修改/删除评价(3天内)管理端:菜品管理、评价管理、分类管理、用户管理后端API:完整的RESTful API数据库:MySQL数据库设计技术栈:后端:Node.js + Express + MySQL前端:原生HTML + CSS + JavaScript认证:JWT TokenAI辅助:华为云码道代码智能体代码仓库及demo演示视频:cid:link_31.2 适用对象个人开发者高校学生1.3 案例时间如:本案例总时长预计60分钟(包含环境准备、开发调试、功能测试)。1.4 案例流程说明:华为云码道(CodeArts)代码智能体安装部署生成项目 PRD 文档,明确系统需求和功能设计基于 PRD 文档,智能体生成完整的后端 API 和数据库设计智能体生成前端顾客端和管理端界面启动服务,测试系统功能,发现问题通过对话方式描述问题,智能体自动修复代码根据用户反馈,迭代优化系统功能1.5 资源总览本案例预计花费26.47元。体验完成后请及时释放资源,避免产生多余的费用。资源名称规格单价(元)华为云码道(CodeArts)代码智能体基础版26.47二、环境和资源准备2.1 开通华为云码道CodeArts登录华为云码道,开通CodeArts服务。在CodeArts中创建项目,进入代码智能体(CodeArts IDE)开发环境。2.2 本地开发环境要求在CodeArts IDE中开发时,需确保本地已安装以下工具:Node.js 18+MySQL 8.0+三、构建食堂菜品评价管理系统3.1 需求分析与系统设计在码道对话界面选择 氛围编程模式(Vibe-Coding),发送下述请求。实现某食堂的菜品评价管理系统。用于顾客评价反馈食堂菜品,为食堂管理人员提供改进依据。实现菜品相关信息(品名、原材料、照片、价格等)管理。实现菜品的评价、打分、查询等功能。这是我的基础要求,请基于这个要求的基础上给出更加详细具体的项目设计报告。3.2 后端开发1)项目结构说明:使用CodeArts智能体生成的后端项目结构:fcems/backend/ ├── src/ │ ├── app.js # 主应用入口 │ ├── config/ │ │ └── database.js # 数据库配置 │ ├── middleware/ │ │ └── auth.js # 认证中间件 │ └── routes/ │ ├── auth.js # 认证路由 │ ├── dish.js # 菜品路由 │ ├── review.js # 评价路由 │ ├── category.js # 分类路由 │ ├── analytics.js # 数据分析路由 │ └── user.js # 用户路由 ├── database/ │ ├── init.js # 数据库初始化脚本 │ ├── add-test-data.js # 添加测试数据 │ └── add-reviews.js # 添加评价数据 ├── .env # 环境变量配置 ├── package.json # 依赖配置 └── package-lock.json根据项目设计报告开发完整的食堂菜品评价管理系统。2)数据库配置 (src/config/database.js)const mysql = require('mysql2/promise'); require('dotenv').config(); const dbConfig = { host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', database: process.env.DB_NAME || 'fcems', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }; const pool = mysql.createPool(dbConfig); async function query(sql, params) { const [rows] = await pool.execute(sql, params); return rows; } module.exports = { query, transaction, pool }; 3)认证中间件 (src/middleware/auth.js)const jwt = require('jsonwebtoken'); const { query } = require('../config/database'); const auth = async (req, res, next) => { try { const token = req.header('Authorization')?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ success: false, message: '请先登录' }); } const decoded = jwt.verify(token, process.env.JWT_SECRET); const users = await query('SELECT * FROM user WHERE id = ? AND status = 1', [decoded.userId]); if (users.length === 0) { return res.status(401).json({ success: false, message: '用户不存在或已被禁用' }); } req.user = users[0]; req.token = token; next(); } catch (error) { res.status(401).json({ success: false, message: '认证失败,请重新登录' }); } }; const requireRole = (...roles) => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ success: false, message: '权限不足' }); } next(); }; }; module.exports = { auth, requireRole }; 4)菜品路由 (src/routes/dish.js)关键功能:获取菜品列表、添加菜品、编辑菜品、上下架、删除// 获取菜品列表(支持状态筛选) router.get('/', async (req, res) => { try { const { page = 1, limit = 10, status, search } = req.query; let sql = ` SELECT d.*, c.name as category_name, (SELECT AVG(rating) FROM review WHERE dish_id = d.id AND status = 1) as avg_rating FROM dish d LEFT JOIN category c ON d.category_id = c.id WHERE d.deleted_at IS NULL `; if (status !== undefined) { sql += ' AND d.status = ?'; params.push(parseInt(status)); } // ... 其他筛选条件 const dishes = await query(sql, params); res.json({ success: true, data: { dishes, total, page, limit } }); } catch (error) { res.status(500).json({ success: false, message: '获取菜品列表失败' }); } }); // 添加菜品(检查名称重复) router.post('/', auth, requireRole(2, 3), async (req, res) => { const { name, category_id, price } = req.body; // 检查名称是否重复 const [existingDish] = await query( 'SELECT id FROM dish WHERE name = ? AND deleted_at IS NULL', [name] ); if (existingDish) { return res.status(400).json({ success: false, message: '菜品名称已存在,请使用其他名称' }); } // 插入新菜品 const result = await query( 'INSERT INTO dish (name, category_id, price, ...) VALUES (?, ?, ?, ...)', [name, category_id, price, ...] ); res.json({ success: true, message: '菜品添加成功', data: { id: result.insertId } }); }); 5)评价路由 (src/routes/review.js)关键功能:提交评价、修改评价(3天内)、删除评价(3天内)、管理员回复// 修改评价(3天内) router.put('/:id', auth, async (req, res) => { const { id } = req.params; const { rating, content } = req.body; const [review] = await query('SELECT * FROM review WHERE id = ?', [id]); if (review.user_id !== req.user.id) { return res.status(403).json({ success: false, message: '只能修改自己的评价' }); } // 检查是否在3天内 const reviewDate = new Date(review.created_at); const now = new Date(); const daysDiff = (now - reviewDate) / (1000 * 60 * 60 * 24); if (daysDiff > 3) { return res.status(403).json({ success: false, message: '评价提交超过3天,不允许修改' }); } // 更新评价 await query('UPDATE review SET rating = ?, content = ? WHERE id = ?', [rating, content, id]); res.json({ success: true, message: '评价修改成功' }); }); // 管理员回复评价 router.post('/:id/reply', auth, requireRole(2, 3), async (req, res) => { const { id } = req.params; const { reply } = req.body; await query('UPDATE review SET admin_reply = ? WHERE id = ?', [reply, id]); res.json({ success: true, message: '回复成功' }); }); 至此后端API路由创建完成后端代码文件列表3.3 前端代码生成生成的前端界面包括:顾客端:菜品浏览、评价提交、我的评价管理管理端:登录页、仪表盘、菜品管理、评价管理、分类管理、用户管理美观的UI设计,使用渐变色和现代风格1)顾客端菜品展示async function loadDishes(page = 1) { let url = `${API_BASE}/dishes?page=${page}&limit=12&status=1`; const res = await fetch(url); const data = await res.json(); if (data.success) { renderDishes(data.data.dishes); } } function renderDishes(dishes) { const container = document.getElementById('dishesGrid'); container.innerHTML = dishes.map(dish => ` <div class="dish-card" onclick="showDishDetail(${dish.id})"> <div class="dish-image-container"> <img src="${dish.images ? JSON.parse(dish.images)[0] : ''}" onerror="this.parentElement.style.background='linear-gradient(135deg, #667eea 0%, #764ba2 100%)'"> </div> <div class="dish-info"> <h3>${dish.name}</h3> <div class="dish-rating">★ ${parseFloat(dish.avg_rating || 0).toFixed(1)}</div> <div class="dish-price">¥${dish.price}</div> </div> </div> `).join(''); } 2)评价提交(含时间限制检查)async function submitReview() { const review = { dish_id: currentDish.id, rating: selectedRating, tags: selectedTags, content: document.getElementById('reviewContent').value, is_anonymous: document.getElementById('isAnonymous').checked }; const res = await fetch(`${API_BASE}/reviews`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify(review) }); const data = await res.json(); if (data.success) { alert(`评价提交成功!获得${data.data.points_earned}积分`); loadReviews(currentDish.id); } else { alert(data.message); } } 3)管理端菜品管理async function loadDishesPage() { const res = await fetch(`${API_BASE}/dishes?limit=100`, { headers: getAuthHeaders() }); const data = await res.json(); // 显示所有未删除的菜品(包括上架和下架) document.getElementById('pageContent').innerHTML = ` <table class="table"> <thead> <tr> <th>菜品名称</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> ${data.data.dishes.map(dish => ` <tr> <td>${dish.name}</td> <td> <span class="badge ${dish.status === 1 ? 'badge-success' : 'badge-danger'}"> ${dish.status === 1 ? '上架' : '下架'} </span> </td> <td> <button onclick="editDish(${dish.id})">编辑</button> <button onclick="toggleDishStatus(${dish.id}, ${dish.status})"> ${dish.status === 1 ? '下架' : '上架'} </button> <button onclick="deleteDish(${dish.id})">删除</button> </td> </tr> `).join('')} </tbody> </table> `; } 4)管理员回复评价async function replyReview(id) { // 获取评价详情,显示已有回复 const res = await fetch(`${API_BASE}/reviews/all?limit=100`, { headers: getAuthHeaders() }); const data = await res.json(); const review = data.data.reviews.find(r => r.id === id); const existingReply = review ? (review.admin_reply || '') : ''; document.getElementById('modalBody').innerHTML = ` <h2>${existingReply ? '修改回复' : '回复评价'}</h2> <textarea id="replyContent" rows="4">${existingReply}</textarea> <button onclick="submitReply(${id})">提交</button> `; document.getElementById('modal').classList.add('active'); } 至此前端界面开发完成3.4 功能测试与问题修复3.4.1 测试系统功能1)测试顾客端访问:fcems/frontend/customer/index.html测试步骤:注册/登录账号浏览菜品列表点击菜品查看详情提交评价在"我的评价"中修改/删除评价【注册/登录账号页面】【浏览菜品列表】【点击菜品查看详情】【提交评价】【在"我的评价"中修改/删除评价】2)测试管理端访问:fcems/frontend/admin/login.html登录账号:用户名:admin密码:admin123测试步骤:查看仪表盘数据进入菜品管理,测试添加、编辑、上下架、删除进入评价管理,测试审核、回复进入分类管理,测试添加、编辑、删除进入用户管理,查看用户列表【管理端登录页面】【管理端仪表盘页面】【管理端菜品管理页面:可以添加、编辑、上下架、删除菜品】【添加菜品】【编辑菜品】【下架菜品】【上架菜品】【删除菜品】【管理端评价管理页面:可以审核、回复评价】【驳回评价】【回复评价】【管理端分类管理页面:可以添加、编辑、删除菜品分类】【点击“添加分类”按键】【点击分类的“删除”按键】【管理端用户管理页面,查看用户列表】3.4.2 发现问题并修复问题1:管理端登录后提示"无法连接到服务器"问题描述:登录成功后,页面弹出提示框"无法连接到服务器,请检查后端服务是否启动"原因分析:showPage函数使用了event.target,但从checkAuth调用时没有event对象修复方法:在CodeArts对话框中描述问题:管理端登录后弹出"无法连接到服务器"提示,控制台错误:"Cannot read properties of undefined (reading 'target')"请修复这个问题。CodeArts自动修复代码:// 修改前 function showPage(page) { event.target.closest('.menu-item').classList.add('active'); // ... } // 修改后 function showPage(page, event) { if (event && event.target) { event.target.closest('.menu-item').classList.add('active'); } // ... } 问题2:菜品编辑按钮无反应问题描述:管理端菜品管理中,点击"编辑"按钮没有反应原因分析:缺少editDish和updateDish函数修复方法:在CodeArts对话框中输入:菜品管理的编辑按钮点击无反应,请添加菜品编辑功能。CodeArts生成编辑功能代码:async function editDish(id) { const [dishRes, categoriesRes] = await Promise.all([ fetch(`${API_BASE}/dishes/${id}`, { headers: getAuthHeaders() }), fetch(`${API_BASE}/categories`, { headers: getAuthHeaders() }) ]); const dishData = await dishRes.json(); const categoriesData = await categoriesRes.json(); if (dishData.success) { const dish = dishData.data; // 显示编辑表单,填充已有数据 document.getElementById('modalBody').innerHTML = ` <h2>编辑菜品</h2> <form onsubmit="updateDish(event, ${id})"> <input id="editDishName" value="${dish.name}" required> <select id="editDishCategory"> ${categoriesData.data.map(c => `<option value="${c.id}" ${c.id === dish.category_id ? 'selected' : ''}>${c.name}</option>` ).join('')} </select> <input id="editDishPrice" value="${dish.price}" required> <button type="submit">保存</button> </form> `; document.getElementById('modal').classList.add('active'); } } async function updateDish(e, id) { e.preventDefault(); const dish = { name: document.getElementById('editDishName').value, category_id: parseInt(document.getElementById('editDishCategory').value), price: parseFloat(document.getElementById('editDishPrice').value) }; const res = await fetch(`${API_BASE}/dishes/${id}`, { method: 'PUT', headers: getAuthHeaders(), body: JSON.stringify(dish) }); if ((await res.json()).success) { closeModal(); loadDishesPage(); } } 问题3:下架菜品从列表中消失问题描述:菜品下架后,从管理端列表中消失,无法重新上架需求:下架菜品应保留在列表中显示"下架"状态标签提供"上架"按钮修复方法:在CodeArts对话框中输入:菜品管理应该显示所有未删除的菜品,包括下架的菜品。下架菜品应该保留编辑、删除、上架按钮。顾客端只显示上架的菜品。CodeArts修复:后端修改:// 修改查询条件,只过滤deleted_at,不过滤status let sql = ` SELECT d.*, c.name as category_name FROM dish d LEFT JOIN category c ON d.category_id = c.id WHERE d.deleted_at IS NULL`; // 添加status参数支持 if (status !== undefined) { sql += ' AND d.status = ?'; params.push(parseInt(status)); } 前端修改:// 顾客端:只获取上架的菜品 let url = `${API_BASE}/dishes?page=${page}&limit=12&status=1`; // 管理端:获取所有未删除的菜品 let url = `${API_BASE}/dishes?limit=100`; 问题4:添加菜品时名称可重复问题描述:可以添加同名菜品,导致数据混乱修复方法:在CodeArts对话框中输入:添加菜品时,新菜品名称不允许与已有菜品名称相同。CodeArts添加名称检查:router.post('/', auth, requireRole(2, 3), async (req, res) => { const { name } = req.body; // 检查名称是否重复 const [existingDish] = await query( 'SELECT id FROM dish WHERE name = ? AND deleted_at IS NULL', [name] ); if (existingDish) { return res.status(400).json({ success: false, message: '菜品名称已存在,请使用其他名称' }); } // 继续添加... }); 问题5:管理员回复无法保存和显示对评价的回复问题描述:回复内容没有被保存下次点击"回复"时无法修改回复内容没有显示在顾客端修复方法:在CodeArts对话框中输入:评价回复功能问题: 1. 回复内容应该被保存 2. 下次点击回复时可以修改上一次的回复 3. 回复内容应该显示在顾客端对应评价的下方CodeArts修复:管理端修改:async function replyReview(id) { // 获取评价详情,显示已有回复 const res = await fetch(`${API_BASE}/reviews/all?limit=100`); const data = await res.json(); const review = data.data.reviews.find(r => r.id === id); const existingReply = review ? (review.admin_reply || '') : ''; // 显示编辑表单,填充已有回复 document.getElementById('modalBody').innerHTML = ` <h2>${existingReply ? '修改回复' : '回复评价'}</h2> <textarea id="replyContent">${existingReply}</textarea> <button onclick="submitReply(${id})">提交</button> `; } 顾客端修改:// 在评价下方显示管理员回复 ${review.admin_reply ? ` <div class="admin-reply" style=" margin-top: 1rem; padding: 1rem; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-left: 4px solid #667eea;"> <div style="color: #667eea; font-weight: 600;"> <i class="fas fa-reply"></i> 管理员回复 </div> <div>${review.admin_reply}</div> </div>` : ''} 四、扩展资料说明华为云码道CodeArts实战速成:cid:link_0华为云开发者AI训练营:cid:link_2
-
嘉欣猎头公司管理系统:码道驱动,智能匹配一、概述1.1 案例介绍本案例采用华为云码道(CodeArts)代码智能体作为核心开发工具,从零构建一个互联网行业猎头公司管理系统。系统面向猎头顾问、软件工程师、企业HR三类角色,实现了工程师信息管理、公司职位管理、智能匹配推荐、面试流程状态机、职业轨迹追踪等核心功能。开发过程采用V1快速原型→V2功能完善→V3亮点打磨的三轮迭代模式,全程使用码道代码智能体辅助开发,充分展示了码道在项目骨架生成、代码续写、智能问答、SDD规范驱动开发等场景下的实践应用。代码仓库:https://gitcode.com/2301_78976454/headhunter-management-system在线演示:http://1.95.91.47/(测试账号:headhunter/123456、zhangsan/123456、huawei_hr/123456)1.2 适用对象高校学生个人开发者企业开发者1.3 案例时间本案例总时长预计120分钟(不含环境安装时间)。1.4 案例流程说明:环境准备:安装华为云码道(CodeArts)代码智能体、Node.js、MySQL;V1快速原型:使用码道生成项目骨架,完成5张核心表+基础CRUD+简单匹配推荐;V2功能完善:新增JWT认证+三角色权限、面试流程状态机、职业轨迹、多维度匹配算法;V3亮点打磨:集成ECharts数据仪表盘、匹配度雷达图、职位对比、通知中心;部署上线:部署至华为云ECS,提供可访问的演示环境。1.5 资源总览本案例预计花费0元(使用免费资源即可完成开发)。如需部署到华为云ECS,预计花费约0.6元/小时。体验完成后请及时释放资源,避免产生多余的费用。资源名称规格单价(元)华为云码道(CodeArts)代码智能体通用体验版免费Node.jsv22.x免费MySQL8.0+免费弹性云服务器ECS(可选,用于部署)2 vCPUs 4GiB0.6/小时二、环境和资源准备2.1 安装华为云码道(CodeArts)代码智能体访问华为云码道下载页面,下载并安装码道代码智能体。安装完成后,使用华为云账号登录。2.2 安装Node.js开发环境访问Node.js官网 下载并安装LTS版本(建议v22.x)。安装完成后,在终端验证:node --version npm --version 2.3 安装MySQL数据库安装MySQL 8.0及以上版本,并确保root用户可正常登录。创建项目数据库:mysql -u root -p CREATE DATABASE IF NOT EXISTS headhunter_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 2.4 完成码道CodeArts实战速成考试访问码道CodeArts实战速成考试,完成在线学习并通过考试,获取通过证书。三、构建嘉欣猎头公司管理系统3.1 项目结构说明本项目采用前后端分离架构,后端使用Node.js + Express + Sequelize + MySQL,前端使用Vue3 + Vite + Element Plus + ECharts。project30/ ├── sql/ │ └── init.sql # 数据库建表+示例数据脚本 ├── backend/ │ ├── package.json # 后端依赖配置 │ └── src/ │ ├── app.js # Express入口,路由注册 │ ├── config/ │ │ └── database.js # MySQL数据库连接配置 │ ├── middleware/ │ │ └── auth.js # JWT认证中间件+角色权限控制 │ ├── models/ │ │ ├── index.js # 模型统一导出 │ │ ├── User.js # 用户模型(三角色) │ │ ├── Engineer.js # 软件工程师模型 │ │ ├── Company.js # 互联网公司模型 │ │ ├── Department.js # 部门模型 │ │ ├── Position.js # 职位模型 │ │ ├── Recommendation.js # 推荐记录模型 │ │ ├── InterviewProcess.js # 面试流程模型 │ │ └── CareerHistory.js # 职业轨迹模型 │ └── routes/ │ ├── auth.js # 登录/注册/个人信息 │ ├── engineer.js # 工程师CRUD+按姓名搜索+职业查询 │ ├── company.js # 公司CRUD+已招聘查询 │ ├── department.js # 部门CRUD │ ├── position.js # 职位CRUD+搜索 │ ├── recommendation.js # 推荐+智能匹配 │ ├── interview.js # 面试流程+看板 │ └── career.js # 职业轨迹CRUD ├── frontend/ │ ├── package.json # 前端依赖配置 │ ├── vite.config.js # Vite配置+API代理 │ ├── index.html # 入口HTML │ └── src/ │ ├── main.js # Vue3入口 │ ├── App.vue # 根组件 │ ├── api/ │ │ ├── request.js # Axios封装+JWT拦截器 │ │ └── index.js # 全部API接口定义 │ ├── router/ │ │ └── index.js # 路由配置+角色守卫 │ ├── components/ │ │ └── NotificationCenter.vue # 通知中心组件 │ ├── layouts/ │ │ ├── HeadhunterLayout.vue # 猎头顾问布局 │ │ ├── EngineerLayout.vue # 工程师布局 │ │ └── HrLayout.vue # 企业HR布局 │ └── views/ │ ├── LoginView.vue # 登录/注册页 │ ├── headhunter/ # 猎头端7个页面(含信息查询) │ ├── engineer/ # 工程师端6个页面 │ └── hr/ # HR端4个页面项目源码已上传至GitCode,可通过以下命令下载到本地:git clone https://gitcode.com/2301_78976454/headhunter-management-system.git3.2 V1快速原型:搭建核心链路3.2.1 使用码道创建项目骨架在码道IDE中,打开终端,创建项目目录并初始化后端:mkdir project30 && cd project30 mkdir sql backend frontend cd backend && npm init -y 使用码道智能问答功能,输入提示词:“帮我创建一个Express + Sequelize + MySQL的后端项目骨架,包含5个模型:Engineer、Company、Department、Position、Recommendation”码道会自动生成模型文件和基础路由代码,我们在此基础上进行修改。3.2.2 数据库设计与初始化使用码道辅助编写SQL建表脚本,核心5张表设计如下:CREATE TABLE engineer ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT '姓名', age INT COMMENT '年龄', gender VARCHAR(10) COMMENT '性别', current_company VARCHAR(100) COMMENT '现工作公司', current_salary DECIMAL(10,2) COMMENT '现薪水(元/月)', current_position VARCHAR(100) COMMENT '现职务', expected_salary DECIMAL(10,2) COMMENT '期望薪水(元/月)', skills VARCHAR(500) COMMENT '擅长技能,逗号分隔', expected_city VARCHAR(50) COMMENT '期望城市', open_to_headhunter TINYINT DEFAULT 1 COMMENT '是否接受猎头推荐', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT '软件工程师表'; 执行初始化脚本:mysql -u root -p < sql/init.sql3.2.3 后端核心路由以推荐路由为例,V1实现了简单的技能+薪资+城市匹配算法:function calcMatchScore(engineer, position) { let score = 0; const eSkills = (engineer.skills || '').split(',').map(s => s.trim().toLowerCase()); const pSkills = (position.required_skills || '').split(',').map(s => s.trim().toLowerCase()); if (eSkills.length > 0 && pSkills.length > 0) { const matched = pSkills.filter(ps => eSkills.some(es => es.includes(ps) || ps.includes(es))); score += (matched.length / pSkills.length) * 50; } if (engineer.expected_salary >= position.salary_min && engineer.expected_salary <= position.salary_max) { score += 30; } if (position.location.includes(engineer.expected_city)) { score += 20; } return Math.min(100, Math.round(score * 100) / 100); } 3.2.4 前端基础页面使用码道代码续写功能,快速生成Vue3 + Element Plus的CRUD页面。V1阶段共6个页面:工作台、工程师管理、公司管理、职位管理、推荐管理、信息查询。3.2.5 V1验证启动后端和前端服务:# 后端 cd backend && npm install && npm run dev # 前端(需在纯英文路径下运行) cd frontend && npm install && npm run devV1交付标准:工程师信息增删改查、公司→部门→职位层级管理、猎头推送推荐+工程师接受/拒绝、按公司/按人双向查询。3.3 V2功能完善:满足全部硬性要求3.3.1 JWT三角色认证新增User模型和auth中间件,实现猎头/工程师/企业HR三角色认证:const jwt = require('jsonwebtoken'); const SECRET = 'jiaxin_headhunter_2026'; function authMiddleware(req, res, next) { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) return res.status(401).json({ code: 1, msg: '未登录' }); try { req.user = jwt.verify(token, SECRET); next(); } catch { res.status(401).json({ code: 1, msg: 'token无效或已过期' }); } } function roleMiddleware(...roles) { return (req, res, next) => { if (!roles.includes(req.user.role)) return res.status(403).json({ code: 1, msg: '无权限' }); next(); }; } 3.3.2 面试流程状态机实现6状态状态机:推荐(recommended) → 确认(confirmed) → 面试(interviewing) → 发Offer(offered) → 入职(onboarded) 或 失败(failed):const VALID_TRANSITIONS = { recommended: ['confirmed', 'failed'], confirmed: ['interviewing', 'failed'], interviewing: ['offered', 'failed'], offered: ['onboarded', 'failed'], onboarded: [], failed: [] }; InterviewProcess.canTransitionTo = function (currentStatus, newStatus) { return VALID_TRANSITIONS[currentStatus]?.includes(newStatus) || false; }; 当状态变为onboarded时,自动更新工程师的当前公司和职位,并写入职业轨迹记录。3.3.3 多维度加权匹配算法将V1的简单匹配升级为分项评分(技能50分+薪资30分+城市20分):function calcMatchDetail(engineer, position) { let skillScore = 0, salaryScore = 0, cityScore = 0; // 技能匹配(满分50分) const eSkills = (engineer.skills || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean); const pSkills = (position.required_skills || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean); if (eSkills.length > 0 && pSkills.length > 0) { const matched = pSkills.filter(ps => eSkills.some(es => es.includes(ps) || ps.includes(es))); skillScore = (matched.length / pSkills.length) * 50; } // 薪资匹配(满分30分) if (engineer.expected_salary && position.salary_min && position.salary_max) { const mid = (Number(position.salary_min) + Number(position.salary_max)) / 2; const diff = Math.abs(Number(engineer.expected_salary) - mid) / mid; if (diff <= 0.1) salaryScore = 30; else if (diff <= 0.2) salaryScore = 25; else if (diff <= 0.3) salaryScore = 20; else salaryScore = 10; } // 城市匹配(满分20分) if (engineer.expected_city && position.location) { if (position.location.includes(engineer.expected_city)) cityScore = 20; } const total = Math.min(100, Math.round((skillScore + salaryScore + cityScore) * 100) / 100); return { total, skill_score: skillScore, salary_score: salaryScore, city_score: cityScore }; } 3.3.4 三角色独立界面V2将V1的单页面Tab切换升级为三角色独立布局:猎头顾问:深蓝侧边栏(#1a1a2e),包含工作台、人才库、职位库、智能匹配、面试看板、数据统计、信息查询软件工程师:靛蓝侧边栏(#0f3460),包含首页、个人档案、推荐职位、职位对比、面试记录、职业轨迹企业HR:紫色侧边栏(#533483),包含招聘概览、职位管理、候选人、已录用3.3.5 V2验证V2交付标准:三角色JWT认证与权限隔离、面试全流程状态流转、多维度智能匹配推荐、跳槽后按公司/按人双向查询(课题硬性要求✅)、工程师职业轨迹展示。3.3.6 自检修复:满足课题硬性要求对照课题30的硬性要求进行自检,发现并修复了以下问题:问题1:每个部门至少3个职位原始示例数据仅11个职位,部分部门不足3个。补充SQL示例数据至30个职位,确保10个部门每个至少3个职位:-- 示例:技术部补充职位(已有3个,此处展示格式) INSERT INTO position (title, department_id, ...) VALUES ('全栈工程师', 1, ...), ('DevOps工程师', 1, ...), ('数据开发工程师', 1, ...); 问题2:按工程师姓名查找职业轨迹原仅支持按工程师ID查询,新增按姓名模糊搜索API:// backend/src/routes/engineer.js - 新增按姓名搜索路由 router.get('/search/name/:name', async (req, res) => { const engineers = await Engineer.findAll({ where: { name: { [Op.like]: `%${req.params.name}%` } } }); res.json({ code: 0, data: engineers }); }); 前端查询页面同步升级,支持"按ID"和"按姓名"两种搜索模式,按姓名搜索时展示匹配工程师列表,可展开查看完整职业轨迹时间线。注意:Express路由注册顺序中,/search/name/:name 必须在 /:id 之前注册,否则会被参数路由拦截。3.4 V3亮点打磨:冲击满分3.4.1 ECharts数据统计仪表盘在猎头端新增数据统计页面,使用ECharts实现6个可视化图表:// 技能分布柱状图 skillChart.setOption({ xAxis: { type: 'category', data: d.topSkills.map(i => i.name) }, yAxis: { type: 'value', name: '人数' }, series: [{ type: 'bar', data: d.topSkills.map(i => i.value), itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#409eff' }, { offset: 1, color: '#79bbff' } ])} }] }); 6个图表覆盖:热门技能TOP10柱状图、期望城市饼图、期望薪资玫瑰图、面试状态环形图、公司招聘横向柱图、职位薪资热力图。3.4.2 匹配度雷达图在智能匹配页面,为每个推荐结果生成ECharts雷达图,直观展示技能/薪资/城市三维匹配情况:chart.setOption({ radar: { indicator: [ { name: '技能匹配', max: 50 }, { name: '薪资匹配', max: 30 }, { name: '城市匹配', max: 20 } ], shape: 'circle' }, series: [{ type: 'radar', data: [{ value: [item.skill_score, item.salary_score, item.city_score] }] }] }); 3.4.3 职位对比功能工程师端新增职位对比页面,可选择两个已接受的推荐职位并排对比,同时生成双雷达图叠加展示:series: [{ type: 'radar', data: [ { value: [...], name: '职位A', areaStyle: { color: 'rgba(64,158,255,0.15)' } }, { value: [...], name: '职位B', areaStyle: { color: 'rgba(103,194,58,0.15)' } } ] }] 3.4.4 通知中心实现基于轮询的通知中心组件,30秒自动刷新,三角色差异化通知:猎头:待处理推荐数、已入职人数工程师:新推荐数、待参加面试数HR:候选人接受数onMounted(() => { fetchNotifications() timer = setInterval(fetchNotifications, 30000) }) 3.4.5 后端统计API增强新增 /api/stats/dashboard 接口,一次请求返回6维度统计数据;新增 /api/stats/notifications 接口,按角色返回差异化通知。3.4.6 V3验证V3交付标准:ECharts数据仪表盘6图、匹配度雷达图、职位对比功能、通知中心轮询。3.4.7 系统运行效果上图展示了猎头顾问登录后的数据统计仪表盘,包含热门技能TOP10柱状图、期望城市饼图、期望薪资玫瑰图、面试状态环形图、公司招聘横向柱图、职位薪资热力图共6个可视化图表。3.5 部署至华为云ECS(可选)3.5.1 购买ECS弹性云服务器登录华为云控制台,购买ECS实例(2vCPUs 4GiB,Ubuntu 22.04),配置安全组开放80端口,购买弹性公网IP(按流量计费)。3.5.2 部署后端# 安装Node.js 22 curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash - sudo apt-get install -y nodejs # 安装PostgreSQL sudo apt-get install -y postgresql postgresql-contrib sudo systemctl start postgresql # 配置数据库 sudo -u postgres psql -c "ALTER USER postgres PASSWORD '123456';" sudo -u postgres psql -c "CREATE DATABASE headhunter_db;" # 下载项目代码 sudo apt-get install -y git cd /root && git clone https://gitcode.com/2301_78976454/headhunter-management-system.git # 导入数据库 cp /root/headhunter-management-system/sql/init_pg.sql /tmp/ sudo -u postgres psql -d headhunter_db -f /tmp/init_pg.sql # 安装后端依赖并启动 cd /root/headhunter-management-system/backend && npm install nohup node src/app.js > /root/backend.log 2>&1 & 3.5.3 部署前端# 构建前端 cd /root/headhunter-management-system/frontend sed -i '/esbuild-windows-64/d' package.json npm install && npm run build # 部署到nginx sudo mkdir -p /var/www/headhunter sudo cp -r dist/* /var/www/headhunter/ sudo chmod -R 755 /var/www/headhunter3.5.4 配置Nginxsudo apt-get install -y nginx创建Nginx配置文件 /etc/nginx/sites-available/headhunter:server { listen 80; server_name _; root /var/www/headhunter; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } sudo ln -sf /etc/nginx/sites-available/headhunter /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl restart nginx3.5.5 开放安全组端口在华为云控制台:ECS详情 → 安全组 → 入方向规则 → 添加规则:协议TCP,端口80,源地址0.0.0.0/0。3.6 系统测试3.6.1 演示环境系统已部署至华为云ECS,可通过以下地址访问:访问地址:http://1.95.91.47测试账号:角色用户名密码功能范围猎头顾问headhunter123456工作台、人才库、职位库、智能匹配、面试看板、数据统计、信息查询工程师zhangsan123456首页、个人档案、推荐职位、职位对比、面试记录、职业轨迹企业HRhuawei_hr123456招聘概览、职位管理、候选人、已录用3.6.2 功能测试用例编号测试功能操作步骤预期结果T01猎头登录输入headhunter/123456,选择猎头顾问,点击登录跳转到猎头工作台页面T02人才库管理点击"人才库",查看工程师列表显示8名工程师信息,支持按技能/城市/薪资筛选T03职位层级管理点击"职位库",展开公司→部门→职位显示5家公司、10个部门、30个职位的层级结构T04智能匹配推荐点击"智能匹配",选择工程师,点击"一键匹配"生成匹配推荐列表,显示匹配度评分和雷达图T05面试流程流转点击"面试看板",拖拽卡片变更状态状态按推荐→确认→面试→Offer→入职流转T06数据统计仪表盘点击"数据统计"显示6个ECharts图表(技能柱状图、城市饼图等)T07按姓名查询职业轨迹点击"信息查询",切换"按姓名",输入"张"模糊搜索出张三,展开查看职业轨迹时间线T08按公司查询已招聘在"信息查询"左侧,选择公司点击查询显示该公司已招聘工程师列表T09工程师登录输入zhangsan/123456,选择工程师跳转到工程师首页T10推荐职位响应工程师端点击"推荐职位",接受/拒绝推荐更新推荐状态,猎头端同步可见T11职位对比工程师端点击"职位对比",选择两个职位显示双雷达图叠加对比T12HR登录输入huawei_hr/123456,选择企业HR跳转到HR招聘概览T13候选人查看HR端点击"候选人"显示接受推荐的候选人列表T14通知中心任意角色登录后,点击右上角铃铛图标显示角色差异化通知,30秒自动刷新T15角色权限隔离猎头账号尝试访问/engineer路径自动重定向回/headhunter,无权限页面不可访问四、释放资源4.1 删除ECS弹性云服务器进入ECS列表,点击全选按钮,点击"更多 → 删除"。在对话框中选择"释放云服务器绑定的公网IP地址"和"删除云服务器挂载的数据盘",点击"是"。五、扩展资料说明华为云码道(CodeArts)代码智能体:https://codearts.huaweicloud.comVue3官方文档:https://cn.vuejs.orgElement Plus组件库:https://element-plus.org/zh-CNECharts数据可视化:https://echarts.apache.org/zh/index.htmlSequelize ORM文档:https://sequelize.orgExpress框架文档:https://expressjs.com
-
一、概述1.1 案例介绍随着校园、社区及公共场所人员流动增加,物品遗失与寻找需求日益普遍。传统失物招领方式通常依赖线下公告、微信群等渠道,存在信息分散、检索困难、匹配效率低等问题。为提升失物信息流转效率,本案例设计并开发了一个智能化失物招领平台,实现失物信息与招领信息的集中管理和便捷查询。本案例基于 AI Agent 自动化开发模式,利用华为开发者空间开发平台和华为云码道(CodeArts)代码智能体完成全流程应用构建。应用采用前后端分离架构,选择采用 Vue3 前端、Flask 后端、SQLite 数据库的技术栈进行开发。系统支持用户注册登录、失物信息发布、招领信息发布、物品图片上传、关键词搜索、分类筛选、详情查看、站内联系以及个人信息管理等核心功能,同时提供信息状态管理能力,帮助用户完成从信息发布到物品找回的完整业务流程。本案例构建的项目和文档已保存至GitCode,可自行下载源码。Demo演示视频技术选型:华为开发者空间开发平台 - 云开发环境(容器):华为云提供的一站式云上开发环境,支持多种主流开发语言和运行环境,开发者无需进行复杂的本地环境配置即可快速开展应用开发。本案例中选用云开发环境(容器)作为应用的开发与运行环境,为项目提供统一稳定的基础环境,避免因操作系统、依赖版本等环境差异导致的应用运行问题,提高项目构建和部署效率。华为云码道(CodeArts)代码智能体:华为云提供的智能化软件开发工具,融合代码大模型、AI IDE 和 Code Agent 等能力,能够理解项目需求并辅助完成代码生成、代码优化和工程构建。本案例中使用华为云码道(CodeArts)代码智能体作为核心开发辅助工具,根据失物招领应用需求完成前后端功能开发,加速 Web 应用构建过程,降低应用开发门槛。1.2 适用对象企业开发者个人开发者高校学生1.3 案例时间本案例总时长预计60分钟。1.4 案例流程说明:VSCode安装开发者空间插件,远程连接云开发环境(容器);云开发环境(容器)搭建华为云码道(CodeArts)代码智能体;对话码道,开发SDD并检验是否符合项目预期;对话码道,编写代码生成项目;启动项目,验证项目功能;对话码道,更新优化SDD,沉淀项目知识库。1.5 资源总览本案例预计花费0元。资源名称规格单价(元)开发者空间云开发环境ARM | 2vCPUs 4GB | All In One 开发环境免费华为云码道(CodeArts)代码智能体体验版免费二、环境和资源准备2.1 VSCode 远程连接云开发环境参考案例《华为开发者空间 - 云开发环境(容器)IDE插件远程连接操作指导》中的“二、 云开发环境IDE插件远程连接操作指导”章节,完成 VS Code 到云开发环境(容器)的远程连接。2.2 VSCode 搭建码道智能体VS Code 连接云开发环境的状态下,在“扩展”中搜索“CodeArts”,安装“CodeArts Agent”插件。安装完成后,VS Code中执行命令:CodeArts Agent: Open the Q&A Dialog右侧弹出的侧栏为码道智能体对话界面。登录后进行对话测试,确认码道智能体搭建成功且能够正常使用。三、使用码道构建失物招领平台系统3.1 需求分析与功能设计项目名称: Anima - 失物招领平台项目定位:现代化失物招领平台,支持用户发布失物和招领信息、浏览与搜索信息、联系发布者,并具备完整的用户管理能力。技术栈:前端:Vue3 + Vite + TypeScript后端:Python Flask数据库:SQLite核心功能:用户登录和信息管理用户发布失物信息和招领信息,可对自己发布的信息进行编辑、删除、修改完成状态展示最新发布的信息,可分类浏览,可按条件检索查看物品详情并联系发布者管理员管理用户和信息3.2 Prompt 设计和 SDD 生成(1)根据以上项目需求,设计如下 Prompt:根据以下需求描述,开发一个失物招领 Web 应用。 1. 项目概述 项目名称:Anima 项目定位:现代化的失物招领平台,支持用户发布失物和招领信息、浏览与搜索信息、联系发布者,并具备完整的用户管理能力。 2. 技术框架 技术栈:实现前后端分离,前端采用Vue3,后端采用Python Flask,数据库使用SQLite。具备良好的可维护性、可扩展性和规范的项目结构,API 设计遵循 RESTful 风格。 3. 核心功能 (1)用户注册、登录、退出及个人信息管理。 (2)发布失物信息和招领信息,并支持上传多张图片。 (3)首页展示最新发布的信息,可按失物/招领分类浏览。 (4)支持按物品名称、分类、地点、时间等条件进行搜索和筛选。 (5)查看物品详情,并提供联系发布者的方式(站内联系即可,无需真实短信或邮件)。 (6)用户可管理自己发布的信息,包括编辑、删除、修改状态(已找到/已认领)。 (7)管理员具备用户管理、信息审核(可选)及违规内容处理能力。 基本业务流程:注册登录后即可发布失物或招领信息;其他用户可浏览、搜索、查看详情,并通过站内方式联系发布者。当物品成功找回后,发布者可以将信息标记为已完成,已完成的信息仍可保留展示但默认降低排序。 4. 设计风格 整体采用现代、简洁、清爽的设计风格,界面注重易用性与响应式布局,能够适配桌面端与移动端浏览器,保证良好的用户体验。在码道对话界面选择 规范驱动模式(Spec-Driven Mode),发送上述请求。(2)码道首先调用 creating-sdd-directory skill 创建项目目录结构。(3)之后码道根据需求描述,生成需求规格文档 spec.md。如有需要可对需求规格文档进行修改。(4)确认后,码道继续生成技术设计文档 design.md,完成生成后可按需修改。(5)确认后,码道继续生成编码任务文档 task.md,完成生成后可按需修改。(6)检查需求规格文档、技术设计文档、编码任务文档是否与预期一致,如有需要调整的地方可直接进行修改。3.3 编码任务执行确认 SSD 无误后,在码道界面发送请求:遵循spec.md、design.md、tasks.md进行项目开发。码道将根据 SSD 执行编码任务。(1)初始化后端项目结构(2)创建数据模型(3)创建工具函数与权限装饰器(4)创建后端核心接口(5)验证后端构建(6)创建初始化前端项目结构、配置依赖(7)创建路由配置和 API 封装(8)更新 main.js(9)创建全局样式和组件(10)实现前端页面视图(11)验证前端构建(12)集成验证(13)开发完成3.4 环境配置与快速启动开发完成后,可查看如下项目结构:/workspace/ ├── anima-backend/ # 后端 Flask 项目 │ ├── app/ │ │ ├── __init__.py # 应用工厂 + 种子数据 │ │ ├── config.py # 配置文件 │ │ ├── extensions.py # SQLAlchemy/JWT/Migrate/CORS │ │ ├── models/ # 数据模型 │ │ │ ├── user.py # User 模型(密码哈希、to_dict) │ │ │ ├── item.py # Item 模型(图片JSON、状态映射) │ │ │ └── message.py # Message 模型 │ │ ├── api/ # API 蓝图 │ │ │ ├── auth.py # 注册/登录/个人信息/改密 │ │ │ ├── items.py # 信息CRUD/搜索/状态变更 │ │ │ ├── messages.py # 站内消息/已读/未读数 │ │ │ └── admin.py # 用户管理/信息下架 │ │ ├── utils/ # 工具函数 │ │ │ ├── __init__.py # 响应辅助/权限装饰器 │ │ │ └── validators.py # 校验函数 │ │ └── static/uploads/ # 图片上传目录 │ ├── run.py # 启动入口 │ └── requirements.txt │ ├── anima-frontend/ # 前端 Vue3 项目 │ ├── src/ │ │ ├── api/ # API 请求封装 │ │ ├── components/ # 通用组件 │ │ │ ├── AppHeader.vue # 导航栏(响应式+消息未读数) │ │ │ ├── AppFooter.vue │ │ │ ├── ItemCard.vue # 信息卡片 │ │ │ ├── SearchBar.vue # 搜索筛选栏 │ │ │ ├── ImageUpload.vue # 图片上传 │ │ │ └── Pagination.vue # 分页 │ │ ├── views/ # 页面视图(7个) │ │ │ ├── HomeView.vue # 首页 │ │ │ ├── LoginView.vue # 登录 │ │ │ ├── RegisterView.vue# 注册 │ │ │ ├── ItemDetailView.vue # 详情+站内联系 │ │ │ ├── ItemPostView.vue # 发布 │ │ │ ├── ItemEditView.vue # 编辑 │ │ │ ├── MyItemsView.vue # 我的发布 │ │ │ ├── ProfileView.vue # 个人信息 │ │ │ ├── MessagesView.vue # 消息中心 │ │ │ └── admin/ # 管理员页面 │ │ ├── router/index.js # 路由+守卫 │ │ ├── stores/user.js # Pinia 用户状态 │ │ └── assets/main.css # 全局样式 │ └── vite.config.js # Vite 代理配置 │ └── .codeartsdoer/specs/anima/ # SDD 文档 ├── spec.md ├── design.md └── tasks.md安装依赖、启动项目后端:cd anima-backend pip install -r requirements.txt python run.py安装依赖、启动项目前端:cd anima-frontend npm install npm run dev成功启动后,浏览器访问 http://localhost:3000/ 进入应用页面。3.5 项目功能测试进入应用首页,可以看到导航栏、信息筛选卡片以及失物招领信息列表。尝试注册账号并登录,可查看账号个人信息并修改昵称和密码。导航栏点击“发布信息”,填写失物或招领的相关内容,点击“发布”后可以在首页看到已发布的信息。首页点击失物招领信息卡片,进入详情页、查看该条信息的具体内容。对于自己发布的信息,可以点击“编辑”来编辑该信息的内容,或者点击“标记为已找到(标记为已认领)”来修改完成状态。对于他人发布的信息,可在详情页联系发布者。可在导航栏点击“消息”,查看自己收到和发出的消息。对于他人发布的信息,可在详情页联系发布者。可在导航栏点击“消息”,查看自己收到和发出的消息。导航栏点击“我的发布”,查看自己发布的失物招领信息。登录管理员账号,验证用户管理和信息管理功能。3.5 后续开发与优化(1)与码道进行对话,反馈测试应用时发现的问题,例如编辑失物招领信息时添加图片功能出现异常。码道会进行修改、解决这些问题。(2)与码道进行对话,对当前应用进行优化,例如完善控件显示、为关键操作添加二次确认、对页面进行美化等。美化后的应用,首页界面如下。(3)完成开发后,向码道以下指令,更新 SSD。请总结项目,更新SDD,确保下次可以直接通过该SDD复现项目的风格、布局、颜色、功能特性以及前后台业务逻辑等。同步更新 SSD,可以完整记录系统设计与实现过程中的关键决策,提高开发过程的可追溯性,并促进团队协作与知识共享。此外,通过保持文档与代码的一致性,构建了系统化的项目文档体系,为后续维护和功能扩展提供了有力支撑。3.6 核心代码讲解(1)后端应用工厂与蓝图注册anima-backend/app/__init__.py 中的 create_app 是后端唯一启动入口,完成配置加载、扩展初始化、蓝图挂载和数据库初始化,组装成一个可运行的 Flask 应用。# anima-backend/app/__init__.py def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) # 挂载数据库、JWT、迁移、跨域等扩展 db.init_app(app) jwt.init_app(app) migrate.init_app(app, db) cors.init_app(app, resources={r"/api/*": {"origins": "*"}}) os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # 提供上传图片的静态访问地址 @app.route('/static/uploads/<path:filename>') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) # 按业务注册四类 API app.register_blueprint(auth_bp, url_prefix='/api/auth') app.register_blueprint(items_bp, url_prefix='/api/items') app.register_blueprint(messages_bp, url_prefix='/api/messages') app.register_blueprint(admin_bp, url_prefix='/api/admin') # 首次启动时建表,并写入默认管理员 with app.app_context(): from app import models # noqa: F401 db.create_all() _seed_admin() return app初始化 db、jwt、migrate、cors,分别负责 ORM、登录令牌、数据库迁移与前端跨域调用支持,并开放图片上传目录 /static/uploads/ 供浏览器访问图片。后端按业务划分 /api/auth、/api/items、/api/messages、/api/admin 四个蓝图,分别负责认证、失物招领、站内消息、管理端;后端启动时自动建表,并通过 _seed_admin 写入默认管理员账号(admin / admin123),便于首次体验管理功能。(2)核心数据模型数据模型定义了 User、Item、Message 三类核心实体,分别对应用户、失物/招领信息与站内消息。User 模型:保存账号身份、负责密码安全# anima-backend/app/models/user.py class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(20), unique=True, nullable=False, index=True) email = db.Column(db.String(120), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(256), nullable=False) nickname = db.Column(db.String(30)) role = db.Column(db.String(20), nullable=False, default='user') status = db.Column(db.String(20), nullable=False, default='active') @password.setter def password(self, password): self.password_hash = bcrypt.hashpw( password.encode('utf-8'), bcrypt.gensalt() ).decode('utf-8') def verify_password(self, password): return bcrypt.checkpw( password.encode('utf-8'), self.password_hash.encode('utf-8') ) def to_dict(self): return { 'id': self.id, 'username': self.username, 'email': self.email, 'nickname': self.display_name, 'role': '管理员' if self.role == 'admin' else '普通用户', 'status': self.status, # ... } User 对应注册用户。除了用户名、邮箱、昵称外,用 role 区分普通用户与管理员,用 status 标记账号是否可用。密码不存明文,而是通过 @password.setter 用 bcrypt 生成哈希写入、登录时校验,保证账号安全。to_dict 把数据库记录转成前端可用的 JSON(角色显示为中文);另有 to_public_dict 只返回昵称等公开字段,用于物品详情页展示发布者,避免泄露邮箱等敏感信息。Item 模型:保存失物招领信息# anima-backend/app/models/item.py class Item(db.Model): __tablename__ = 'items' title = db.Column(db.String(50), nullable=False) type = db.Column(db.String(10), nullable=False, index=True) # lost / found description = db.Column(db.Text, nullable=False) category = db.Column(db.String(20), nullable=False, index=True) location = db.Column(db.String(100), nullable=False) event_time = db.Column(db.DateTime, nullable=False) _images = db.Column('images', db.Text) status = db.Column(db.String(20), nullable=False, default='active', index=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) STATUS_DISPLAY = { 'active': '进行中', 'found': '已找到', 'claimed': '已认领', 'removed': '已下架' } @property def images(self): return json.loads(self._images) if self._images else [] @images.setter def images(self, value): self._images = json.dumps(value) if value else None @property def is_completed(self): return self.status in ('found', 'claimed') Item 是平台的核心业务对象,一条记录代表用户发布的一则失物或招领,包含标题、描述、分类、地点、事件时间,以及多张图片和当前状态字段。user_id 指向发布者,从而把信息与用户关联起来。Message 模型:保存围绕某条信息的站内联系记录# anima-backend/app/models/message.py class Message(db.Model): __tablename__ = 'messages' content = db.Column(db.String(500), nullable=False) sender_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) receiver_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) item_id = db.Column(db.Integer, db.ForeignKey('items.id'), nullable=False) is_read = db.Column(db.Boolean, nullable=False, default=False) Message 用于实现“在详情页联系发布者”。每条消息都绑定发送方、接收方和关联的 Item,并记录是否已读,从而支撑消息中心列表与导航栏未读提示。与普通私信不同,这里强制要求 item_id,保证联系必须围绕某条失物/招领展开,消息中心也能据此展示“关于哪条信息的沟通”;is_read 则用于统计未读数,并在用户查看后标记为已读。(3)统一响应与权限装饰器app/utils 提供两套公共能力:统一 JSON 响应格式,以及对接口做登录/管理员校验。所有业务接口都复用它们,避免每个接口各自拼返回体、各自写鉴权代码。# anima-backend/app/utils/__init__.py def success_response(data=None, message='success'): return jsonify({'code': 0, 'message': message, 'data': data or {}}) def error_response(code, message): return jsonify({'code': code, 'message': message, 'data': {}}), \ 400 if code < 500 else 500 def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): verify_jwt_in_request() identity = get_jwt_identity() user = User.query.get(identity) if not user: return error_response(401, '用户不存在') if user.status == 'disabled': return error_response(403, '账号已被禁用') return f(*args, **kwargs, current_user=user) return decorated_function def admin_required(f): # 在登录校验基础上,额外要求 role == 'admin' ... 成功与失败都返回 {code, message, data}:前端只需判断 code === 0 即可处理结果,错误提示取自 message。login_required 装饰在需要登录的接口上:校验请求头中的 JWT,查出对应用户,若账号被禁用则拒绝访问,通过后把 current_user 传给视图函数。admin_required 在此基础上再检查 role == 'admin',专门保护管理端接口。这样鉴权逻辑集中维护,业务代码只需关心业务本身。(4)用户认证接口auth 蓝图负责用户进入系统的全过程,包括创建账号、验证身份、签发登录凭证,以及登录后的个人资料与改密。# anima-backend/app/api/auth.py @auth_bp.route('/register', methods=['POST']) def register(): data = request.get_json() username = data.get('username', '').strip() email = data.get('email', '').strip() password = data.get('password', '') # 校验格式与唯一性后创建用户 user = User(username=username, email=email) user.password = password db.session.add(user) db.session.commit() token = create_access_token(identity=user.id) return success_response({ 'token': token, 'user': user.to_dict() }, '注册成功') @auth_bp.route('/login', methods=['POST']) def login(): username = data.get('username', '').strip() password = data.get('password', '') # 支持用用户名或邮箱登录 user = User.query.filter( (User.username == username) | (User.email == username) ).first() if not user or not user.verify_password(password): return error_response(401, '用户名或密码错误') if user.status == 'disabled': return error_response(403, '账号已被禁用') token = create_access_token(identity=user.id) return success_response({'token': token, 'user': user.to_dict()}, '登录成功') 注册流程为:校验用户名/邮箱/密码格式 → 检查是否已存在 → 创建 User(密码自动哈希)→ 签发 JWT 并连同用户信息返回,注册成功即处于登录态。登录流程为:按用户名或邮箱查找用户 → 校验密码与账号状态 → 同样返回 token 与 user。前端保存该 Token 后,后续请求即可证明身份。同文件还提供需登录才能访问的个人资料读写与修改密码接口。(5)失物招领接口items 蓝图实现平台主业务,包括用户浏览/搜索信息、登录后发布带图信息、对自己发布的信息做编辑删除,以及在找回物品后标记完成。首页列表、详情页、发布页、我的发布都依赖这里的接口。列表查询与排序:口面向所有访客,负责把符合条件的信息分页返回给首页# anima-backend/app/api/items.py @items_bp.route('', methods=['GET']) def get_items(): query = Item.query.filter(Item.status != 'removed') if item_type and item_type in Item.VALID_TYPES: query = query.filter(Item.type == item_type) if keyword: query = query.filter( db.or_(Item.title.contains(keyword), Item.description.contains(keyword)) ) if category and category in Item.VALID_CATEGORIES: query = query.filter(Item.category == category) if location: query = query.filter(Item.location.contains(location)) # 还可按 start_date / end_date 过滤 event_time # 进行中优先,再按创建时间倒序 query = query.order_by( db.case((Item.status == 'active', 0), else_=1), Item.created_at.desc() ) pagination = query.paginate(page=page, per_page=per_page, error_out=False) return success_response({ 'items': [item.to_dict() for item in pagination.items], 'total': pagination.total, 'page': pagination.page, 'pages': pagination.pages }) 查询时,先排除已下架信息,再按类型、关键词、分类、地点、时间等条件叠加过滤;排序上使 active(进行中)排在前面,同优先级再按发布时间倒序,这样首页优先看到仍待匹配的信息。最后分页返回,供前端翻页展示。发布信息与图片上传:要求登录,负责把表单字段和图片落库为一条新 Item@items_bp.route('', methods=['POST']) @login_required def create_item(current_user): # 校验标题、类型、描述、分类、地点、时间等必填项 images = [] files = request.files.getlist('images') for f in files: if f.filename: # 校验格式与大小后,用 UUID 重命名并保存 filename = f'{uuid.uuid4().hex}.{ext}' f.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename)) images.append(f'/static/uploads/{filename}') item = Item( title=title, type=item_type, description=description, category=category, location=location, event_time=event_time, user_id=current_user.id ) item.images = images db.session.add(item) db.session.commit() return success_response(item.to_dict(), '发布成功') 接口使用 multipart/form-data 同时接收文字与文件:先校验业务字段,再逐张检查图片扩展名与大小,保存到上传目录后把访问路径写入 item.images,最后关联 current_user.id 入库。编辑接口逻辑类似,但会保留前端回传的 existing_images,再追加新上传图片,避免改信息时把原图清空。状态变更:用于发布者在物品找回后结束该条信息@items_bp.route('/<int:item_id>/status', methods=['PUT']) @login_required def update_item_status(current_user, item_id): item = Item.query.get(item_id) if item.user_id != current_user.id: return error_response(403, '无权操作') if item.is_completed: return error_response(400, '已完成的信息不可恢复为进行中') if item.type == 'lost' and new_status == 'found': item.status = 'found' elif item.type == 'found' and new_status == 'claimed': item.status = 'claimed' else: return error_response(400, '无效的状态变更') db.session.commit() return success_response(item.to_dict(), '状态更新成功') 只有发布者本人可操作;失物只能标为“已找到”,招领只能标为“已认领”;一旦完成不可再改回进行中。这样既闭环了“发布 → 找回 → 结束”流程,也避免状态被随意篡改。(6)站内消息接口messages 蓝图实现站内联系,即浏览者在详情页给发布者留言,双方在消息中心查看往来,导航栏展示未读数量。它替代真实短信/邮件,把沟通留在平台内完成。# anima-backend/app/api/messages.py @messages_bp.route('', methods=['POST']) @login_required def send_message(current_user): if receiver_id == current_user.id: return error_response(400, '不能联系自己') item = Item.query.get(item_id) if not item or item.status == 'removed': return error_response(404, '关联信息不存在') if item.user_id != receiver_id: return error_response(400, '接收者不是该信息的发布者') message = Message( content=content, sender_id=current_user.id, receiver_id=receiver_id, item_id=item_id ) db.session.add(message) db.session.commit() return success_response(message.to_dict(), '消息发送成功') @messages_bp.route('/unread-count', methods=['GET']) @login_required def get_unread_count(current_user): count = Message.query.filter( Message.receiver_id == current_user.id, Message.is_read == False ).count() return success_response({'count': count}) 发送时校验:不能给自己发、关联信息必须存在且未下架、接收者必须是该信息的发布者。通过后写入 Message,消息中心即可按收发关系列表展示。unread-count 统计当前用户未读收件数,供导航栏提示;同模块还有标记已读等接口,配合前端完成“联系 → 查看 → 已读”的完整链路。(7)管理端接口admin 蓝图只对管理员开放,用于处理违规账号与违规信息:查看用户列表并启用/禁用账号,查看全部信息并上架/下架。# anima-backend/app/api/admin.py @admin_bp.route('/users/<int:user_id>/status', methods=['PUT']) @admin_required def update_user_status(current_user, user_id): if user_id == current_user.id: return error_response(400, '不能禁用自己的账号') # status 仅允许 active / disabled user.status = new_status db.session.commit() return success_response(user.to_dict(), '操作成功') @admin_bp.route('/items/<int:item_id>/status', methods=['PUT']) @admin_required def update_item_status(current_user, item_id): # 管理员可将信息设为 active 或 removed(下架) item.status = new_status db.session.commit() return success_response(item.to_dict(), '操作成功') 两个接口都使用 @admin_required,非管理员无法调用。禁用用户时禁止操作自己,避免管理员误锁自身;信息状态只允许在“进行中 / 已下架”之间切换,下架后前台列表不再展示。同文件还提供用户与信息的分页查询,支撑管理页表格展示。(8)前端请求封装与用户状态前端要调用上述 API,需要在每次请求自动带上登录 Token,并在页面间共享用户身份。request.js 与 Pinia 的 user store 分别承担这两项功能;Vite 代理则让开发环境下前端能无跨域地访问后端。请求层用 Axios 拦截器统一处理鉴权头与登录失效:// anima-frontend/src/api/request.js const api = axios.create({ baseURL: '/api', timeout: 10000 }) api.interceptors.request.use(config => { const userStore = useUserStore() if (userStore.token) { config.headers.Authorization = `Bearer ${userStore.token}` } return config }) api.interceptors.response.use( response => response, error => { if (error.response && error.response.status === 401) { useUserStore().logout() router.push({ name: 'Login', query: { redirect: router.currentRoute.value.fullPath } }) } return Promise.reject(error) } ) 发出请求前,若本地已有 Token,则写入 Authorization: Bearer ...,后端 login_required 才能识别用户;若返回 401,说明登录已失效,则清空本地状态并跳转登录页,同时带上当前路径以便登录后返回。用户状态由 Pinia store 集中管理,并同步到 localStorage,刷新页面后仍保持登录:// anima-frontend/src/stores/user.js export const useUserStore = defineStore('user', () => { const token = ref(localStorage.getItem('token') || '') const user = ref(JSON.parse(localStorage.getItem('user') || 'null')) const isLoggedIn = computed(() => !!token.value) const isAdmin = computed(() => user.value?.role === '管理员') async function login(credentials) { const res = await authApi.login(credentials) if (res.data.code === 0) { token.value = res.data.data.token user.value = res.data.data.user localStorage.setItem('token', res.data.data.token) localStorage.setItem('user', JSON.stringify(res.data.data.user)) } return res } // register / logout / fetchProfile 同理 }) login / register 成功后同时更新内存与本地存储;isLoggedIn、isAdmin 供路由守卫和导航栏判断是否展示管理入口。logout 则清除两端数据。开发时,Vite 把 /api、/static 代理到后端 5000 端口,浏览器只访问 3000,即可完成前后端联调:// anima-frontend/vite.config.js server: { port: 3000, proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true }, '/static': { target: 'http://localhost:5000', changeOrigin: true } } } (9)前端路由守卫与首页路由模块决定用户能打开哪些页面,保护需登录/管理员的功能;首页为失物招领信息的主入口。前者保护需登录/管理员的功能页,把后端列表接口的数据呈现为可搜索、可翻页的卡片列表。路由守卫在每次跳转前检查权限:// anima-frontend/src/router/index.js router.beforeEach((to, from, next) => { const userStore = useUserStore() if (to.meta.requiresAuth && !userStore.isLoggedIn) { next({ name: 'Login', query: { redirect: to.fullPath } }) } else if (to.meta.requiresAdmin && !userStore.isAdmin) { next({ name: 'Home' }) } else { next() } }) 发布、编辑、我的发布、个人中心、消息等路由配置了 meta.requiresAuth,未登录会跳到登录页并记录原目标地址;管理端路由额外配置 requiresAdmin,非管理员会被拦回首页,避免前端直接进入无权页面。首页脚本负责拉取并刷新信息列表:// anima-frontend/src/views/HomeView.vue(脚本核心) async function loadItems() { const params = { page: page.value, per_page: 20, ...filters.value } const res = await itemsApi.getItems(params) if (res.data.code === 0) { items.value = res.data.data.items totalPages.value = res.data.data.pages } } function handleSearch(searchFilters) { filters.value = searchFilters page.value = 1 loadItems() } 页面挂载时调用 loadItems 请求 /api/items;用户在 SearchBar 中筛选时,handleSearch 更新条件并回到第 1 页再请求;分页组件切换页码时同样调用 loadItems。返回的 items 交给 ItemCard 渲染。至此,后端列表能力与前端浏览体验对接完成,构成平台最核心的使用路径:搜索筛选 → 浏览卡片 → 进入详情 → 联系或管理。3.7 开发总结本案例基于华为开发者空间云开发环境和华为云码道(CodeArts)代码智能体,完成了一个智能化失物招领平台的设计与开发。整个开发过程采用规范驱动开发(Spec-Driven Development,SDD)模式,通过需求规格文档、技术设计文档和编码任务文档逐步明确系统目标、架构设计和实现任务,实现了从需求分析到代码生成的流程化开发。在开发过程中,利用云开发环境提供统一稳定的运行环境,避免了本地环境配置和依赖管理带来的问题;通过 CodeArts Agent 对需求进行理解和拆解,自动生成项目 SDD,并依据 SDD 完成前后端代码构建。开发过程中结合实际运行测试结果,通过持续反馈与智能体协作,对系统功能、界面交互和代码结构进行优化,最终实现了用户管理、失物招领信息管理、搜索筛选、图片上传、站内消息等完整业务功能。通过本案例可以看到,AI Agent 驱动的软件开发模式能够有效降低应用开发门槛,提高原型构建和功能迭代效率。同时,基于 SDD 的开发方式使需求、设计与代码之间保持关联,增强了开发过程的可追溯性,为后续维护、扩展以及团队协作提供了良好的基础。四、释放资源4.1 关闭华为开发者空间云开发环境(容器)案例完成后,如无需继续使用该开发环境,可关闭对应云开发环境,避免持续占用开发资源。VS Code 中左侧导航栏选择”开发者空间,选中容器,点击“关机”按钮,弹框点击“确认”即可关闭容器。五、扩展资料说明华为云码道CodeArts实战速成:cid:link_1华为云开发者AI训练营:cid:link_2
-
一、概述1.1 案例介绍随着校园、社区及公共场所人员流动增加,物品遗失与寻找需求日益普遍。传统失物招领方式通常依赖线下公告、微信群等渠道,存在信息分散、检索困难、匹配效率低等问题。为提升失物信息流转效率,本案例设计并开发了一个智能化失物招领平台,实现失物信息与招领信息的集中管理和便捷查询。本案例基于 AI Agent 自动化开发模式,利用华为开发者空间开发平台和华为云码道(CodeArts)代码智能体完成全流程应用构建。应用采用前后端分离架构,选择采用 Vue3 前端、Flask 后端、SQLite 数据库的技术栈进行开发。系统支持用户注册登录、失物信息发布、招领信息发布、物品图片上传、关键词搜索、分类筛选、详情查看、站内联系以及个人信息管理等核心功能,同时提供信息状态管理能力,帮助用户完成从信息发布到物品找回的完整业务流程。本案例构建的项目和文档已保存至GitCode,可自行下载源码。Demo演示视频技术选型:华为开发者空间开发平台 - 云开发环境(容器):华为云提供的一站式云上开发环境,支持多种主流开发语言和运行环境,开发者无需进行复杂的本地环境配置即可快速开展应用开发。本案例中选用云开发环境(容器)作为应用的开发与运行环境,为项目提供统一稳定的基础环境,避免因操作系统、依赖版本等环境差异导致的应用运行问题,提高项目构建和部署效率。华为云码道(CodeArts)代码智能体:华为云提供的智能化软件开发工具,融合代码大模型、AI IDE 和 Code Agent 等能力,能够理解项目需求并辅助完成代码生成、代码优化和工程构建。本案例中使用华为云码道(CodeArts)代码智能体作为核心开发辅助工具,根据失物招领应用需求完成前后端功能开发,加速 Web 应用构建过程,降低应用开发门槛。1.2 适用对象企业开发者个人开发者高校学生1.3 案例时间本案例总时长预计60分钟。1.4 案例流程说明:VSCode安装开发者空间插件,远程连接云开发环境(容器);云开发环境(容器)搭建华为云码道(CodeArts)代码智能体;对话码道,开发SDD并检验是否符合项目预期;对话码道,编写代码生成项目;启动项目,验证项目功能;对话码道,更新优化SDD,沉淀项目知识库。1.5 资源总览本案例预计花费0元。资源名称规格单价(元)开发者空间云开发环境ARM | 2vCPUs 4GB | All In One 开发环境免费华为云码道(CodeArts)代码智能体体验版免费二、环境和资源准备2.1 VSCode 远程连接云开发环境参考案例《华为开发者空间 - 云开发环境(容器)IDE插件远程连接操作指导》中的“二、 云开发环境IDE插件远程连接操作指导”章节,完成 VS Code 到云开发环境(容器)的远程连接。2.2 VSCode 搭建码道智能体VS Code 连接云开发环境的状态下,在“扩展”中搜索“CodeArts”,安装“CodeArts Agent”插件。安装完成后,VS Code中执行命令:CodeArts Agent: Open the Q&A Dialog右侧弹出的侧栏为码道智能体对话界面。登录后进行对话测试,确认码道智能体搭建成功且能够正常使用。三、使用码道构建失物招领平台系统3.1 需求分析与功能设计项目名称: Anima - 失物招领平台项目定位:现代化失物招领平台,支持用户发布失物和招领信息、浏览与搜索信息、联系发布者,并具备完整的用户管理能力。技术栈:前端:Vue3 + Vite + TypeScript后端:Python Flask数据库:SQLite核心功能:用户登录和信息管理用户发布失物信息和招领信息,可对自己发布的信息进行编辑、删除、修改完成状态展示最新发布的信息,可分类浏览,可按条件检索查看物品详情并联系发布者管理员管理用户和信息3.2 Prompt 设计和 SDD 生成(1)根据以上项目需求,设计如下 Prompt:根据以下需求描述,开发一个失物招领 Web 应用。 1. 项目概述 项目名称:Anima 项目定位:现代化的失物招领平台,支持用户发布失物和招领信息、浏览与搜索信息、联系发布者,并具备完整的用户管理能力。 2. 技术框架 技术栈:实现前后端分离,前端采用Vue3,后端采用Python Flask,数据库使用SQLite。具备良好的可维护性、可扩展性和规范的项目结构,API 设计遵循 RESTful 风格。 3. 核心功能 (1)用户注册、登录、退出及个人信息管理。 (2)发布失物信息和招领信息,并支持上传多张图片。 (3)首页展示最新发布的信息,可按失物/招领分类浏览。 (4)支持按物品名称、分类、地点、时间等条件进行搜索和筛选。 (5)查看物品详情,并提供联系发布者的方式(站内联系即可,无需真实短信或邮件)。 (6)用户可管理自己发布的信息,包括编辑、删除、修改状态(已找到/已认领)。 (7)管理员具备用户管理、信息审核(可选)及违规内容处理能力。 基本业务流程:注册登录后即可发布失物或招领信息;其他用户可浏览、搜索、查看详情,并通过站内方式联系发布者。当物品成功找回后,发布者可以将信息标记为已完成,已完成的信息仍可保留展示但默认降低排序。 4. 设计风格 整体采用现代、简洁、清爽的设计风格,界面注重易用性与响应式布局,能够适配桌面端与移动端浏览器,保证良好的用户体验。在码道对话界面选择 规范驱动模式(Spec-Driven Mode),发送上述请求。(2)码道首先调用 creating-sdd-directory skill 创建项目目录结构。(3)之后码道根据需求描述,生成需求规格文档 spec.md。如有需要可对需求规格文档进行修改。(4)确认后,码道继续生成技术设计文档 design.md,完成生成后可按需修改。(5)确认后,码道继续生成编码任务文档 task.md,完成生成后可按需修改。(6)检查需求规格文档、技术设计文档、编码任务文档是否与预期一致,如有需要调整的地方可直接进行修改。3.3 编码任务执行确认 SSD 无误后,在码道界面发送请求:遵循spec.md、design.md、tasks.md进行项目开发。码道将根据 SSD 执行编码任务。(1)初始化后端项目结构(2)创建数据模型(3)创建工具函数与权限装饰器(4)创建后端核心接口(5)验证后端构建(6)创建初始化前端项目结构、配置依赖(7)创建路由配置和 API 封装(8)更新 main.js(9)创建全局样式和组件(10)实现前端页面视图(11)验证前端构建(12)集成验证(13)开发完成3.4 环境配置与快速启动开发完成后,可查看如下项目结构:/workspace/ ├── anima-backend/ # 后端 Flask 项目 │ ├── app/ │ │ ├── __init__.py # 应用工厂 + 种子数据 │ │ ├── config.py # 配置文件 │ │ ├── extensions.py # SQLAlchemy/JWT/Migrate/CORS │ │ ├── models/ # 数据模型 │ │ │ ├── user.py # User 模型(密码哈希、to_dict) │ │ │ ├── item.py # Item 模型(图片JSON、状态映射) │ │ │ └── message.py # Message 模型 │ │ ├── api/ # API 蓝图 │ │ │ ├── auth.py # 注册/登录/个人信息/改密 │ │ │ ├── items.py # 信息CRUD/搜索/状态变更 │ │ │ ├── messages.py # 站内消息/已读/未读数 │ │ │ └── admin.py # 用户管理/信息下架 │ │ ├── utils/ # 工具函数 │ │ │ ├── __init__.py # 响应辅助/权限装饰器 │ │ │ └── validators.py # 校验函数 │ │ └── static/uploads/ # 图片上传目录 │ ├── run.py # 启动入口 │ └── requirements.txt │ ├── anima-frontend/ # 前端 Vue3 项目 │ ├── src/ │ │ ├── api/ # API 请求封装 │ │ ├── components/ # 通用组件 │ │ │ ├── AppHeader.vue # 导航栏(响应式+消息未读数) │ │ │ ├── AppFooter.vue │ │ │ ├── ItemCard.vue # 信息卡片 │ │ │ ├── SearchBar.vue # 搜索筛选栏 │ │ │ ├── ImageUpload.vue # 图片上传 │ │ │ └── Pagination.vue # 分页 │ │ ├── views/ # 页面视图(7个) │ │ │ ├── HomeView.vue # 首页 │ │ │ ├── LoginView.vue # 登录 │ │ │ ├── RegisterView.vue# 注册 │ │ │ ├── ItemDetailView.vue # 详情+站内联系 │ │ │ ├── ItemPostView.vue # 发布 │ │ │ ├── ItemEditView.vue # 编辑 │ │ │ ├── MyItemsView.vue # 我的发布 │ │ │ ├── ProfileView.vue # 个人信息 │ │ │ ├── MessagesView.vue # 消息中心 │ │ │ └── admin/ # 管理员页面 │ │ ├── router/index.js # 路由+守卫 │ │ ├── stores/user.js # Pinia 用户状态 │ │ └── assets/main.css # 全局样式 │ └── vite.config.js # Vite 代理配置 │ └── .codeartsdoer/specs/anima/ # SDD 文档 ├── spec.md ├── design.md └── tasks.md安装依赖、启动项目后端:cd anima-backend pip install -r requirements.txt python run.py安装依赖、启动项目前端:cd anima-frontend npm install npm run dev成功启动后,浏览器访问 http://localhost:3000/ 进入应用页面。3.5 项目功能测试进入应用首页,可以看到导航栏、信息筛选卡片以及失物招领信息列表。尝试注册账号并登录,可查看账号个人信息并修改昵称和密码。导航栏点击“发布信息”,填写失物或招领的相关内容,点击“发布”后可以在首页看到已发布的信息。首页点击失物招领信息卡片,进入详情页、查看该条信息的具体内容。对于自己发布的信息,可以点击“编辑”来编辑该信息的内容,或者点击“标记为已找到(标记为已认领)”来修改完成状态。对于他人发布的信息,可在详情页联系发布者。可在导航栏点击“消息”,查看自己收到和发出的消息。对于他人发布的信息,可在详情页联系发布者。可在导航栏点击“消息”,查看自己收到和发出的消息。导航栏点击“我的发布”,查看自己发布的失物招领信息。登录管理员账号,验证用户管理和信息管理功能。3.5 后续开发与优化(1)与码道进行对话,反馈测试应用时发现的问题,例如编辑失物招领信息时添加图片功能出现异常。码道会进行修改、解决这些问题。(2)与码道进行对话,对当前应用进行优化,例如完善控件显示、为关键操作添加二次确认、对页面进行美化等。美化后的应用,首页界面如下。(3)完成开发后,向码道以下指令,更新 SSD。请总结项目,更新SDD,确保下次可以直接通过该SDD复现项目的风格、布局、颜色、功能特性以及前后台业务逻辑等。同步更新 SSD,可以完整记录系统设计与实现过程中的关键决策,提高开发过程的可追溯性,并促进团队协作与知识共享。此外,通过保持文档与代码的一致性,构建了系统化的项目文档体系,为后续维护和功能扩展提供了有力支撑。3.6 核心代码讲解(1)后端应用工厂与蓝图注册anima-backend/app/__init__.py 中的 create_app 是后端唯一启动入口,完成配置加载、扩展初始化、蓝图挂载和数据库初始化,组装成一个可运行的 Flask 应用。# anima-backend/app/__init__.py def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) # 挂载数据库、JWT、迁移、跨域等扩展 db.init_app(app) jwt.init_app(app) migrate.init_app(app, db) cors.init_app(app, resources={r"/api/*": {"origins": "*"}}) os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) # 提供上传图片的静态访问地址 @app.route('/static/uploads/<path:filename>') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) # 按业务注册四类 API app.register_blueprint(auth_bp, url_prefix='/api/auth') app.register_blueprint(items_bp, url_prefix='/api/items') app.register_blueprint(messages_bp, url_prefix='/api/messages') app.register_blueprint(admin_bp, url_prefix='/api/admin') # 首次启动时建表,并写入默认管理员 with app.app_context(): from app import models # noqa: F401 db.create_all() _seed_admin() return app初始化 db、jwt、migrate、cors,分别负责 ORM、登录令牌、数据库迁移与前端跨域调用支持,并开放图片上传目录 /static/uploads/ 供浏览器访问图片。后端按业务划分 /api/auth、/api/items、/api/messages、/api/admin 四个蓝图,分别负责认证、失物招领、站内消息、管理端;后端启动时自动建表,并通过 _seed_admin 写入默认管理员账号(admin / admin123),便于首次体验管理功能。(2)核心数据模型数据模型定义了 User、Item、Message 三类核心实体,分别对应用户、失物/招领信息与站内消息。User 模型:保存账号身份、负责密码安全# anima-backend/app/models/user.py class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(20), unique=True, nullable=False, index=True) email = db.Column(db.String(120), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(256), nullable=False) nickname = db.Column(db.String(30)) role = db.Column(db.String(20), nullable=False, default='user') status = db.Column(db.String(20), nullable=False, default='active') @password.setter def password(self, password): self.password_hash = bcrypt.hashpw( password.encode('utf-8'), bcrypt.gensalt() ).decode('utf-8') def verify_password(self, password): return bcrypt.checkpw( password.encode('utf-8'), self.password_hash.encode('utf-8') ) def to_dict(self): return { 'id': self.id, 'username': self.username, 'email': self.email, 'nickname': self.display_name, 'role': '管理员' if self.role == 'admin' else '普通用户', 'status': self.status, # ... } User 对应注册用户。除了用户名、邮箱、昵称外,用 role 区分普通用户与管理员,用 status 标记账号是否可用。密码不存明文,而是通过 @password.setter 用 bcrypt 生成哈希写入、登录时校验,保证账号安全。to_dict 把数据库记录转成前端可用的 JSON(角色显示为中文);另有 to_public_dict 只返回昵称等公开字段,用于物品详情页展示发布者,避免泄露邮箱等敏感信息。Item 模型:保存失物招领信息# anima-backend/app/models/item.py class Item(db.Model): __tablename__ = 'items' title = db.Column(db.String(50), nullable=False) type = db.Column(db.String(10), nullable=False, index=True) # lost / found description = db.Column(db.Text, nullable=False) category = db.Column(db.String(20), nullable=False, index=True) location = db.Column(db.String(100), nullable=False) event_time = db.Column(db.DateTime, nullable=False) _images = db.Column('images', db.Text) status = db.Column(db.String(20), nullable=False, default='active', index=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) STATUS_DISPLAY = { 'active': '进行中', 'found': '已找到', 'claimed': '已认领', 'removed': '已下架' } @property def images(self): return json.loads(self._images) if self._images else [] @images.setter def images(self, value): self._images = json.dumps(value) if value else None @property def is_completed(self): return self.status in ('found', 'claimed') Item 是平台的核心业务对象,一条记录代表用户发布的一则失物或招领,包含标题、描述、分类、地点、事件时间,以及多张图片和当前状态字段。user_id 指向发布者,从而把信息与用户关联起来。Message 模型:保存围绕某条信息的站内联系记录# anima-backend/app/models/message.py class Message(db.Model): __tablename__ = 'messages' content = db.Column(db.String(500), nullable=False) sender_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) receiver_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) item_id = db.Column(db.Integer, db.ForeignKey('items.id'), nullable=False) is_read = db.Column(db.Boolean, nullable=False, default=False) Message 用于实现“在详情页联系发布者”。每条消息都绑定发送方、接收方和关联的 Item,并记录是否已读,从而支撑消息中心列表与导航栏未读提示。与普通私信不同,这里强制要求 item_id,保证联系必须围绕某条失物/招领展开,消息中心也能据此展示“关于哪条信息的沟通”;is_read 则用于统计未读数,并在用户查看后标记为已读。(3)统一响应与权限装饰器app/utils 提供两套公共能力:统一 JSON 响应格式,以及对接口做登录/管理员校验。所有业务接口都复用它们,避免每个接口各自拼返回体、各自写鉴权代码。# anima-backend/app/utils/__init__.py def success_response(data=None, message='success'): return jsonify({'code': 0, 'message': message, 'data': data or {}}) def error_response(code, message): return jsonify({'code': code, 'message': message, 'data': {}}), \ 400 if code < 500 else 500 def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): verify_jwt_in_request() identity = get_jwt_identity() user = User.query.get(identity) if not user: return error_response(401, '用户不存在') if user.status == 'disabled': return error_response(403, '账号已被禁用') return f(*args, **kwargs, current_user=user) return decorated_function def admin_required(f): # 在登录校验基础上,额外要求 role == 'admin' ... 成功与失败都返回 {code, message, data}:前端只需判断 code === 0 即可处理结果,错误提示取自 message。login_required 装饰在需要登录的接口上:校验请求头中的 JWT,查出对应用户,若账号被禁用则拒绝访问,通过后把 current_user 传给视图函数。admin_required 在此基础上再检查 role == 'admin',专门保护管理端接口。这样鉴权逻辑集中维护,业务代码只需关心业务本身。(4)用户认证接口auth 蓝图负责用户进入系统的全过程,包括创建账号、验证身份、签发登录凭证,以及登录后的个人资料与改密。# anima-backend/app/api/auth.py @auth_bp.route('/register', methods=['POST']) def register(): data = request.get_json() username = data.get('username', '').strip() email = data.get('email', '').strip() password = data.get('password', '') # 校验格式与唯一性后创建用户 user = User(username=username, email=email) user.password = password db.session.add(user) db.session.commit() token = create_access_token(identity=user.id) return success_response({ 'token': token, 'user': user.to_dict() }, '注册成功') @auth_bp.route('/login', methods=['POST']) def login(): username = data.get('username', '').strip() password = data.get('password', '') # 支持用用户名或邮箱登录 user = User.query.filter( (User.username == username) | (User.email == username) ).first() if not user or not user.verify_password(password): return error_response(401, '用户名或密码错误') if user.status == 'disabled': return error_response(403, '账号已被禁用') token = create_access_token(identity=user.id) return success_response({'token': token, 'user': user.to_dict()}, '登录成功') 注册流程为:校验用户名/邮箱/密码格式 → 检查是否已存在 → 创建 User(密码自动哈希)→ 签发 JWT 并连同用户信息返回,注册成功即处于登录态。登录流程为:按用户名或邮箱查找用户 → 校验密码与账号状态 → 同样返回 token 与 user。前端保存该 Token 后,后续请求即可证明身份。同文件还提供需登录才能访问的个人资料读写与修改密码接口。(5)失物招领接口items 蓝图实现平台主业务,包括用户浏览/搜索信息、登录后发布带图信息、对自己发布的信息做编辑删除,以及在找回物品后标记完成。首页列表、详情页、发布页、我的发布都依赖这里的接口。列表查询与排序:口面向所有访客,负责把符合条件的信息分页返回给首页# anima-backend/app/api/items.py @items_bp.route('', methods=['GET']) def get_items(): query = Item.query.filter(Item.status != 'removed') if item_type and item_type in Item.VALID_TYPES: query = query.filter(Item.type == item_type) if keyword: query = query.filter( db.or_(Item.title.contains(keyword), Item.description.contains(keyword)) ) if category and category in Item.VALID_CATEGORIES: query = query.filter(Item.category == category) if location: query = query.filter(Item.location.contains(location)) # 还可按 start_date / end_date 过滤 event_time # 进行中优先,再按创建时间倒序 query = query.order_by( db.case((Item.status == 'active', 0), else_=1), Item.created_at.desc() ) pagination = query.paginate(page=page, per_page=per_page, error_out=False) return success_response({ 'items': [item.to_dict() for item in pagination.items], 'total': pagination.total, 'page': pagination.page, 'pages': pagination.pages }) 查询时,先排除已下架信息,再按类型、关键词、分类、地点、时间等条件叠加过滤;排序上使 active(进行中)排在前面,同优先级再按发布时间倒序,这样首页优先看到仍待匹配的信息。最后分页返回,供前端翻页展示。发布信息与图片上传:要求登录,负责把表单字段和图片落库为一条新 Item@items_bp.route('', methods=['POST']) @login_required def create_item(current_user): # 校验标题、类型、描述、分类、地点、时间等必填项 images = [] files = request.files.getlist('images') for f in files: if f.filename: # 校验格式与大小后,用 UUID 重命名并保存 filename = f'{uuid.uuid4().hex}.{ext}' f.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename)) images.append(f'/static/uploads/{filename}') item = Item( title=title, type=item_type, description=description, category=category, location=location, event_time=event_time, user_id=current_user.id ) item.images = images db.session.add(item) db.session.commit() return success_response(item.to_dict(), '发布成功') 接口使用 multipart/form-data 同时接收文字与文件:先校验业务字段,再逐张检查图片扩展名与大小,保存到上传目录后把访问路径写入 item.images,最后关联 current_user.id 入库。编辑接口逻辑类似,但会保留前端回传的 existing_images,再追加新上传图片,避免改信息时把原图清空。状态变更:用于发布者在物品找回后结束该条信息@items_bp.route('/<int:item_id>/status', methods=['PUT']) @login_required def update_item_status(current_user, item_id): item = Item.query.get(item_id) if item.user_id != current_user.id: return error_response(403, '无权操作') if item.is_completed: return error_response(400, '已完成的信息不可恢复为进行中') if item.type == 'lost' and new_status == 'found': item.status = 'found' elif item.type == 'found' and new_status == 'claimed': item.status = 'claimed' else: return error_response(400, '无效的状态变更') db.session.commit() return success_response(item.to_dict(), '状态更新成功') 只有发布者本人可操作;失物只能标为“已找到”,招领只能标为“已认领”;一旦完成不可再改回进行中。这样既闭环了“发布 → 找回 → 结束”流程,也避免状态被随意篡改。(6)站内消息接口messages 蓝图实现站内联系,即浏览者在详情页给发布者留言,双方在消息中心查看往来,导航栏展示未读数量。它替代真实短信/邮件,把沟通留在平台内完成。# anima-backend/app/api/messages.py @messages_bp.route('', methods=['POST']) @login_required def send_message(current_user): if receiver_id == current_user.id: return error_response(400, '不能联系自己') item = Item.query.get(item_id) if not item or item.status == 'removed': return error_response(404, '关联信息不存在') if item.user_id != receiver_id: return error_response(400, '接收者不是该信息的发布者') message = Message( content=content, sender_id=current_user.id, receiver_id=receiver_id, item_id=item_id ) db.session.add(message) db.session.commit() return success_response(message.to_dict(), '消息发送成功') @messages_bp.route('/unread-count', methods=['GET']) @login_required def get_unread_count(current_user): count = Message.query.filter( Message.receiver_id == current_user.id, Message.is_read == False ).count() return success_response({'count': count}) 发送时校验:不能给自己发、关联信息必须存在且未下架、接收者必须是该信息的发布者。通过后写入 Message,消息中心即可按收发关系列表展示。unread-count 统计当前用户未读收件数,供导航栏提示;同模块还有标记已读等接口,配合前端完成“联系 → 查看 → 已读”的完整链路。(7)管理端接口admin 蓝图只对管理员开放,用于处理违规账号与违规信息:查看用户列表并启用/禁用账号,查看全部信息并上架/下架。# anima-backend/app/api/admin.py @admin_bp.route('/users/<int:user_id>/status', methods=['PUT']) @admin_required def update_user_status(current_user, user_id): if user_id == current_user.id: return error_response(400, '不能禁用自己的账号') # status 仅允许 active / disabled user.status = new_status db.session.commit() return success_response(user.to_dict(), '操作成功') @admin_bp.route('/items/<int:item_id>/status', methods=['PUT']) @admin_required def update_item_status(current_user, item_id): # 管理员可将信息设为 active 或 removed(下架) item.status = new_status db.session.commit() return success_response(item.to_dict(), '操作成功') 两个接口都使用 @admin_required,非管理员无法调用。禁用用户时禁止操作自己,避免管理员误锁自身;信息状态只允许在“进行中 / 已下架”之间切换,下架后前台列表不再展示。同文件还提供用户与信息的分页查询,支撑管理页表格展示。(8)前端请求封装与用户状态前端要调用上述 API,需要在每次请求自动带上登录 Token,并在页面间共享用户身份。request.js 与 Pinia 的 user store 分别承担这两项功能;Vite 代理则让开发环境下前端能无跨域地访问后端。请求层用 Axios 拦截器统一处理鉴权头与登录失效:// anima-frontend/src/api/request.js const api = axios.create({ baseURL: '/api', timeout: 10000 }) api.interceptors.request.use(config => { const userStore = useUserStore() if (userStore.token) { config.headers.Authorization = `Bearer ${userStore.token}` } return config }) api.interceptors.response.use( response => response, error => { if (error.response && error.response.status === 401) { useUserStore().logout() router.push({ name: 'Login', query: { redirect: router.currentRoute.value.fullPath } }) } return Promise.reject(error) } ) 发出请求前,若本地已有 Token,则写入 Authorization: Bearer ...,后端 login_required 才能识别用户;若返回 401,说明登录已失效,则清空本地状态并跳转登录页,同时带上当前路径以便登录后返回。用户状态由 Pinia store 集中管理,并同步到 localStorage,刷新页面后仍保持登录:// anima-frontend/src/stores/user.js export const useUserStore = defineStore('user', () => { const token = ref(localStorage.getItem('token') || '') const user = ref(JSON.parse(localStorage.getItem('user') || 'null')) const isLoggedIn = computed(() => !!token.value) const isAdmin = computed(() => user.value?.role === '管理员') async function login(credentials) { const res = await authApi.login(credentials) if (res.data.code === 0) { token.value = res.data.data.token user.value = res.data.data.user localStorage.setItem('token', res.data.data.token) localStorage.setItem('user', JSON.stringify(res.data.data.user)) } return res } // register / logout / fetchProfile 同理 }) login / register 成功后同时更新内存与本地存储;isLoggedIn、isAdmin 供路由守卫和导航栏判断是否展示管理入口。logout 则清除两端数据。开发时,Vite 把 /api、/static 代理到后端 5000 端口,浏览器只访问 3000,即可完成前后端联调:// anima-frontend/vite.config.js server: { port: 3000, proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true }, '/static': { target: 'http://localhost:5000', changeOrigin: true } } } (9)前端路由守卫与首页路由模块决定用户能打开哪些页面,保护需登录/管理员的功能;首页为失物招领信息的主入口。前者保护需登录/管理员的功能页,把后端列表接口的数据呈现为可搜索、可翻页的卡片列表。路由守卫在每次跳转前检查权限:// anima-frontend/src/router/index.js router.beforeEach((to, from, next) => { const userStore = useUserStore() if (to.meta.requiresAuth && !userStore.isLoggedIn) { next({ name: 'Login', query: { redirect: to.fullPath } }) } else if (to.meta.requiresAdmin && !userStore.isAdmin) { next({ name: 'Home' }) } else { next() } }) 发布、编辑、我的发布、个人中心、消息等路由配置了 meta.requiresAuth,未登录会跳到登录页并记录原目标地址;管理端路由额外配置 requiresAdmin,非管理员会被拦回首页,避免前端直接进入无权页面。首页脚本负责拉取并刷新信息列表:// anima-frontend/src/views/HomeView.vue(脚本核心) async function loadItems() { const params = { page: page.value, per_page: 20, ...filters.value } const res = await itemsApi.getItems(params) if (res.data.code === 0) { items.value = res.data.data.items totalPages.value = res.data.data.pages } } function handleSearch(searchFilters) { filters.value = searchFilters page.value = 1 loadItems() } 页面挂载时调用 loadItems 请求 /api/items;用户在 SearchBar 中筛选时,handleSearch 更新条件并回到第 1 页再请求;分页组件切换页码时同样调用 loadItems。返回的 items 交给 ItemCard 渲染。至此,后端列表能力与前端浏览体验对接完成,构成平台最核心的使用路径:搜索筛选 → 浏览卡片 → 进入详情 → 联系或管理。3.7 开发总结本案例基于华为开发者空间云开发环境和华为云码道(CodeArts)代码智能体,完成了一个智能化失物招领平台的设计与开发。整个开发过程采用规范驱动开发(Spec-Driven Development,SDD)模式,通过需求规格文档、技术设计文档和编码任务文档逐步明确系统目标、架构设计和实现任务,实现了从需求分析到代码生成的流程化开发。在开发过程中,利用云开发环境提供统一稳定的运行环境,避免了本地环境配置和依赖管理带来的问题;通过 CodeArts Agent 对需求进行理解和拆解,自动生成项目 SDD,并依据 SDD 完成前后端代码构建。开发过程中结合实际运行测试结果,通过持续反馈与智能体协作,对系统功能、界面交互和代码结构进行优化,最终实现了用户管理、失物招领信息管理、搜索筛选、图片上传、站内消息等完整业务功能。通过本案例可以看到,AI Agent 驱动的软件开发模式能够有效降低应用开发门槛,提高原型构建和功能迭代效率。同时,基于 SDD 的开发方式使需求、设计与代码之间保持关联,增强了开发过程的可追溯性,为后续维护、扩展以及团队协作提供了良好的基础。四、释放资源4.1 关闭华为开发者空间云开发环境(容器)案例完成后,如无需继续使用该开发环境,可关闭对应云开发环境,避免持续占用开发资源。VS Code 中左侧导航栏选择”开发者空间,选中容器,点击“关机”按钮,弹框点击“确认”即可关闭容器。五、扩展资料说明华为云码道CodeArts实战速成:cid:link_1华为云开发者AI训练营:cid:link_2
-
借助华为云 CodeArts 实现微信公众号表情包机器人一、案例背景在日常使用微信的过程中,表情包经常分散在聊天记录和收藏列表中,手动保存和整理效率较低。为提升表情包保存效率,本项目实现了一个微信公众号表情包机器人:用户向公众号发送图片或表情后,服务端自动保存图片文件,并返回可访问的下载链接。本项目采用 Python Flask 开发微信公众号回调服务,使用 Docker Compose 部署到华为云 ECS。开发过程中,主要借助华为云 CodeArts 进行技术问题咨询、实现流程拆解、部署步骤梳理和问题排查。项目仓库:https://github.com/T0ngJ1PC/wechat-bot二、项目目标本项目需要实现以下能力:接收微信公众号服务器校验请求。接收用户发送的图片或表情消息。下载图片并保存到服务器目录。返回公网可访问的图片链接。对无法直接解析的收藏表情,记录待处理任务并交由后台服务补偿处理。使用 Docker Compose 在华为云 ECS 上部署 Web 服务和后台服务。开源前排除环境变量、登录态、二维码截图、用户素材等隐私文件。三、整体架构项目整体流程如下:微信用户 -> 向公众号发送图片或表情 -> 微信服务器回调 /wechat -> Flask 服务完成签名校验和消息解析 -> 下载图片并保存到 stickers/ -> 返回图片访问链接 无法直接解析的收藏表情 -> 写入 pending_unsupported.jsonl -> scraper 后台服务读取待处理任务 -> 尝试从公众号后台私信页面补偿抓取 -> 保存图片并更新状态文件主要模块如下:模块文件作用微信公众号回调服务app.py处理服务器校验、消息接收、图片保存和管理接口后台补偿服务mp_private_scraper.py处理无法直接解析的收藏表情容器编排docker-compose.yml启动 Web 服务和 scraper 服务镜像构建Dockerfile构建项目运行环境环境变量模板.env.example提供公开配置示例部署说明README-deploy.md记录基础部署命令本项目使用的华为云资源如下:华为云资源用途CodeArts辅助梳理技术细节、实现步骤和排障方法ECS部署并持续运行微信公众号回调服务弹性公网 IP提供公网访问入口安全组控制 HTTP、HTTPS 等端口访问域名与 SSL 证书提供稳定的 HTTPS 微信公众号回调地址四、CodeArts 辅助开发过程本项目中,CodeArts 主要用于辅助完成开发过程中的具体问题分析,而不是直接替代开发过程。实际使用方式是将问题拆分为明确的技术点,再根据 CodeArts 给出的建议进行代码实现和环境验证。开发阶段向 CodeArts 咨询的问题CodeArts 给出的简要结果实际落地结果需求拆解微信公众号表情包机器人需要拆分哪些模块建议拆分为回调接入、图片保存、后台补偿、状态管理和部署运维确定 Web 回调、图片保存、后台补偿、状态管理和部署模块回调接入微信公众号服务器校验如何实现说明 signature、timestamp、nonce、echostr 的校验关系实现 /wechat GET 校验和 check_sig() 签名校验函数消息处理图片、表情和不支持消息类型如何区分处理建议按消息类型分支处理,普通图片同步保存,异常类型进入待处理流程普通图片直接保存,收藏表情进入待处理队列安全模式EncodingAESKey 配置后如何处理加密消息建议将明文模式和安全模式分支处理,先解密再解析,回复时再加密使用 WeChatCrypto 解密消息并加密回复文件保存如何判断图片格式并生成访问链接建议根据响应头判断扩展名,并使用公网基础地址拼接下载链接根据响应头识别图片类型,保存到 stickers/ 目录后台补偿收藏表情无法直接解析时如何处理建议主回调快速响应,复杂抓取逻辑交给后台任务异步执行使用 pending_unsupported.jsonl 记录任务,由 scraper 后台处理容器部署Flask 服务和后台任务如何部署建议 Web 回调和 scraper 拆成两个 Compose 服务,并共享持久化目录使用 Docker Compose 拆分 web 和 scraper 两个服务ECS 部署云服务器部署需要准备哪些步骤建议按 ECS、端口、安全组、环境变量、日志验证拆分部署清单整理 ECS、端口、安全组、环境变量和日志检查流程公网域名域名解析、备案和 SSL 证书应如何准备建议先确认域名解析和备案状态,再配置 HTTPS 证书和反向代理形成域名解析、HTTPS 入口、证书绑定和回调地址检查项开源检查哪些文件不能提交到公开仓库建议排除环境变量、登录态、二维码、token 缓存、用户素材和本地工具状态完善 .gitignore 和 .dockerignore,排除隐私文件通过这种方式,CodeArts 在项目中承担了技术助手角色,主要用于明确实现路径、减少遗漏项,并辅助形成可复用的开发和部署清单。五、核心实现1. 微信公众号服务器校验微信公众号服务器配置时,需要对 signature、timestamp、nonce 和本地配置的 WX_TOKEN 进行校验。项目中通过 check_sig() 完成签名计算:def check_sig(signature, timestamp, nonce): if not signature or not timestamp or not nonce: return False sort_list = sorted([TOKEN, timestamp, nonce]) sha1 = hashlib.sha1(''.join(sort_list).encode()).hexdigest() return hmac.compare_digest(sha1, signature) 服务器校验通过后,接口返回微信传入的 echostr:@app.route('/wechat', methods=['GET']) def verify(): signature = request.args.get('signature', '') timestamp = request.args.get('timestamp', '') nonce = request.args.get('nonce', '') echostr = request.args.get('echostr', '') if check_sig(signature, timestamp, nonce): return text_response(echostr) return text_response('', 403) 2. 消息接收与图片保存用户向公众号发送图片或表情后,Flask 服务接收微信 POST 请求,解析 XML 消息内容。当消息类型为 image 或 emoji 时,读取图片地址并保存到服务器:if parsed.type in ('image', 'emoji'): pic_url = getattr(parsed, 'image', None) or getattr(parsed, 'picurl', None) if pic_url: link = download_and_save(pic_url) 图片下载后保存到 stickers/ 目录,并根据 BASE_URL 返回公网访问链接。这样用户发送图片后,即可收到下载地址。3. 收藏表情补偿处理部分收藏表情无法在微信公众号回调中直接获得原图。项目采用异步处理方式:主回调先记录待处理消息,后台 scraper 服务再尝试补偿抓取。收到无法直接解析的消息 -> 写入 pending_unsupported.jsonl -> scraper 后台读取任务 -> 登录公众号后台私信页面 -> 尝试识别并下载图片这种设计可以避免主回调阻塞,保证微信公众号服务器能够及时收到响应。4. 状态文件持久化项目运行过程中需要保存图片、待处理任务和后台登录态。Docker Compose 中将这些文件和目录挂载到宿主机,避免容器重建后数据丢失:volumes: - ./stickers:/opt/wechat-bot/stickers - ./mp_state.json:/opt/wechat-bot/mp_state.json - ./mp_scraper_state.json:/opt/wechat-bot/mp_scraper_state.json - ./mp_scraper_status.json:/opt/wechat-bot/mp_scraper_status.json - ./pending_unsupported.jsonl:/opt/wechat-bot/pending_unsupported.jsonl - ./access_token_cache.json:/opt/wechat-bot/access_token_cache.json六、华为云 ECS 部署1. 准备服务器在华为云 ECS 上准备 Linux 运行环境,并完成以下配置:绑定弹性公网 IP。配置安全组,放通 80、443 端口。安装 Docker 和 Docker Compose。如使用域名,将域名解析到 ECS 公网 IP。生产环境建议通过 Nginx 或 Caddy 提供 HTTPS 入口。2. 准备公网域名、备案和 SSL 证书微信公众号服务器地址需要公网可访问。部署阶段,CodeArts 辅助梳理了公网入口相关检查项,主要包括域名解析、备案状态、SSL 证书和反向代理配置。配置项CodeArts 给出的简要结果项目中的处理方式域名解析将业务域名解析到 ECS 绑定的公网 IP使用域名作为微信公众号回调地址备案状态如域名使用场景需要备案,应先完成备案状态确认在正式配置公众号回调前确认域名可用于公网访问SSL 证书微信公众号回调建议使用 HTTPS 地址为域名配置 SSL 证书,提供 https://你的域名/wechat反向代理HTTPS 入口转发到本地 Flask 服务端口使用 Nginx 或 Caddy 将请求转发到 127.0.0.1:5000访问验证回调地址配置前应先验证域名和证书可访问使用浏览器或 curl 检查 HTTPS 访问结果公网入口准备完成后,再将 .env 中的 BASE_URL 配置为正式域名下的图片访问地址:BASE_URL=https://你的域名/stickers3. 初始化目录和配置mkdir -p /opt/wechat-bot/stickers /opt/wechat-bot/verify cd /opt/wechat-bot cp .env.example .env touch pending_unsupported.jsonl touch mp_state.json mp_scraper_state.json mp_scraper_status.json access_token_cache.json.env 中配置公众号和服务运行所需参数:WX_TOKEN=替换为公众号后台Token WX_APPID=替换为公众号AppID WX_AES_KEY=替换为EncodingAESKey BASE_URL=https://example.com/stickers ADMIN_TOKEN=替换为管理接口Token STICKER_DIR=/opt/wechat-bot/stickers PENDING_FILE=/opt/wechat-bot/pending_unsupported.jsonl MP_QR_FILE=/opt/wechat-bot/mp_login_qr.png MP_STATUS_FILE=/opt/wechat-bot/mp_scraper_status.json4. 启动服务docker-compose build docker-compose up -d docker-compose ps 查看日志:docker-compose logs -f web docker-compose logs -f scraper健康检查:curl http://127.0.0.1:5000/返回以下内容表示服务正常:wechat-bot ok5. 配置微信公众号回调在微信公众号后台配置服务器地址:https://你的域名/wechat保存配置时,观察 web 服务日志。如果出现以下日志,说明服务器校验成功:VERIFY OK七、问题排查问题排查方向公众号服务器校验失败检查 WX_TOKEN 是否一致,反向代理是否保留 query string,回调路径是否为 /wechat加密消息解密失败检查 WX_APPID 和 WX_AES_KEY 是否正确图片保存成功但链接打不开检查 BASE_URL、安全组、HTTPS 入口和静态文件访问路径域名或 HTTPS 不可访问检查域名解析、备案状态、SSL 证书、证书绑定和反向代理配置容器重启后状态丢失检查 docker-compose.yml 中 volumes 是否正确挂载收藏表情未被处理检查 pending_unsupported.jsonl、scraper 日志和 mp_scraper_status.json管理接口无法访问检查 ADMIN_TOKEN 是否配置正确常用排查命令:docker-compose ps docker-compose logs -f web docker-compose logs -f scraper ls -lh stickers tail -n 20 pending_unsupported.jsonl八、安全与开源处理项目公开前,需要排除隐私信息和运行态文件。本项目已通过 .gitignore 和 .dockerignore 排除以下内容:类型示例环境变量.env、private.env公众号密钥WX_TOKEN、WX_APPSECRET、WX_AES_KEY管理密钥ADMIN_TOKEN登录态文件mp_state.jsontoken 缓存access_token_cache.json运行状态mp_scraper_state.json、mp_scraper_status.json二维码和截图mp_login_qr.png、mp_private_last.png用户素材stickers/*本地工具状态.arts/、.codeartsdoer/提交前可执行以下命令检查仓库跟踪文件:git ls-files也可进行敏感关键字扫描:rg -n "(token=|APPSECRET|ADMIN_TOKEN|WX_TOKEN|access_token|private.env|mp_state)" . 公开案例或向 CodeArts 提问时,也应避免粘贴真实密钥、登录二维码、OpenID、公网 IP、聊天记录和用户素材。九、实践效果通过本项目,完成了一个可部署、可运行、可开源的微信公众号表情包机器人。项目实现了公众号回调接入、图片保存、收藏表情补偿处理、Docker Compose 部署和隐私文件排除。CodeArts 在实践中的主要价值体现在:帮助拆解项目模块和实现流程。辅助确认微信公众号接入和消息处理细节。辅助整理 ECS 部署步骤和容器持久化配置。辅助梳理公网域名、备案状态、SSL 证书和 HTTPS 回调地址配置。辅助形成常见问题排查清单。辅助梳理开源前的隐私信息排除范围。十、后续优化方向当前方案可优化方向ECS 本地 stickers/ 存储迁移到 OBS 对象存储JSON 文件保存状态使用 SQLite、Redis 或云数据库Docker Compose 查看日志接入云日志服务单台 ECS 部署镜像化部署、多实例和负载均衡ADMIN_TOKEN 简单保护增加 IP 白名单、WAF 或访问审计手动向 CodeArts 提问沉淀固定提问模板和项目知识库对于个人项目,当前方案可以先满足基本使用需求。后续可根据访问量、稳定性要求和维护成本逐步接入更多云服务能力。十一、总结本案例基于华为云 CodeArts 和 ECS,完成了微信公众号表情包机器人的开发与部署。CodeArts 主要用于辅助技术咨询、流程拆解、部署梳理、公网域名与 SSL 配置检查、以及排障复盘;ECS 提供稳定的云上运行环境;Docker Compose 负责服务编排和运行维护。项目最终形成了从需求分析、代码实现、云上部署、问题排查到安全开源处理的完整实践流程,适合作为个人开发者使用 CodeArts 辅助完成云上应用开发的参考案例。项目仓库:https://github.com/T0ngJ1PC/wechat-bot参考资料华为云论坛:cid:link_4华为云 ECS 最佳实践:cid:link_2CodeArts 代码智能体产品介绍:cid:link_3CodeArts IDE 智能问答:cid:link_0CodeArts IDE 智能体对话:cid:link_1
-
[问题求助] Cannot connect to API: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in theCannot connect to API: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()这个问题怎么解决?
yd_253849762
发表于2026-07-21 10:54:24
2026-07-21 10:54:24
最后回复
CodeArts小助手-蚂蚁
2026-07-21 11:18:25
57 1
上滑加载中
推荐直播
-
用码道,让你的AI作品三步上朋友圈2026/08/04 周二 19:00-20:00
林华鼎-华为云AI开发者运营负责人
从入门 · 到做AI应用 · 到企业级开发。不教编程,只教用AI · 零代码、有产出、能带走、可炫耀 · 每课人人动手实操
回顾中 -
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
基于华为云码道,构建你的定制化AI搭子2026/08/14 周五 09:00-11:30
明亮-华为云开发者发展与支持部部长
本期直播将向您全面介绍华为云码道产品,并基于码道手把手教你部署自己的定制化AI陪伴搭子。
回顾中
热门标签