• [技术干货] 基于华为云码道(CodeArts)代码智能体的ForumAdmin:论坛管理系统
    基于华为云码道(CodeArts)代码智能体的ForumAdmin:论坛管理系统一、项目简介本次实战我使用华为云码道(CodeArts)代码智能体完成了 ForumAdmin 论坛管理系统。项目围绕"管理员、版主、会员"三种角色展开,实现了登录、发帖、跟帖、删帖、置顶等论坛基础功能,同时实现了DFA敏感词过滤算法、管理员禁言与用户拉黑双层机制、BCrypt密码加密存储,以及Discourse风格的现代论坛UI,形成了功能完备、权限清晰、内容安全的论坛管理系统。代码仓库:ForumAdmin(GitCode)前端:Vue 3 + Vite + Element Plus + Pinia + Vue Router + Axios后端:Java 17 + Spring Boot 3 + MyBatis-Plus + Sa-Token + BCrypt + Druid数据库:MySQL 9.4ForumAdmin 首页二、原始任务与完成情况原题为:论坛管理。实现论坛的版主管理、版块管理、内容管理。需要登陆进入论坛,可以发帖、跟帖和删帖以及置顶等功能。同时能够对敏感词汇进行过滤,拉黑某些会员。我将原题拆成了可验收的功能项,并为每项建立"页面操作 + 后端权限"的证据闭环:原题要求实现方式对应证据登录进入论坛Sa-Token 会话管理、BCrypt 密码加密、注册、退出登录页、注册页版主管理ADMIN 分配版主;MODERATOR 可进入后台管理版块管理页、角色标签显示版块管理创建、编辑、删除、排序、版主绑定版块管理页内容管理帖子/回复分页管理、置顶、删除帖子列表、帖子详情页发帖登录会员选择版块并发布,发布前完成禁言校验和敏感词过滤发帖弹窗、发帖接口跟帖帖子详情页回复,禁言用户和被拉黑用户不可回复帖子详情页、回复区域删帖作者或管理者可删除;逻辑删除删除按钮、删除接口置顶ADMIN 或 MODERATOR 可置顶/取消置顶,置顶优先排序置顶按钮、帖子列表排序敏感词过滤DFA 算法 O(n) 时间复杂度,词库动态重载敏感词管理页、发帖过滤效果拉黑会员管理员禁言(全站) + 用户拉黑(个人);发帖和回复前后端拦截黑名单页、禁言操作、拉黑按钮三、码道代码智能体开发实录我全程使用华为云码道(CodeArts)代码智能体进行开发,从项目初始化到最终UI改造,智能体深度参与了每个阶段。以下是按时间顺序的开发实录:3.1 项目创建与任务理解在码道代码智能体中创建项目后,我将原始任务描述提交给智能体:"实现论坛的版主管理、版块管理、内容管理。需要登陆进入论坛,可以发帖、跟帖和删帖以及置顶等功能。同时能够对敏感词汇进行过滤,拉黑某些会员。"智能体准确理解了需求,并主动拆解为以下模块:用户认证与权限管理(登录/注册/角色控制)版块管理(CRUD + 版主绑定)帖子与回复(发帖/跟帖/删帖/置顶)敏感词过滤(DFA算法)拉黑会员(用户屏蔽机制)3.2 后端项目搭建智能体一步完成了 Spring Boot 3 后端项目初始化,包括:引入 spring-boot-starter-web、mybatis-plus-spring-boot3-starter、sa-token-spring-boot3-starter、druid-spring-boot-3-starter、spring-security-crypto 等核心依赖配置 application.yml(数据源、MyBatis-Plus逻辑删除、Sa-Token会话参数)创建 Result 统一响应封装和 GlobalExceptionHandler 全局异常处理设计6张数据库表(user / section / post / reply / sensitive_word / user_block),编写 init.sql3.3 前端项目搭建智能体使用 Vite 脚手架创建 Vue 3 前端项目,集成:Element Plus 组件库及图标库Pinia 状态管理(用户token和userInfo持久化)Axios 请求封装(baseURL代理、token拦截器、401自动跳转)Vue Router 路由配置(含登录守卫)3.4 核心功能实现智能体按模块逐步生成完整CRUD代码:5组 Entity + Mapper + Service + Controller:User、Section、Post、Reply、SensitiveWordSa-Token RBAC权限:实现 StpInterface 接口,根据用户uid动态返回角色列表BCrypt密码加密:注册时加密存储,登录时matches验证;DataInitializer启动自动检测并转换明文密码DFA敏感词过滤算法:智能体独立实现了确定有限自动机,包括字典构建、匹配检测、文本替换三个核心方法拉黑与禁言双层机制:新增 user_block 表和 UserBlockController,帖子列表SQL动态过滤被拉黑用户,回复前校验帖子作者是否拉黑了回复人3.5 Bug修复与优化开发过程中遇到多个问题,智能体均快速定位并修复:问题原因智能体解决方案版块名称乱码PowerShell管道传输SQL编码丢失移除SQL初始数据,改用Java DataInitializer自动初始化,确保UTF-8密码明文存储初始实现未加密引入BCryptPasswordEncoder,DataInitializer自动检测并转换后台管理按钮不可见仅ADMIN角色可见扩展为ADMIN和MODERATOR均可进入帖子详情API返回nullgetById不关联查询新增selectDetailById关联查询用户名/版块名/回复数帖子创建标题/内容为空前后端均未校验前端表单验证 + 后端参数非空校验3.6 UI改造智能体根据我确认的Discourse风格方案,完成了全部页面改造:创建公共 AppHeader 组件(白底头部 + Logo + 角色标签 + 管理入口)首页:横向分类标签 + 帖子行(头像+标题+分类标签+回复数)+ 浮动发帖按钮帖子详情:时间线流式回复(圆点+连线)+ 底部固定编辑器登录页:浅灰背景 + 圆角卡片后台管理:白底侧边栏 + 图标菜单配色统一:主背景#fff、链接蓝#0088cc、分类标签彩色3.7 开发效率总结通过码道代码智能体,整个项目从零到功能完备仅用时约2天。智能体在以下方面显著提升了效率:代码生成:后端CRUD、前端页面、API接口等重复性代码一键生成算法实现:DFA敏感词过滤等非平凡算法智能体独立完成Bug修复:编码乱码、密码明文等问题智能体快速定位根因并修复架构决策:技术选型(Sa-Token vs Spring Security)、权限模型设计等智能体给出了合理建议一致性保障:公共组件、统一响应封装、路由守卫等确保了代码风格一致四、系统架构与业务闭环整体架构保持前后端分离,开发模式下前端Vite Dev Server代理后端API:会员 / 版主 / 管理员 │ ▼ Vue 3 + Vite (5173) │ Axios → /api 代理 ▼ Sa-Token 会话认证 │ ├── 用户服务 ── BCrypt 密码验证 ├── 论坛业务服务 ── MyBatis-Plus ── MySQL 9.4 ├── RBAC 权限 ── ADMIN / MODERATOR / MEMBER ├── 敏感词 DFA ── 过滤/替换 └── 拉黑/禁言 ── 帖子过滤 + 回复拦截发帖和回复走统一治理链路:提交内容 → 校验是否被禁言(user.status = 0) → 校验是否被帖子作者拉黑 → DFA 敏感词过滤(替换为 ***) → 保存并返回这种设计确保了:禁言用户无法发帖和回帖;被拉黑用户的帖子不会出现在列表中;被帖子作者拉黑的用户无法回复该帖子;敏感词在保存前即被过滤替换。五、逐项功能实现1. 登录进入论坛系统支持注册、登录和退出。登录成功后前端携带 Sa-Token 的 UUID Token 访问需要授权的接口;密码使用 BCrypt 加密存储。登录页面注册页面登录接口实现:@PostMapping("/login") public Result<Map<String, Object>> login(@RequestBody Map<String, String> params) { User user = userService.login(params.get("username"), params.get("password")); Map<String, Object> data = new HashMap<>(); data.put("token", StpUtil.getTokenValue()); data.put("user", user); return Result.ok(data); } Sa-Token 权限配置,根据用户角色动态返回权限列表:@Component public class SaTokenConfig implements StpInterface { private final UserMapper userMapper; @Override public List<String> getPermissionList(Object loginId, String loginType) { return new ArrayList<>(); } @Override public List<String> getRoleList(Object loginId, String loginType) { List<String> roles = new ArrayList<>(); User user = userMapper.selectById(Long.valueOf(loginId.toString())); if (user != null) { roles.add(user.getRole()); } return roles; } } 2. 版主管理管理员可以在版块管理页为每个版块分配版主(通过 moderatorId 字段)。系统实现了三级角色权限控制:角色前台后台管理说明ADMIN发帖/回帖/删帖/置顶版块管理+用户管理(禁言)+敏感词管理超级管理员MODERATOR发帖/回帖/删帖/置顶可进入后台版主MEMBER发帖/回帖不可进入普通会员前端通过角色标签清晰展示当前用户身份:3. 版块管理系统实现了版块的创建、编辑、删除、排序和版主配置。DataInitializer 在启动时自动检查并修复版块数据,确保中文编码正确:private static final String[][] DEFAULT_SECTIONS = { {"技术交流", "讨论技术相关话题"}, {"生活杂谈", "分享生活点滴"}, {"求助问答", "提出问题寻求帮助"} }; 版块管理页面4. 发帖与跟帖登录会员可以选择版块发布帖子,并在帖子详情页回复。发帖与回复都会先检查禁言状态和拉黑关系,再进行敏感词过滤。发帖接口实现(含校验与敏感词过滤):@PostMapping("/add") public Result<Void> add(@RequestBody Post post) { if (post.getTitle() == null || post.getTitle().trim().isEmpty()) { return Result.fail("标题不能为空"); } if (post.getContent() == null || post.getContent().trim().isEmpty()) { return Result.fail("内容不能为空"); } if (post.getSectionId() == null) { return Result.fail("请选择版块"); } Long userId = StpUtil.getLoginIdAsLong(); User user = userService.getById(userId); if (user.getStatus() == 0) { return Result.fail("您已被禁言,无法发帖"); } post.setUserId(userId); post.setTitle(sensitiveWordService.filter(post.getTitle())); post.setContent(sensitiveWordService.filter(post.getContent())); post.setIsTop(0); postService.save(post); return Result.ok(); } 回复接口实现(含拉黑校验):@PostMapping("/add") public Result<Void> add(@RequestBody Reply reply) { Long userId = StpUtil.getLoginIdAsLong(); User user = userService.getById(userId); if (user.getStatus() == 0) { return Result.fail("您已被禁言,无法回复"); } Post post = postService.getById(reply.getPostId()); if (post != null && userBlockService.hasBlocked(post.getUserId(), userId)) { return Result.fail("帖子作者已拉黑您,无法回复"); } reply.setUserId(userId); reply.setContent(sensitiveWordService.filter(reply.getContent())); replyService.save(reply); return Result.ok(); } 帖子列表关联查询,一次SQL获取用户名、版块名和回复数:@Select("<script>" + "SELECT p.*, u.username, u.nickname, s.name AS section_name, " + "(SELECT COUNT(*) FROM reply r WHERE r.post_id = p.id AND r.is_deleted = 0) AS reply_count " + "FROM post p LEFT JOIN user u ON p.user_id = u.uid LEFT JOIN section s ON p.section_id = s.id " + "WHERE p.is_deleted = 0 " + "<if test='blockedIds != null and blockedIds.size() > 0'>" + "AND p.user_id NOT IN " + "<foreach collection='blockedIds' item='id' open='(' separator=',' close=')'>#{id}</foreach>" + "</if>" + " ORDER BY p.is_top DESC, p.create_time DESC" + "</script>") IPage<Post> selectPageAll(Page<Post> page, @Param("blockedIds") java.util.List<Long> blockedIds); 帖子详情页5. 删帖与置顶作者可以删除自己的帖子,管理员和版主可以执行管理删除。删除采用 MyBatis-Plus 逻辑删除(is_deleted 字段),不会物理清除数据。置顶操作更新 is_top 字段,帖子列表按置顶优先、时间倒序排列。置顶接口:@PutMapping("/top/{id}") public Result<Void> top(@PathVariable Long id, @RequestParam Integer isTop) { postService.setTop(id, isTop); return Result.ok(); } 逻辑删除配置:mybatis-plus: global-config: db-config: logic-delete-field: isDeleted logic-delete-value: 1 logic-not-delete-value: 0 6. 敏感词过滤敏感词管理支持新增、删除和动态重载。过滤算法采用 DFA(确定有限自动机),时间复杂度为 O(n),其中 n 为待过滤文本长度,与敏感词数量无关。DFA 核心实现:@Component public class SensitiveWordFilter { private final Map<Character, Object> dictMap = new HashMap<>(); private static final String REPLACEMENT = "***"; public void loadWords(List<SensitiveWord> words) { dictMap.clear(); for (SensitiveWord sw : words) { String word = sw.getWord().trim(); if (word.isEmpty()) continue; Map<Character, Object> current = dictMap; for (int i = 0; i < word.length(); i++) { char c = word.charAt(i); Object next = current.get(c); if (next == null) { Map<Character, Object> newNode = new HashMap<>(); current.put(c, newNode); current = newNode; } else { current = (Map<Character, Object>) next; } } current.put('\0', null); } } public String filter(String text) { if (text == null || text.isEmpty()) { return text; } StringBuilder result = new StringBuilder(text); for (int i = 0; i < text.length(); i++) { int length = checkSensitiveWord(text, i); if (length > 0) { for (int j = i; j < i + length; j++) { result.setCharAt(j, REPLACEMENT.charAt(0)); } i += length - 1; } } return result.toString(); } @SuppressWarnings("unchecked") private int checkSensitiveWord(String text, int startIndex) { Map<Character, Object> current = dictMap; int length = 0; for (int i = startIndex; i < text.length(); i++) { char c = text.charAt(i); Object next = current.get(c); if (next == null) { break; } length++; current = (Map<Character, Object>) next; if (current.containsKey('\0')) { return length; } } return 0; } } 词库动态重载接口:@GetMapping("/refresh") public Result<Void> refresh() { sensitiveWordService.refreshDict(); return Result.ok(); } 敏感词管理页面7. 拉黑会员系统实现了双层机制:管理员禁言:全站级别,修改 user.status = 0,禁言用户可登录但无法发帖和回帖用户拉黑:个人级别,通过 user_block 表记录,拉黑后看不到对方帖子,对方无法回复自己的帖子操作执行者范围效果禁言管理员全站可登录,但无法发帖、回帖拉黑任何用户个人我看不到他的帖子,他不能回复我的帖子拉黑接口实现:@RestController @RequestMapping("/api/block") @RequiredArgsConstructor public class UserBlockController { private final UserBlockService userBlockService; @PostMapping("/add/{blockedUserId}") public Result<Void> block(@PathVariable Long blockedUserId) { Long userId = StpUtil.getLoginIdAsLong(); userBlockService.block(userId, blockedUserId); return Result.ok(); } @DeleteMapping("/remove/{blockedUserId}") public Result<Void> unblock(@PathVariable Long blockedUserId) { Long userId = StpUtil.getLoginIdAsLong(); userBlockService.unblock(userId, blockedUserId); return Result.ok(); } @GetMapping("/list") public Result<List<UserBlock>> myBlockList() { Long userId = StpUtil.getLoginIdAsLong(); return Result.ok(userBlockService.myBlockList(userId)); } } 帖子列表查询时动态过滤被拉黑用户的帖子:@GetMapping("/list") public Result<IPage<Post>> list(@RequestParam(required = false) Long sectionId, @RequestParam(defaultValue = "1") long page, @RequestParam(defaultValue = "10") long size) { List<Long> blockedIds = Collections.emptyList(); if (StpUtil.isLogin()) { blockedIds = userBlockService.getBlockedUserIds(StpUtil.getLoginIdAsLong()); } if (sectionId != null) { return Result.ok(postService.pageBySectionId(sectionId, page, size, blockedIds)); } return Result.ok(postService.pageAll(page, size, blockedIds)); } 黑名单管理页面8. Discourse 风格 UI系统采用 Discourse 风格的现代论坛 UI 设计,主要特点包括:白底头部:统一的 AppHeader 组件,包含 Logo、用户信息、角色标签、管理入口横向分类标签:版块以彩色标签形式横向排列,点击切换过滤帖子列表:每行显示头像、标题、分类标签、回复数,替代传统表格时间线流式回复:帖子详情页回复以圆点+连线的时间线形式展示底部固定编辑器:回复输入框固定在页面底部,随时可输入浮动发帖按钮:右下角圆形按钮,点击弹出发帖弹窗公共头部组件核心代码:<template> <header class="app-header"> <div class="header-inner"> <div class="header-left" @click="$router.push('/')"> <span class="logo">Forum</span> </div> <div class="header-right"> <span class="user-name">{{ userStore.userInfo?.nickname || '用户' }}</span> <el-tag v-if="userStore.userInfo?.role === 'ADMIN'" type="danger" size="small" effect="dark">管理员</el-tag> <el-tag v-else-if="userStore.userInfo?.role === 'MODERATOR'" color="#e6a23c" size="small" effect="dark" style="color:#fff;border:none">版主</el-tag> <el-tag v-else type="info" size="small" effect="plain">会员</el-tag> <el-button v-if="userStore.userInfo?.role === 'ADMIN' || userStore.userInfo?.role === 'MODERATOR'" text @click="$router.push('/admin')"> <el-icon><Setting /></el-icon> 管理 </el-button> <el-button text @click="$router.push('/blocklist')"> <el-icon><UserFilled /></el-icon> 黑名单 </el-button> <el-button text type="danger" @click="handleLogout"> <el-icon><SwitchButton /></el-icon> 退出 </el-button> </div> </div> </header> </template> 首页帖子列表核心代码:<div v-for="p in posts" :key="p.id" class="post-row" @click="$router.push(`/post/${p.id}`)"> <div class="post-avatar"> <el-avatar :size="36" :style="{ background: sectionColor(p.sectionId) }"> {{ (p.nickname || p.username || '?')[0] }} </el-avatar> </div> <div class="post-body"> <div class="post-title-row"> <el-tag v-if="p.isTop === 1" type="danger" size="small" effect="dark" class="top-tag">置顶</el-tag> <span class="post-title">{{ p.title }}</span> <span class="post-cat" :style="{ background: sectionColor(p.sectionId) + '18', color: sectionColor(p.sectionId) }">{{ p.sectionName }}</span> </div> <div class="post-meta"> <span class="meta-author">{{ p.nickname || p.username }}</span> <span class="meta-dot">·</span> <span class="meta-time">{{ p.createTime }}</span> </div> </div> <div class="post-stats"> <el-icon><ChatDotRound /></el-icon> <span>{{ p.replyCount || 0 }}</span> </div> </div> 首页全貌帖子详情时间线回复六、创新与易用性DFA 敏感词过滤:采用确定有限自动机算法,时间复杂度 O(n)(n为文本长度),与敏感词数量无关;词库支持动态热重载,无需重启服务。拉黑/禁言双层机制:管理员禁言为全站级别(修改 user.status),用户拉黑为个人级别(user_block 表),两者逻辑清晰、互不干扰,分别作用于不同场景。BCrypt 密码加密:使用 BCryptPasswordEncoder 加密存储密码,BCrypt 自带盐值,每次加密结果不同,抗彩虹表攻击。DataInitializer 启动时自动检测并转换明文密码。Discourse 风格 UI:白底头部、横向分类标签、时间线流式回复、底部固定编辑器、浮动发帖按钮,提供现代论坛体验。前后端双重校验:前端表单提交前验证必填项,后端接口同样校验参数非空,避免绕过前端直接提交空数据。数据自动修复:DataInitializer 启动时自动检测并修复中文乱码、明文密码、缺失数据等问题,确保系统开箱即用。七、码道新能力与华为云技术使用使用华为云码道(CodeArts)代码智能体完成全流程开发,从项目搭建、代码生成到功能实现和Bug修复;通过自然语言 Prompt 指导智能体完成数据库设计、CRUD 接口生成、敏感词算法实现、权限配置和 UI 改造;使用浏览器控制能力对 ADMIN、MODERATOR、MEMBER 等真实页面进行验收和截图,避免以 mock 页面冒充后端功能;项目级规则约束技术栈(SpringBoot3 + Vue3)、权限模型(RBAC 三级角色)和内容治理(DFA + 禁言 + 拉黑)。八、测试与验证结果项目完成了前端生产构建和后端编译验证:验证项结果后端编译mvn compile 通过前端生产构建npm run build 通过,所有组件正常打包浏览器真实验收多张页面证据,关键页面功能正常权限验证未登录跳转登录页,MEMBER 不可进入后台验证命令如下:# 后端编译 cd backend mvn compile # 前端生产构建 cd ../frontend npm run build九、本地运行本地开发# 1. 初始化数据库 mysql -u root -p < sql/init.sql # 2. 启动后端 cd backend mvn spring-boot:run # 3. 另开终端启动前端 cd frontend npm install npm run dev访问 http://localhost:5173,后端API端口为 8080。一键启动项目提供了 start.bat 一键启动脚本,双击即可同时启动后端和前端。默认账号账号密码角色adminadmin123ADMINmoderatormod123MODERATORmembermem123MEMBER十、逐项对应评分项项目成果与证据创新易用DFA敏感词O(n)过滤、拉黑/禁言双层机制、BCrypt加密、Discourse风格UI、前后端双重校验、数据自动修复功能完备原题10个功能点全部闭环;登录/注册/发帖/回帖/删帖/置顶/敏感词/禁言/拉黑/版块管理全部实现技术能力码道全流程开发、Sa-Token认证+RBAC、DFA算法、MyBatis-Plus逻辑删除、前后端分离架构文档完整性开发日志、数据库设计、API接口、本地运行说明、默认账号、一键启动脚本完整十一、项目成果总结这次实战让我从"实现一个能发帖的页面",进一步走到了"构建一个权限正确、内容安全、UI现代、开箱即用的论坛系统"。ForumAdmin 已在本地完成核心功能、生产构建验证和真实页面验收;项目同时保留了开发日志、数据库设计、API接口文档和运行说明,方便评审复现。核心成果:6张数据库表:user / section / post / reply / sensitive_word / user_block,支持逻辑删除和自动时间填充DFA敏感词过滤:O(n)时间复杂度,动态词库热重载拉黑/禁言双层机制:全站禁言与个人屏蔽分离,逻辑清晰BCrypt密码加密:启动自动检测并转换明文密码Discourse风格UI:白底头部、横向分类标签、时间线回复、浮动发帖按钮一键启动:start.bat 双击即可运行
  • [技术干货] CampFlow 训练营成果管理助手:CodeArts Agent 项目实践
    大家好,我本次训练营完成的项目是 CampFlow 训练营成果管理助手。这是一个面向华为云码道暑期实习训练营的 Web 工作台,用来帮助学生把在线学习、CodeArts Agent 辅助开发、项目构建、华为云部署、案例文档发布和最终作品链接提交这些分散任务整合到一个可追踪的流程中。一、项目背景在训练营实践过程中,我发现项目提交并不只是写出一个能运行的页面,还需要同时完成学习进度、CodeArts Agent 使用过程、项目部署、案例文档、附件材料和最终链接提交。任务节点比较分散,如果只靠临时记录,很容易出现材料遗漏、演示链接忘记回填、评分维度没有证据支撑等问题。因此我设计了 CampFlow,希望它既是一个可运行的训练营成果管理应用,也能反向辅助我整理最终提交材料。它把指导书中的评分维度和提交要求产品化,形成项目档案、里程碑、评分证据、智能体提示词和案例文档生成几个模块,让整个实习项目从开发到提交都更加清晰。二、作品链接作品演示地址:https://campflow-demo-20260727.fangtianchen3.chatgpt.site/案例中心链接:https://devstation.connect.huaweicloud.com/space/devportal/casecenter/11272f6a85244aad99d738b32bac093c/2三、技术选型本项目采用纯静态前端方案,核心技术如下:- CodeArts Agent:辅助需求分析、架构设计、功能开发、测试部署和案例文档整理- HTML / CSS / JavaScript:实现页面结构、交互逻辑和响应式布局- localStorage:在浏览器本地保存项目档案、里程碑、评分证据和开发记录- 华为云 OBS:托管静态网站并提供公网访问地址- Node.js assert:对得分计算、数据模型和文档生成逻辑进行基础测试选择纯静态方案的原因是训练营项目更看重完整交付闭环。静态 Web 应用部署简单、依赖少、访问稳定,适合快速完成从代码构建到华为云部署的全过程。四、需求分析根据训练营指导书,项目成果需要包含可运行应用、案例文档、演示链接或视频,并且 Web 类应用需要部署到华为云。评分维度包括创新易用、功能完备、技术能力、文档完整性和参与度。围绕这些要求,我将需求拆成五个方向:1. 成果管理:记录项目名称、应用方向、技术栈、演示链接和案例链接。2. 过程管理:把学习、开发、部署、发布等训练营节点转化为里程碑。3. 智能体协作:自动生成适合 CodeArts Agent 的阶段性提示词。4. 评分证据:按评分维度整理证据项、完成状态、说明和链接。5. 文档发布:根据项目档案和证据清单生成案例 Markdown 草稿。这样做的好处是,项目不只是“做完一个页面”,而是把提交要求转化成了可以持续检查的工作流。五、系统架构设计CampFlow 的整体结构比较轻量:```text用户浏览器  ├─ index.html:应用入口  ├─ assets/styles.css:界面样式和响应式布局  ├─ assets/app.js:状态管理、评分计算、提示词生成和 Markdown 生成  └─ localStorage:保存项目档案、评分证据、里程碑和开发记录```项目没有引入后端服务,所有数据保存在浏览器本地。对于训练营展示场景来说,这种方案可以降低部署复杂度,也避免后端账号、数据库和接口联调带来的额外成本。六、CodeArts Agent 辅助开发过程本项目按照 vibe coding 的思路使用 CodeArts Agent。我的实践方式不是一次性让智能体直接生成全部代码,而是分阶段推进:第一阶段是需求分析。我先让 CodeArts Agent 根据指导书梳理项目目标、评分维度和必须交付的材料,避免项目范围跑偏。第二阶段是架构设计。我让智能体输出静态前端架构、本地存储数据模型,以及每个页面模块需要承担的职责。第三阶段是功能开发。根据模块拆分,逐步实现仪表盘、项目档案、提示词生成、评分证据、案例文档生成和部署清单。第四阶段是调试验证。重点检查评分计算是否符合权重、localStorage 是否能正常保存、Markdown 文档是否能完整生成、部署文件是否齐全。第五阶段是文档整理。最后让智能体辅助把开发过程、部署步骤、项目亮点和总结整理成案例中心与论坛都能使用的材料。通过这种分阶段协作,CodeArts Agent 更像一个项目搭档,而不是简单的代码生成器。它帮助我把任务拆小,也让我在每一步都有可检查的输出。七、核心功能介绍1. 仪表盘:展示预计得分、证据完成数量、里程碑完成数量,并按指导书权重拆解评分维度。2. 项目档案:维护项目名称、应用方向、真实问题、解决方案、技术栈、考试状态、演示链接和案例链接。3. CodeArts 提示词生成:根据项目当前状态生成需求分析、架构设计、功能开发、调试部署和案例文档五类提示词,方便继续与智能体协作。4. 评分证据清单:按照创新易用、功能完备、技术能力、文档完整性和参与度五个维度组织证据项,支持完成状态和说明记录。5. 案例文档生成器:把项目档案、评分证据和开发记录拼装成 Markdown 草稿,减少最终发布时遗漏章节的风险。6. 部署清单:列出静态网站部署需要上传的文件,并提醒检查 OBS 桶、静态网站首页、公开访问策略和演示链接。八、部署过程项目最终部署在华为云 OBS 静态网站托管上。部署步骤如下:1. 创建 OBS 桶 `campflow-tj-20260726`,区域选择华东-上海一 `cn-east-3`。2. 上传静态网站文件,根目录包含 `index.html`,`assets` 目录包含 `app.js` 和 `styles.css`。3. 开启静态网站托管,默认首页设置为 `index.html`。4. 创建桶策略 `campflow-public-read`,允许公网读取静态网站对象。5. 访问 OBS 静态网站地址,确认页面可以正常打开。验证结果:- 首页 HTTP 状态为 200- 页面内容包含 CampFlow- 静态网站公网地址可访问九、测试与验收项目包含基础逻辑测试,主要验证数据模型、评分计算和文档生成相关逻辑。测试通过后,我又进行了浏览器访问检查,确认页面能够在本地和 OBS 环境中正常运行。验收时重点检查了以下内容:- 页面能正常加载- 仪表盘数据能展示- 表单内容能保存到 localStorage- 提示词能够生成- 案例文档能够生成- OBS 演示链接能够公网访问- 案例中心附件 PDF 小于 20MB十、遇到的问题与解决方式第一个问题是提交材料分散。训练营要求包含代码、部署、案例文档、演示链接等内容,开始时容易只关注开发本身,忽略最终提交材料。因此我把这些要求做成了应用中的评分证据和部署清单。第二个问题是静态部署的公开访问配置。OBS 上传文件后,如果没有正确配置静态网站托管和公开读策略,外部访问会失败。最后通过设置默认首页 `index.html` 和桶策略 `campflow-public-read` 解决。第三个问题是论坛和案例中心链接类型不同。案例中心链接用于案例提交,OBS 链接用于作品演示,而论坛链接用于社区帖子展示。理解这三类链接的区别后,最终提交路径就清晰了。## 十一、项目总结CampFlow 是一次围绕训练营真实提交场景设计的小型工具实践。它不只是一个静态页面,而是把 CodeArts Agent 使用过程、评分标准、部署清单和案例文档整理流程放在同一个工作台里。通过这个项目,我完成了从需求分析、智能体辅助开发、本地测试、OBS 部署、案例中心提交到论坛发布材料整理的完整闭环。后续如果继续扩展,可以加入截图上传、多人协作、华为云登录和一键生成提交材料等能力,让它从个人训练营工具升级为更通用的项目交付助手。以上就是我的训练营项目实践分享,欢迎大家交流指正。
  • [案例共创] 影院管理系统(Cinema Management System)开发案例
     案例介绍本案例采用华为云码道(CodeArts)代码智能体作为核心开发工具,结合SDD(Specification-Driven Development)规范驱动开发方法论,构建从需求分析、系统设计、页面原型到SDD文档体系的完整开发流程。实现影院管理系统从需求到设计的全流程智能化场景,让影院运营告别手动管理,迈入数字化新时代。一、概述1.1 案例介绍在中小型影院日常运营中,票务管理往往成为"隐形痛点":排片信息依赖手动更新导致观众跑空,座位销售情况不透明无法实时掌握空座率,售票数据分散无法进行精准的票房统计,观众购票体验差无法在线选座。影院运营人员疲于奔命,管理者对经营状况一知半解,影院因信息不透明和运营效率低下造成的收入损失难以估量。在数字化转型浪潮下,如何让影院的票务数据"活"起来,成为影院管理升级的关键一环。本案例旨在通过华为云码道(CodeArts)代码智能体驱动的SDD开发方法论,为影院管理系统注入"数字智慧",实现影院运营的全流程数字化、智能化管理。本案例采用华为云码道(CodeArts)代码智能体作为核心开发工具,结合dev-process-framework、page-mockup、function-detail、fullstack-testing等专业化skills,构建从需求分析、系统设计、页面原型到SDD文档体系的完整开发流程。该方案将传统开发中分散的需求文档、设计文档、任务清单整合为规范化的SDD体系,使决策过程可追溯、文档与代码同步更新,大幅提升开发效率和项目可维护性。通过华为云码道的智能体模式,开发者无需深厚架构背景即可快速完成复杂企业级应用的设计与规划。案例技术选型:华为云码道(CodeArts)代码智能体:集代码大模型、AI IDE、Code Agent为一体的智能编码产品。具备强大的需求理解、架构设计和代码生成能力,支持智能体模式自动规划并执行复杂开发任务。本案例中作为核心开发平台,通过对话式交互快速完成影院管理系统的需求分析、架构设计和SDD文档生成。SDD规范驱动开发方法论:系统化的软件开发方法论,包含creating-sdd-directory、managing-spec/design/tasks-document四大核心流程skills。使决策过程可追溯,文档与代码同步更新,形成完整知识库,特别适合多模块复杂功能项目。本案例中作为整体开发方法论指导,确保影院管理系统开发的规范性、可追溯性和可维护性。系统技术选型:  层次技术选型版本说明前端框架Vue.js3.4+组合式API,TypeScript支持前端构建Vite5.x快速构建工具UI组件库Ant Design Vue4.x企业级Vue3组件库状态管理Pinia2.xVue3官方状态管理后端框架Spring Boot3.2.5应用框架ORM框架MyBatis-Plus3.5.6ORM框架数据库MySQL8.0关系型数据库缓存Redis6.x缓存/分布式锁认证授权Spring Security + JWT6.x认证授权容器化Docker-容器化部署1.2 适用对象个人开发者高校学生企业开发者架构设计师1.3 案例时间本案例总时长预计 120分钟。1.4 案例流程  阶段内容说明阶段一环境准备与技能配置安装部署华为云码道(CodeArts)代码智能体,配置项目级Skills阶段二系统设计使用dev-process-framework skill生成系统设计阶段三页面设计使用page-mockup skill生成前端页面设计阶段四测试设计使用fullstack-testing skill生成测试设计文档阶段五SDD文档生成使用function-detail skill整合生成完整SDD文档体系阶段六代码实现基于SDD文档完成前后端代码开发阶段七测试验证执行测试用例并修复缺陷二、环境与资源准备2.1 开发环境部署2.1.1 JDK 17 安装访问 Adoptium 官网,下载 JDK 17 Windows 版本 .msi 安装包配置环境变量:新建系统变量 JAVA_HOME,值为 C:\Program Files\Eclipse Adoptium\jdk-17.xx.xx在 Path 变量中添加 %JAVA_HOME%\bin打开新的CMD窗口,验证安装:cmdjava -version应显示 openjdk version "17.0.x"2.1.2 Maven 安装访问 Maven 官网(cid:link_1),下载 Binary zip 格式文件解压到 C:\Program Files\apache-maven-3.9.x配置环境变量:新建系统变量 MAVEN_HOME,值为 C:\Program Files\apache-maven-3.9.x在 Path 变量中添加 %MAVEN_HOME%\bin验证安装:cmdmvn -version2.1.3 MySQL 8.0 安装与配置访问 MySQL 官网,下载 MySQL 8.0 Windows 安装包双击安装,选择"Developer Default"安装类型安装过程中设置 root 密码(本案例记为 Guoll050329)安装完成后,MySQL 服务自动启动验证安装:cmdmysql --version创建数据库:sqlCREATE DATABASE cinema_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;2.1.4 Redis 安装访问微软维护的 Redis Windows 版本(cid:link_0)下载 .msi 安装包,如 Redis-x64-3.0.504.msi验证安装:cmdredis-cli ping应返回 PONG2.1.5 Node.js 安装访问 Node.js 官网(cid:link_2)下载 LTS 版本(v20.x.x 或 v22.x.x)的 Windows Installer (.msi)"验证安装:cmdnode --versionnpm --version2.2 华为云码道配置2.2.1 项目级技能配置通过前序案例《影院管理系统(一):码道搭台,设计筑基》,已完成码道开发生态skills的集中配置。本案例用到的Skills:  Skill名称用途使用阶段sdd-workflow开发流程管理、进度跟踪全过程bug-fix-reporter自动生成Bug修复报告Bug修复fullstack-testing测试用例编写与执行测试阶段三、对话华为云码道:构建影院管理系统3.1 基础环境准备与项目初始化3.1.1 Windows开发环境部署对话华为云码道(CodeArts)代码智能体: 请执行tasks中的任务 1.1.1:Windows开发环境部署。 任务执行总结:  检查项状态说明JDK 17✅ 已完成环境变量已配置Maven✅ 已完成环境变量已配置MySQL 8.0✅ 已完成服务运行中,cinema_db已创建Redis✅ 已完成服务运行中Node.js✅ 已完成环境变量已配置Sprint报告: ProjectDocs\sprint\第1部分:基础环境准备\001环境部署.md3.1.2 后端项目初始化继续对话华为云码道:请继续完成tasks中的任务 1.1.2:后端项目初始化(Spring Boot工程目录+API骨架+数据模型骨架)。项目信息:项目路径:S:\Cinema Management\demo包结构:com.cinemaSpring Boot版本:3.2.5构建工具:Maven任务执行总结:  任务ID任务描述状态产出物1.1.2后端项目初始化✅ 完成pom.xml、application.yml、实体类、Mapper接口关键成果:Spring Boot 3.2.5 项目结构建立MyBatis-Plus 配置完成6张核心实体类(Film、Hall、Showtime、SeatInstance、Order、User)Mapper接口与Service骨架数据库连接配置Sprint报告: ProjectDocs\sprint\第1部分:基础环境准备\002后端项目初始化.md3.1.3 前端项目初始化继续对话华为云码道:请继续完成tasks中的任务 1.1.3:前端项目初始化(Vue3工程目录+API封装骨架+基础组件)。项目信息:项目路径:S:\Cinema Management\demo\cinema-frontend技术栈:Vue 3 + Vite + Pinia + Vue Router + Ant Design Vue任务执行总结:  任务ID任务描述状态产出物1.1.3前端项目初始化✅ 完成package.json、vite.config.js、路由配置、Store骨架关键成果:Vue 3 + Vite 项目结构建立Pinia状态管理配置Vue Router路由配置Axios封装(request.js)基础布局组件(Navbar、Footer)Sprint报告: ProjectDocs\sprint\第1部分:基础环境准备\003前端项目初始化.md基础环境准备阶段总结:任务编号任务描述状态1.1.1Windows开发环境部署✅ 完成1.1.2后端项目初始化✅ 完成1.1.3前端项目初始化✅ 完成3.2 核心业务模块开发对话华为云码道:# 影院管理系统(Cinema Management System)## 项目概述开发一个影院管理系统的后端服务,采用Spring Boot 3.x + MyBatis-Plus + MySQL + Redis技术栈,提供RESTful API接口供前端调用。系统面向两类用户:普通观众(购票)和影院管理员(后台管理)。整体采用模块化单体架构,按业务领域划分包结构。## 技术约束- 开发语言:Java 17- 框架:Spring Boot 3.x,使用Spring Security + JWT做认证授权- ORM:MyBatis-Plus 3.5.x- 数据库:MySQL 8.0,使用InnoDB引擎,字符集utf8mb4- 缓存:Redis 6.x(用于存储座位状态、热点数据、分布式锁)- 工具库:Lombok、Hutool、Jackson- API风格:RESTful,统一返回格式为 { code, message, data }- 包结构:按模块划分,每个模块包含 controller, service, mapper, entity, dto, vo, enums## 数据库核心表结构- films:影片表,字段包括id, title, genre, duration, director, cast, description, poster_url, release_date, status(枚举:COMING_SOON/NOW_SHOWING/EXPIRED),created_at, updated_at- halls:影厅表,字段包括id, name, type(枚举:STANDARD/IMAX/3D),total_rows, total_columns, total_seats, created_at, updated_at- showtimes:场次表,字段包括id, film_id, hall_id, start_time, end_time, price, created_at, updated_at- seat_instances:座位实例表(按场次生成),字段包括id, showtime_id, hall_id, row_num, col_num, seat_number, status(枚举:AVAILABLE/LOCKED/SOLD/MAINTENANCE),lock_time, lock_expire_at, order_id, version(乐观锁), created_at, updated_at- orders:订单表,字段包括id, order_no(业务唯一编号), user_id, showtime_id, seat_ids(JSON数组), total_price, status(枚举:PENDING/PAID/CANCELLED/REFUNDED/EXPIRED), pay_time, expire_time, created_at, updated_at- users:用户表,字段包括id, username, password(BCrypt加密), phone, email, role(枚举:ADMIN/USER), created_at, updated_at## 模块划分与功能描述### 模块一:影片管理模块(Film Module)包路径:com.cinema.film功能描述:- 管理影片的增删改查操作- 支持分页查询影片列表,可按状态(上映中/即将上映/已下架)、类型、关键词搜索- 提供"即将下架"预警功能:查询距离下架日期(release_date + 默认上映周期30天)不足3天的上映中影片- 管理员可新增、修改、下架影片- 普通用户可查看影片详情和列表- Controller:FilmController,提供 /api/films 前缀的接口### 模块二:影厅与排片管理模块(Hall & Showtime Module)包路径:com.cinema.hall, com.cinema.showtime功能描述:- 影厅管理:增删改查影厅信息,包括座位布局(行数、列数)- 排片管理:为影片安排放映场次,需校验同一影厅同一时间段无冲突- 排片时自动生成该场次的座位实例(状态均为AVAILABLE),总座位数=影厅总座位数- 提供按影片ID查询排片列表、按日期查询排片列表- 管理员专属操作,普通用户只读- Controller:HallController(/api/halls),ShowtimeController(/api/showtimes)### 模块三:座位与库存管理模块(Seat Module)包路径:com.cinema.seat功能描述:- 提供按场次ID查询座位图,返回包含行、列、座位号、状态(AVAILABLE/LOCKED/SOLD/MAINTENANCE)的列表- 提供统计接口:查询某场次的空座数(状态为AVAILABLE的座位数量)- 座位锁定操作:将指定座位的状态从AVAILABLE改为LOCKED,设置lock_expire_at=当前时间+15分钟,记录order_id(临时)- 座位释放操作:将LOCKED状态的座位恢复为AVAILABLE,清空lock_time、lock_expire_at、order_id- 座位售出操作:将LOCKED状态的座位改为SOLD,关联实际订单ID- 所有座位状态变更必须基于乐观锁(version字段)防止并发冲突,并配合Redis缓存提升性能- 缓存设计:Redis中存储 key="seat:showtime:{showtimeId}", value为Hash结构,field为"row-col",value为状态枚举值,用于快速查询和更新- Controller:SeatController,提供 /api/seats 前缀的接口### 模块四:售票与选座模块(Ticketing Module)包路径:com.cinema.ticketing功能描述:- 自动选座:接收参数(场次ID,购票数量),自动分配连续且居中位置的AVAILABLE座位,一次性锁定所有分配座位,返回座位列表- 手工选座:接收参数(场次ID,座位ID列表),校验所有座位均为AVAILABLE状态,一次性锁定,返回锁定结果- 锁定座位时需使用Redis分布式锁(key="lock:showtime:{showtimeId}")防止同一场次并发锁定冲突- 锁定成功后创建待支付订单,订单超时时间为15分钟- 提供选座预览(不实际锁定):模拟自动选座返回推荐座位但不改变状态- Controller:TicketingController,提供 /api/ticketing 前缀的接口### 模块五:订单管理模块(Order Module)包路径:com.cinema.order功能描述:- 创建订单:生成全局唯一订单号(格式:ORD+yyyyMMddHHmmss+6位随机数),记录用户ID、场次ID、座位ID列表、总价、状态为PENDING,设置expire_time=当前时间+15分钟- 支付订单:将PENDING状态改为PAID,触发座位状态变更为SOLD,记录支付时间- 取消订单:用户主动取消,将PAYING状态改为CANCELLED,释放锁定座位- 订单超时自动处理:定时任务扫描expire_time < 当前时间且状态为PENDING的订单,改为EXPIRED,释放座位- 查询订单:用户可查询自己的订单列表和详情,管理员可查询所有订单- Controller:OrderController,提供 /api/orders 前缀的接口### 模块六:用户与会员管理模块(User Module)包路径:com.cinema.user功能描述:- 用户注册:用户名唯一,密码使用BCrypt加密存储- 用户登录:验证用户名密码,签发JWT Token(有效期24小时)- 获取当前用户信息:通过JWT解析用户身份- 区分角色:ADMIN可访问管理接口,USER仅可访问用户接口- Controller:AuthController(/api/auth),UserController(/api/users)### 模块七:权限与安全模块(Security Module)包路径:com.cinema.security功能描述:- 基于Spring Security + JWT实现认证和授权- 配置SecurityFilterChain,定义白名单路径(/api/auth/login, /api/auth/register)- 除白名单外所有接口需携带JWT Token(Header: Authorization: Bearer <token>)- 角色拦截:管理员接口(/api/admin/**)仅允许ROLE_ADMIN访问- 提供全局异常处理,返回统一格式错误响应### 模块八:通知与监控模块(Notification Module)包路径:com.cinema.notification功能描述:- 支付成功后发送通知(现阶段实现为日志记录,预留接口便于后续接入短信/邮件)- 订单超时释放座位的日志记录- 系统异常告警日志记录## 关键业务流程(必须实现)### 购票完整流程(核心)1. 用户登录,获取JWT Token2. 浏览影片列表 -> 选择影片 -> 查看排片列表 -> 选择场次3. 查看座位图 -> 选择自动选座或手工选座4. 调用选座接口,锁定座位,创建待支付订单,返回订单信息5. 用户调用支付接口(模拟支付:传入订单ID即可),将订单状态改为PAID6. 支付成功后,座位状态变为SOLD,触发通知7. 若15分钟内未支付,定时任务自动取消订单并释放座位### 超时释放任务- 使用@Scheduled注解,每30秒执行一次- 查询所有状态为PENDING且expire_time < NOW()的订单- 遍历订单,将订单状态改为EXPIRED,调用座位释放逻辑## API接口命名规范- GET /api/films - 分页查询影片- GET /api/films/{id} - 查询影片详情- POST /api/admin/films - 新增影片(管理员)- PUT /api/admin/films/{id} - 修改影片(管理员)- DELETE /api/admin/films/{id} - 下架影片(管理员)- GET /api/showtimes/film/{filmId} - 查询影片的排片- GET /api/seats/showtime/{showtimeId} - 查询场次座位图- GET /api/seats/available-count/{showtimeId} - 查询空座数- POST /api/ticketing/auto - 自动选座(参数:showtimeId, quantity)- POST /api/ticketing/manual - 手工选座(参数:showtimeId, seatIds)- POST /api/orders - 创建订单(通常由选座接口内部调用)- PUT /api/orders/pay/{orderId} - 支付订单- PUT /api/orders/cancel/{orderId} - 取消订单- GET /api/orders/user - 查询当前用户订单- POST /api/auth/login - 登录- POST /api/auth/register - 注册## 非功能性要求- 所有接口响应时间控制在200ms以内(不含网络延迟)- 座位锁定必须保证原子性,防止超卖- 代码需包含完善的注释和日志记录- 遵循RESTful设计规范- 数据库设计需建立合理索引:orders表需对user_id、showtime_id、status建立索引;seat_instances表需对showtime_id、status建立联合索引3.3 测试与优化3.3.1 测试设计优化对话华为云码道:请深度检索项目,并使用fullstack-testing skill根据当前项目状态,优化和完善SDD中的测试设计以及tasks中的测试任务。注: 建议该指令执行三次,以避免问题遗漏。3.3.2 后端单元测试编写对话华为云码道:请使用fullstack-testing skill继续完成tasks中的任务 3.2.1:后端单元测试编写。任务执行总结:  测试文件用例数模块状态test_auth_service.py18认证服务✅ 完成test_film_service.py14影片服务✅ 完成test_showtime_service.py12排片服务✅ 完成test_seat_service.py17座位服务✅ 完成test_order_service.py16订单服务✅ 完成合计77  3.3.3 前端单元测试编写对话华为云码道:请使用fullstack-testing skill继续完成tasks中的任务 3.2.2:前端单元测试编写。任务执行总结:  测试文件用例数模块状态auth.test.ts7认证Store✅ 完成film.test.ts8影片Store✅ 完成seat.test.ts10座位Store✅ 完成order.test.ts8订单Store✅ 完成format.test.ts15格式化工具✅ 完成合计48  3.3.4 API集成测试编写对话华为云码道:请使用fullstack-testing skill继续完成tasks中的任务 3.2.3:API集成测试编写。3.3.5 E2E测试编写对话华为云码道:请使用fullstack-testing skill继续完成tasks中的任务 3.2.4:E2E测试编写。3.3.6 本地测试执行与Bug修复对话华为云码道:请继续完成tasks中的任务 3.3.1:本地Windows环境测试执行与Bug修复。(插图位置14:测试执Bug修复总结:  Bug编号模块问题描述状态BUG-001选座模块自动选座并发超卖✅ 已修复BUG-002订单模块超时释放任务未执行✅ 已修复BUG-003前端座位图状态刷新延迟✅ 已修复测试执行结果:后端单元测试:77/77 通过 ✅前端单元测试:48/48 通过 ✅API集成测试:50/50 通过 ✅E2E测试:15/15 通过 ✅3.4 项目启动与验证3.4.1 启动后端服务在项目根目录 S:\Cinema Management\demo 执行:cmdmvn spring-boot:run成功启动标志:Started CinemaManagementApplication in 5.123 seconds3.4.2 启动前端服务在前端项目目录 S:\Cinema Management\demo\cinema-frontend 执行:cmdnpm run dev成功启动标志:Local: http://localhost:5173/四、案例总结4.1 案例阶段性总结本案例在本地 Windows PC 上搭建开发测试环境,以华为云 CodeArts 代码智能体为核心开发工具,结合 sdd-workflow、bug-fix-reporter、fullstack-testing 等技能,基于影院管理系统 SDD 设计文档,成功完成了影院管理系统的开发、测试与运行验证。实现了影院管理系统从"设计蓝图"到"可运行系统"的完整落地,让影院运营管理真正从"纸上谈兵"走向"实战落地"。4.2 交付成果清单  类别交付物数量后端代码Java源文件、配置文件70+前端代码Vue组件、Store、API封装50+测试代码单元测试、集成测试、E2E测试150+文档Sprint报告、Bug修复报告10+4.3 技术成果总结后端架构:Spring Boot 3.2.5 + MyBatis-Plus 3.5.66张核心数据表30+ RESTful API端点JWT认证 + 权限控制Redis缓存 + 分布式锁前端架构:Vue 3 + Vite + Pinia + Vue RouterAnt Design Vue UI组件库12个核心页面5个Pinia Store本案例中所涉及的影院管理系统的相关需求、设计等文档已上传至gitCode,请根据需要进行下载:项目源码地址: https://github.com/ajswjindjiw/Cinema-Management
  • [案例共创] CampFlow 训练营成果管理助手:CodeArts Agent 项目实践
    大家好,我本次训练营完成的项目是 **CampFlow 训练营成果管理助手**。这是一个面向华为云码道暑期实习训练营的 Web 工作台,用来帮助学生把在线学习、CodeArts Agent 辅助开发、项目构建、华为云部署、案例文档发布和最终作品链接提交这些分散任务整合到一个可追踪的流程中。## 一、项目背景在训练营实践过程中,我发现项目提交并不只是写出一个能运行的页面,还需要同时完成学习进度、CodeArts Agent 使用过程、项目部署、案例文档、附件材料和最终链接提交。任务节点比较分散,如果只靠临时记录,很容易出现材料遗漏、演示链接忘记回填、评分维度没有证据支撑等问题。因此我设计了 CampFlow,希望它既是一个可运行的训练营成果管理应用,也能反向辅助我整理最终提交材料。它把指导书中的评分维度和提交要求产品化,形成项目档案、里程碑、评分证据、智能体提示词和案例文档生成几个模块,让整个实习项目从开发到提交都更加清晰。## 二、作品链接作品演示地址:https://campflow-tj-20260726.obs-website.cn-east-3.myhuaweicloud.com/案例中心链接:https://devstation.connect.huaweicloud.com/space/devportal/casecenter/11272f6a85244aad99d738b32bac093c/2## 三、技术选型本项目采用纯静态前端方案,核心技术如下:- CodeArts Agent:辅助需求分析、架构设计、功能开发、测试部署和案例文档整理- HTML / CSS / JavaScript:实现页面结构、交互逻辑和响应式布局- localStorage:在浏览器本地保存项目档案、里程碑、评分证据和开发记录- 华为云 OBS:托管静态网站并提供公网访问地址- Node.js assert:对得分计算、数据模型和文档生成逻辑进行基础测试选择纯静态方案的原因是训练营项目更看重完整交付闭环。静态 Web 应用部署简单、依赖少、访问稳定,适合快速完成从代码构建到华为云部署的全过程。## 四、需求分析根据训练营指导书,项目成果需要包含可运行应用、案例文档、演示链接或视频,并且 Web 类应用需要部署到华为云。评分维度包括创新易用、功能完备、技术能力、文档完整性和参与度。围绕这些要求,我将需求拆成五个方向:1. 成果管理:记录项目名称、应用方向、技术栈、演示链接和案例链接。2. 过程管理:把学习、开发、部署、发布等训练营节点转化为里程碑。3. 智能体协作:自动生成适合 CodeArts Agent 的阶段性提示词。4. 评分证据:按评分维度整理证据项、完成状态、说明和链接。5. 文档发布:根据项目档案和证据清单生成案例 Markdown 草稿。这样做的好处是,项目不只是“做完一个页面”,而是把提交要求转化成了可以持续检查的工作流。## 五、系统架构设计CampFlow 的整体结构比较轻量:```text用户浏览器  ├─ index.html:应用入口  ├─ assets/styles.css:界面样式和响应式布局  ├─ assets/app.js:状态管理、评分计算、提示词生成和 Markdown 生成  └─ localStorage:保存项目档案、评分证据、里程碑和开发记录```项目没有引入后端服务,所有数据保存在浏览器本地。对于训练营展示场景来说,这种方案可以降低部署复杂度,也避免后端账号、数据库和接口联调带来的额外成本。## 六、CodeArts Agent 辅助开发过程本项目按照 vibe coding 的思路使用 CodeArts Agent。我的实践方式不是一次性让智能体直接生成全部代码,而是分阶段推进:第一阶段是需求分析。我先让 CodeArts Agent 根据指导书梳理项目目标、评分维度和必须交付的材料,避免项目范围跑偏。第二阶段是架构设计。我让智能体输出静态前端架构、本地存储数据模型,以及每个页面模块需要承担的职责。第三阶段是功能开发。根据模块拆分,逐步实现仪表盘、项目档案、提示词生成、评分证据、案例文档生成和部署清单。第四阶段是调试验证。重点检查评分计算是否符合权重、localStorage 是否能正常保存、Markdown 文档是否能完整生成、部署文件是否齐全。第五阶段是文档整理。最后让智能体辅助把开发过程、部署步骤、项目亮点和总结整理成案例中心与论坛都能使用的材料。通过这种分阶段协作,CodeArts Agent 更像一个项目搭档,而不是简单的代码生成器。它帮助我把任务拆小,也让我在每一步都有可检查的输出。## 七、核心功能介绍1. 仪表盘:展示预计得分、证据完成数量、里程碑完成数量,并按指导书权重拆解评分维度。2. 项目档案:维护项目名称、应用方向、真实问题、解决方案、技术栈、考试状态、演示链接和案例链接。3. CodeArts 提示词生成:根据项目当前状态生成需求分析、架构设计、功能开发、调试部署和案例文档五类提示词,方便继续与智能体协作。4. 评分证据清单:按照创新易用、功能完备、技术能力、文档完整性和参与度五个维度组织证据项,支持完成状态和说明记录。5. 案例文档生成器:把项目档案、评分证据和开发记录拼装成 Markdown 草稿,减少最终发布时遗漏章节的风险。6. 部署清单:列出静态网站部署需要上传的文件,并提醒检查 OBS 桶、静态网站首页、公开访问策略和演示链接。## 八、部署过程项目最终部署在华为云 OBS 静态网站托管上。部署步骤如下:1. 创建 OBS 桶 `campflow-tj-20260726`,区域选择华东-上海一 `cn-east-3`。2. 上传静态网站文件,根目录包含 `index.html`,`assets` 目录包含 `app.js` 和 `styles.css`。3. 开启静态网站托管,默认首页设置为 `index.html`。4. 创建桶策略 `campflow-public-read`,允许公网读取静态网站对象。5. 访问 OBS 静态网站地址,确认页面可以正常打开。验证结果:- 首页 HTTP 状态为 200- 页面内容包含 CampFlow- 静态网站公网地址可访问## 九、测试与验收项目包含基础逻辑测试,主要验证数据模型、评分计算和文档生成相关逻辑。测试通过后,我又进行了浏览器访问检查,确认页面能够在本地和 OBS 环境中正常运行。验收时重点检查了以下内容:- 页面能正常加载- 仪表盘数据能展示- 表单内容能保存到 localStorage- 提示词能够生成- 案例文档能够生成- OBS 演示链接能够公网访问- 案例中心附件 PDF 小于 20MB## 十、遇到的问题与解决方式第一个问题是提交材料分散。训练营要求包含代码、部署、案例文档、演示链接等内容,开始时容易只关注开发本身,忽略最终提交材料。因此我把这些要求做成了应用中的评分证据和部署清单。第二个问题是静态部署的公开访问配置。OBS 上传文件后,如果没有正确配置静态网站托管和公开读策略,外部访问会失败。最后通过设置默认首页 `index.html` 和桶策略 `campflow-public-read` 解决。第三个问题是论坛和案例中心链接类型不同。案例中心链接用于案例提交,OBS 链接用于作品演示,而论坛链接用于社区帖子展示。理解这三类链接的区别后,最终提交路径就清晰了。## 十一、项目总结CampFlow 是一次围绕训练营真实提交场景设计的小型工具实践。它不只是一个静态页面,而是把 CodeArts Agent 使用过程、评分标准、部署清单和案例文档整理流程放在同一个工作台里。通过这个项目,我完成了从需求分析、智能体辅助开发、本地测试、OBS 部署、案例中心提交到论坛发布材料整理的完整闭环。后续如果继续扩展,可以加入截图上传、多人协作、华为云登录和一键生成提交材料等能力,让它从个人训练营工具升级为更通用的项目交付助手。以上就是我的训练营项目实践分享,欢迎大家交流指正。
  • [案例共创] 用华为云码道构建 SoundScape:让音乐在浏览器中“看得见”
    用华为云码道构建 SoundScape:让音乐在浏览器中“看得见”一、概述1.1 案例介绍传统播放器只负责“播放声音”,SoundScape 则把声音变成实时画面:用户在浏览器中上传本地音频后,应用通过 Web Audio API 解码音频并持续采集频谱数据,再使用 Canvas 2D 绘制柱状、环形和球形三种可视化效果。低频能量、节拍变化和全频强度还会驱动粒子大小、速度、色相与画面节奏,实现音画同步的沉浸式体验。本案例以华为云码道(CodeArts)代码智能体为辅助开发工具,围绕“需求梳理、架构设计、核心引擎实现、页面联调、运行验收和生产构建”完成一个 Vue 3 单页应用。项目全部在浏览器端处理音频,不需要后端服务,也不会主动上传用户的音频文件。案例技术选型如下:华为云码道(CodeArts)代码智能体:辅助分析需求、理解代码、生成实现建议和排查问题。Vue 3 + Vite:构建组件化前端应用并提供开发、构建与预览能力。Web Audio API:完成音频解码、播放控制和实时频谱采集。Canvas 2D:完成频谱、拟 3D 球形点阵、粒子和氛围光效绘制。1.2 适用对象个人开发者高校学生Web 前端开发者对音频处理、数据可视化或创意编程感兴趣的开发者1.3 案例时间本案例总时长预计 90 分钟,不包含首次下载安装开发工具和依赖包所需时间。1.4 案例流程说明:使用码道梳理上传、播放、可视化、主题切换和截图等需求,形成验收标准;准备 Node.js 环境,安装项目锁定的 Vue 3 和 Vite 依赖;使用 AudioEngine 完成音频解码、播放状态管理和频谱数据采集;分析低频、中频、高频和整体强度,驱动频谱、粒子、色彩和节拍效果;联调文件上传、播放控制、模式切换、主题切换、拖拽旋转和截图下载;执行生产构建,检查 dist/ 静态文件并完成案例验收。1.5 资源总览本案例核心功能均使用浏览器原生能力,预计花费 0 元。如将构建产物部署到收费的云资源,请以控制台实时价格为准,并在体验结束后及时释放资源。资源名称规格或版本费用华为开发者空间云开发环境建议 2 vCPUs、4 GiB 及以上,Node.js 20.19+ 或 22.12+以页面展示为准华为云码道(CodeArts)代码智能体体验版或当前账号可用版本以页面展示为准Vue项目锁定版本 3.5.40免费Vite项目锁定版本 8.1.5免费Web Audio API、Canvas 2DChrome、Edge 等现代浏览器的原生能力免费二、环境和资源准备2.1 准备华为云码道开发环境登录华为开发者空间,按账号可用方式进入码道代码智能体或创建云开发环境。也可以在 Windows 本地安装码道 AI IDE 后打开项目目录 music-visualizer。在终端中检查 Node.js 和 npm:node --version npm --version Vite 8 要求使用 Node.js 20.19+ 或 22.12+。如果版本过低,请先切换到符合要求的 Node.js 版本,再安装依赖。2.2 安装项目依赖进入项目目录:cd music-visualizer npm cinpm ci 会严格按照 package-lock.json 安装依赖。安装完成后,可以执行以下命令核对顶层版本:npm list --depth=0 本案例验证时使用的顶层依赖如下:@vitejs/plugin-vue@6.0.8 vite@8.1.5 vue@3.5.402.3 使用码道上下文选择功能当希望码道只分析某个文件或目录时,可先在 AI IDE 中打开目标内容,再在对话框输入 #,选择 File 或 Folder 作为上下文。这样可以缩小分析范围,减少无关内容干扰。例如,选择 #Folder src/engine 后输入:请分析 #Folder src/engine 中音频数据到画面渲染的数据链路,重点检查: 1. AudioBufferSourceNode 的生命周期; 2. AnalyserNode 的参数是否合理; 3. requestAnimationFrame 渲染循环是否正确释放; 4. 节拍检测器和粒子池是否完整初始化。 请先给出问题清单,再给出最小修改建议,不要改动无关文件。本项目不依赖额外的项目级 skills 即可完成案例。如果团队已经配置研发流程或测试类 skills,也可以在需求拆分、代码审查和测试设计阶段按需启用。三、对话码道:构建 SoundScape 在线音乐可视化器3.1 明确项目需求与验收标准在编码前,先向码道描述产品目标和约束:请为一个浏览器端在线音乐可视化器整理需求和验收标准。 约束:Vue 3 + Vite;音频只在本地浏览器处理;不使用后端;使用Web Audio API采集频谱;使用Canvas 2D绘图。 功能:点击或拖拽上传音频、播放/暂停、进度跳转、三种可视化模式、四种主题、粒子效果、画面截图。 请按“功能需求、非功能需求、验收用例”输出,并标出浏览器兼容性和资源释放风险。根据当前项目实现,核心需求如下:编号需求验收标准FR-01本地音频加载可点击或拖拽选择浏览器支持解码的音频文件;界面显示歌曲文件名FR-02播放控制可播放、暂停,并显示当前时间和总时长FR-03进度跳转点击进度条后从目标位置继续播放FR-04多模式可视化可在柱状、环形和球形三种模式间切换FR-05多主题显示可在霓虹、星空、火焰和极光四种主题间切换FR-06音频驱动画面低、中、高频和整体强度能够影响色相、粒子大小、速度和画面尺度FR-07交互旋转加载音频后,可使用鼠标或触摸拖动画面,改变球形效果观察角度FR-08截图保存点击截图按钮后,可将当前 Canvas 保存为 PNG 文件NFR-01隐私性音频在浏览器本地读取和解码,不主动上传到服务器NFR-02资源管理组件卸载时停止动画、断开音频节点并关闭 AudioContext3.2 设计系统架构和数据流SoundScape 采用纯前端分层结构。Vue 组件负责用户交互与状态展示,音频引擎负责音频生命周期,渲染引擎负责逐帧绘图,特征分析与粒子系统作为独立模块被渲染引擎调用。本地音频文件 │ File.arrayBuffer() ▼ AudioContext.decodeAudioData() │ ▼ AudioBufferSourceNode ──▶ AnalyserNode ──▶ 浏览器扬声器 │ 每帧采集1024个频谱值 ▼ 低频 / 中频 / 高频 / 整体强度 │ │ ▼ ▼ 柱状 / 环形 / 球形 节拍与粒子系统 └─────────┬─────────┘ ▼ Canvas 2D数据链路全部位于用户浏览器内部。除非后续自行增加上传接口,否则音频内容不会进入云端。3.3 了解项目结构当前项目的主要目录如下:music-visualizer/ ├── public/ │ ├── favicon.svg │ └── icons.svg ├── src/ │ ├── assets/ │ │ ├── hero.png │ │ └── vite.svg │ ├── components/ │ │ ├── Player.vue │ │ ├── Toolbar.vue │ │ ├── Upload.vue │ │ └── Visualizer.vue │ ├── engine/ │ │ ├── audioEngine.js │ │ ├── particleSystem.js │ │ ├── renderEngine.js │ │ └── themeManager.js │ ├── App.vue │ └── main.js ├── index.html ├── package-lock.json ├── package.json └── vite.config.js文件职责src/App.vue组合主界面,维护播放状态、进度、当前模式和主题,处理文件选择与截图src/components/Visualizer.vue挂载 Canvas,创建和释放渲染引擎,转发模式、主题及拖拽事件src/engine/audioEngine.js解码音频,创建播放源,管理播放、暂停、跳转和频谱数据src/engine/themeManager.js定义四套主题,提取频段特征,检测节拍并估算 BPM 与情绪标签src/engine/particleSystem.js使用固定对象池生成、更新和绘制粒子src/engine/renderEngine.js驱动动画循环,绘制柱状、环形、球形、粒子和背景光效当前运行入口把上传、播放控制和工具栏直接组合在 App.vue 中,只实际挂载 Visualizer.vue。Player.vue、Toolbar.vue 和 Upload.vue 是预留的拆分组件,当前版本未被 App.vue 导入。理解这一点可以避免修改了未挂载组件却看不到页面变化。3.4 实现音频加载和频谱采集AudioEngine.init() 在第一次加载文件时创建 AudioContext 和 AnalyserNode:init() { if (this.ctx) return const AC = window.AudioContext || window.webkitAudioContext if (!AC) return this.ctx = new AC() this.analyser = this.ctx.createAnalyser() this.analyser.fftSize = 2048 this.analyser.smoothingTimeConstant = 0.8 this.analyser.minDecibels = -90 this.analyser.maxDecibels = -10 this.analyser.connect(this.ctx.destination) this.frequencyData = new Uint8Array(this.analyser.frequencyBinCount) this.timeDomainData = new Uint8Array(this.analyser.frequencyBinCount) } 当 fftSize 为 2048 时,frequencyBinCount 为 1024。smoothingTimeConstant 设置为 0.8,可以减少相邻帧之间的突变,使画面更连贯。加载文件时,应用通过 File.arrayBuffer() 读取本地数据,再调用 decodeAudioData() 得到 AudioBuffer:async load(file) { this.init() if (!this.ctx) throw new Error('Web Audio API not supported') this._stopSource() try { const arrayBuffer = await file.arrayBuffer() this.buffer = await this.ctx.decodeAudioData(arrayBuffer) this.pauseOffset = 0 this.playing = false } catch (e) { this.buffer = null this.playing = false throw e } } AudioBufferSourceNode 只能启动一次,因此播放、跳转或重新播放时必须新建播放源。项目使用 pauseOffset 保存暂停位置,并使用 _generation 避免旧播放源的 onended 回调覆盖新播放状态。3.5 将音频特征映射为视觉参数themeManager.js 将频谱数组划分为低频、中频和高频,并计算整体强度指标:const bassEnd = Math.max(1, Math.floor(len * 0.1)) const midEnd = Math.max(bassEnd + 1, Math.floor(len * 0.5)) const bassAvg = bassSum / bassEnd / 255 const midAvg = midSum / (midEnd - bassEnd) / 255 const highAvg = highSum / (len - midEnd) / 255 const rms = Math.sqrt(totalSum / len) / 255 特征与画面参数的映射关系如下:音频特征视觉反馈低频平均值 bassAvg增大粒子尺寸;超过阈值时参与节拍判定并触发粒子爆发中频平均值 midAvg在当前主题基础上偏移色相高频平均值 highAvg提高粒子速度并改变色相整体强度 rms调整粒子数量、透明度、饱和度和环境光尺度四套主题只提供基础色彩和背景,实时音频特征会继续改变视觉参数:主题键显示名称基础风格neon霓虹品红与青色,深蓝黑背景starry星空蓝色系,深空背景flame火焰橙红色系,深棕黑背景aurora极光绿色与蓝色,深绿背景3.6 构建三种可视化模式与粒子系统RenderEngine 使用 requestAnimationFrame() 驱动绘制。每一帧先采集频谱,再进行特征分析、背景绘制、主模式绘制和粒子更新:loop() { if (!this.running) return this.render() this.animId = requestAnimationFrame(() => this.loop()) } render() { const freq = this.audio.getFrequencyData() if (!freq || !this.audio.buffer) { this.drawIdleBackground(this.w, this.h) return } const features = analyzeFeatures(freq) const params = adaptVisualParams(features, this.theme) if (this.mode === 'bars') this.drawBars(freq, params, features) else if (this.mode === 'circular') this.drawCircular(freq, params, features) else this.drawSphere(freq, params, features) } 三种模式的实现重点如下:模式实现方式主要参数柱状 bars对数映射频谱区间,绘制上下对称的渐变柱和倒影64 根频谱柱、逐帧平滑环形 circular将频谱值映射到圆周,向内外绘制径向线段120 根径向频谱线球形 sphere使用黄金角在单位球面生成点阵,经旋转与深度计算投影到二维 Canvas900 个球面点、邻近点连线粒子系统预先创建 600 个 Particle 对象,并循环复用对象池,避免在高频动画循环中持续创建和回收对象。检测到明显低频节拍时,渲染引擎会在画面中心额外发射一组高速粒子。3.6.1 运行前代码核对当前源码中的 RenderEngine 已导入 BPMDetector,并在渲染阶段调用 this.bpmDetector.update(),但构造函数尚未创建其实例。播放音频前,需要在 src/engine/renderEngine.js 的构造函数中补充以下一行:constructor(canvas, audioEngine) { this.canvas = canvas this.ctx = canvas.getContext('2d') this.audio = audioEngine this.particles = new ParticleSystem(600) this.bpmDetector = new BPMDetector() // 其余初始化代码保持不变 } 如果未补充该行,音频加载后执行到 this.bpmDetector.update(beat) 时会出现 TypeError,动画循环随即中断。可将当前文件作为上下文交给码道,让代码智能体检查“导入、实例化、使用、释放”是否形成完整闭环。3.7 完成页面交互和截图下载主界面由全屏 Canvas 和底部控制栏组成:未加载音频时,页面中央显示点击或拖拽上传区域;加载成功后,顶部显示去除扩展名的歌曲名;底部控制栏显示播放/暂停、当前时间、总时长和进度条;右侧提供三种模式、四种主题和截图按钮;鼠标或触摸拖动 Canvas 时,渲染引擎更新旋转角度;点击截图按钮后,调用 canvas.toDataURL('image/png') 下载当前画面。截图功能的核心代码如下:function screenshot() { const canvas = visualizer.value?.getCanvas() if (!canvas) return const link = document.createElement('a') link.download = `soundscape-${Date.now()}.png` link.href = canvas.toDataURL('image/png') link.click() } 3.8 运行项目并完成功能验收在项目根目录执行:npm run dev -- --host 0.0.0.0本地开发通常访问终端输出的 http://localhost:5173/。如果使用云开发环境,请通过环境提供的端口转发或预览入口访问 5173 端口,不要直接暴露不必要的公网端口。按以下顺序进行人工验收:步骤操作预期结果1打开页面显示深色背景、上传区域和底部模式/主题按钮2上传浏览器支持的 MP3、WAV 或 OGG 文件上传区域消失,显示歌曲名并自动开始播放3点击播放/暂停按钮播放状态正确切换,时间显示同步变化4点击进度条不同位置播放位置跳转,画面继续响应音频5依次切换柱状、环形和球形三种画面均能持续渲染,无空白或明显卡顿6依次切换四种主题背景和主色调立即变化7在球形模式拖动画面球体观察角度随拖拽变化8点击截图按钮浏览器下载名称形如 soundscape-时间戳.png 的图片9刷新或关闭页面动画停止,音频上下文和节点被释放如果文件无法播放,首先打开浏览器开发者工具查看控制台,并检查:文件是否为浏览器实际支持的编码格式,而不仅是扩展名符合要求;是否已按 3.6.1 节初始化 BPMDetector;浏览器是否允许当前页面创建或恢复 AudioContext;当前 Node.js 版本是否满足 Vite 8 的要求。3.9 执行生产构建功能验收通过后,执行:npm run build构建成功后,Vite 会在 dist/ 目录生成 index.html、CSS、JavaScript 和公共静态资源。当前项目已验证能够通过 Vite 8.1.5 完成生产构建,终端会显示类似以下结果:✓ 19 modules transformed. ✓ built in ...ms使用本地静态服务器预览生产构建:npm run preview -- --host 0.0.0.0四、案例总结与资源释放4.1 案例阶段性总结通过本案例,已经完成一条可复用的浏览器音频可视化链路:使用 Vue 3 组织应用状态、文件上传和交互界面;使用 Web Audio API 解码本地音频,并通过 AnalyserNode 提供实时频谱;使用 Canvas 2D 实现柱状、环形和拟 3D 球形三种效果;使用频段特征和节拍检测驱动色彩、粒子、光效及 BPM 情绪标签;使用对象池和 requestAnimationFrame() 控制实时绘制开销;使用 Vite 完成开发调试、生产构建与静态预览。码道在本案例中承担需求细化、上下文聚焦、代码链路检查和问题定位等辅助工作。开发者仍需以实际源码、浏览器运行结果和验收用例为依据,核对智能体建议后再实施修改。4.2 释放资源本案例默认不创建收费云资源。完成体验后:在运行 Vite 的终端中按 Ctrl+C 停止开发或预览服务器;如果使用华为开发者空间云开发环境,可按实际需要停止或删除不再使用的环境;如果自行部署到对象存储、CDN 或云服务器,请根据对应产品说明释放收费资源;关闭页面时,应用会在组件卸载流程中调用 audio.destroy(),停止播放源并关闭 AudioContext。五、扩展资料说明5.1 可继续完善的功能在 AudioEngine 中增加 GainNode,实现真正的音量调节;为解码失败、超大文件和不支持的编码增加可见错误提示;增加单元测试和端到端测试,覆盖暂停、跳转、源节点重建和截图下载;将复杂特征分析迁移到 Web Worker,或使用 OffscreenCanvas 降低主线程压力;在高性能版本中评估 WebGL,但当前项目实际渲染后端为 Canvas 2D;将 dist/ 部署到静态网站托管,并按需接入 HTTPS 与 CDN;增加移动端布局、键盘无障碍操作和截图分享能力。5.2 扩展学习资料华为开发者空间
  • [训练营] 基于HTML5的单词闯关学习系统
    一、案例介绍1.1 案例流程本次项目为期两周,依托华为开发者空间及华为云码道(CodeArts)代码智能体,完成"单词闯关"Web应用的全流程开发。具体日程安排如下:          1.2  适用对象                        ·高校学生                       ·个人开发者        1.3 资源总览                              本案例预计花费39元(使用训练营代金券)  资源名称规格单价(元) 华为云码道代码智能体基础版39         二、环境和资源准备华为云码道(CodeArts)代码智能体:集代码大模型、AI IDE、Code Agent为一体的智能编码产品。具  备强大的需求理解、架构设计和代码生成能力,支持智能体模式自动规划并执行复杂开发任务。本案例中作为开发平台,通过对话式交互快速完成固定资产管理系统的需求分析、架构设计和SDD文档生成。AI IDE华为云码道开发环境搭建本次实习使用华为开发者空间提供的云开发环境(ARM | 4 vCPUs 8GB | Ubuntu 24.04 Server定制版),配合华为云码道(CodeArts)代码智能体进行辅助开发。CodeArts代码智能体能够根据自然语言描述自动生成代码、定位Bug、解释代码逻辑,显著提升了开发效率。项目采用纯前端技术栈,无需后端服务器,使用HTML5 + CSS3 + JavaScript(ES6)开发,数据存储使用浏览器localStorage,无需数据库依赖。项目通过本地文件系统直接运行,使用浏览器打开index.html即可体验完整功能。CodeArts辅助开发工具在整个开发过程中,华为云码道(CodeArts)代码智能体发挥了关键作用,主要体现在以下几个方面:(1)需求理解与架构设计:通过自然语言描述项目需求("2D背单词闯关游戏,结合搜打撤地图玩法"),CodeArts能够快速理解需求并生成项目目录结构、模块划分建议。例如,它建议将项目拆分为data(数据层)、js(逻辑层)、css(样式层)、assets(资源层)四个核心目录,实践证明这一划分清晰合理。(2)代码生成与迭代:CodeArts根据功能描述直接生成模块代码。以地图生成模块为例,描述"动态生成10×10地图,30%概率生成墙壁,BFS验证起点到终点可达性"后,CodeArts生成了完整的generateMap()函数,包含墙壁随机放置、BFS可达性检测、不可达时逐步移除墙壁等逻辑。后续迭代中,发现直接清空所有墙壁会导致无墙地图的问题,CodeArts准确识别了这一Bug并修复为逐步移除策略。(3)Bug定位与修复:开发过程中遇到多个典型Bug,CodeArts均能快速定位根因。例如:箱子搜索动画期间拖拽物品会重复触发事件监听器导致物品复制,CodeArts识别出setupBoxDrop()被重复调用的问题,引入boxDropInitialized标志防止重复绑定;背包中同ID物品的removeItem()会误删所有实例,CodeArts提出使用唯一实例ID(uid)替代直接存储itemId的方案,从根本上解决了冲突问题。(4)性能优化:当页面出现卡顿时,CodeArts分析出两个性能瓶颈——render()函数在按键长按时每秒触发30-40次全量重绘,以及墙壁查找使用Array.some()导致O(n)复杂度。它给出了requestAnimationFrame帧节流和Set数据结构优化的解决方案,有效改善了页面流畅度。三、项目介绍3.1 项目简介本次开发采用HTML5 Canvas(地图渲染)+ Web前端技术(HTML/CSS/JS)+ 华为云码道CodeArts代码智能体(辅助开发),这是一款将英语词汇学习与2D地图探索游戏相结合的Web应用,采用"搜打撤"(搜索-打击-撤离)玩法,让背单词变得有趣且富有挑战性。以下是核心功能:核心功能说明背单词支持雅思/GRE两套词书,Fisher-Yates算法随机打乱顺序,浏览式学习,完成当日任务后解锁地图游戏地图游戏10×10动态生成地图,BFS保证可达性,WASD操控角色移动,探索保险箱、走向撤离点双保险箱普通保险箱(直接搜索开启)与密码保险箱(需答5道单词题,正确率≥60%方可开启,稀有度更高)搜索动画打开保险箱时物品逐个搜索揭示,灰色底+旋转加载图标,每800ms揭示一个背包系统6×6网格,支持不同尺寸物品(1×1到3×3),拖拽拾取/丢弃/重排,双击自动拾取,碰撞检测收藏品70种收藏品(紫色普通29种/金色稀有30种/红色传说11种),带PNG图片和slots二维数组描述形状仓库展示撤离成功后收藏品持久化到仓库,按稀有度分类展示,未收集的显示"???"统计今日学习数、连续学习天数、总计学习数操作流程1. 在"设置"页选择词书和每日数量2. 在"背单词"页完成当日学习任务3. 进入"地图游戏",WASD移动角色探索地图4. 走到保险箱自动打开搜索界面,拖拽物品到背包拾取5. 走到密码保险箱需答题开锁,获取更高稀有度物品6. 到达EXIT撤离点完成游戏,背包物品存入仓库3.2 系统架构设计本应用采用模块化前端架构,各模块职责清晰、松耦合协作。系统架构如下:├── index.html          // 主页面,含所有模态框HTML结构├── css/│   └── style.css       // 全局样式(网格/拖拽/搜索动画/稀有度配色)├── js/│   ├── data.js         // 数据加载模块(fetch JSON词库与收藏品)│   ├── storage.js      // localStorage持久化管理│   ├── settings.js     // 设置模块(词书选择/每日数量)│   ├── learn.js        // 背单词模块(Fisher-Yates随机/浏览式学习)│   ├── game.js         // 地图游戏核心(地图生成/移动/保险箱/答题)│   ├── backpack.js     // 背包系统(6×6网格/uid实例管理/碰撞检测/拖拽)│   ├── warehouse.js    // 仓库展示模块│   ├── stats.js        // 统计模块│   └── app.js          // 主应用逻辑(页面导航/模块初始化)├── data/│   ├── ielts.json      // 雅思词库(100词)│   ├── gre.json        // GRE词库(100词)│   └── collections.json // 收藏品数据(70项,含slots二维数组)└── assets/             // 70个PNG收藏品图片 + 2个保险箱图片数据流设计:词库数据通过fetch异步加载,收藏品数据包含slots二维数组描述物品在背包网格中的占用形状。游戏状态(地图、背包、保险箱)在单次游戏会话中维护,撤离成功后背包物品通过localStorage持久化到仓库。3.3 核心功能实现本应用"单词闯关"成功将英语词汇学习与2D地图探索游戏相结合,通过"搜打撤"玩法(搜索保险箱→答题开锁→拾取收藏品→安全撤离)将枯燥的背单词过程转化为有趣的游戏体验。核心技术方案包括:(1)模块化前端架构:9个JS模块各司其职,数据层(data.js/storage.js)与逻辑层(learn.js/game.js/backpack.js)分离,便于维护和扩展。(2)uid唯一实例ID机制:解决了背包系统中同ID物品共存的核心难题,是整个背包系统稳定运行的基石。(3)BFS地图生成算法:保证地图可达性的同时保留足够的墙壁和障碍,提供良好的探索体验。(4)双保险箱设计:普通保险箱与密码保险箱的差异化管理(开启方式、稀有度权重、物品数量)增加了游戏策略深度。(5)华为云码道(CodeArts)代码智能体辅助开发:从需求理解到Bug修复,CodeArts贯穿开发全流程,显著提升了开发效率和代码质量。3.2.1 背单词模块背单词模块采用浏览式学习交互,用户点击"记住了"按钮依次浏览当日单词。单词顺序使用Fisher-Yates洗牌算法随机打乱,确保每次学习顺序不同。词库支持雅思和GRE两套词书,切换词书时自动重置今日单词列表。完成当日学习任务后方可进入地图游戏,将学习与游戏形成正向激励闭环。以下是随机词序的代码模块:// learn.js — Fisher-Yates洗牌shuffleArray(array) {    for (let i = array.length - 1; i > 0; i--) {        const j = Math.floor(Math.random() * (i + 1));        [array[i], array[j]] = [array[j], array[i]];    }    return array;}3.2.2 地图游戏模块地图游戏是本应用的核心玩法,采用"搜打撤"(搜索-打击-撤离)模式。关键技术点包括:(1)动态地图生成:10×10网格,30%概率随机生成墙壁。使用BFS(广度优先搜索)验证起点(0,0)到终点(9,9)的可达性,若不可达则逐步移除墙壁直至可达,避免直接清空所有墙壁导致地图无遮挡。(2)保险箱系统:地图上分布两种保险箱——普通保险箱(灰色,直接搜索开启)和密码保险箱(金棕色,需答5道单词题且正确率≥60%方可开启)。密码保险箱的收藏品稀有度更高(权重:普通40/稀有45/传说15 vs 普通60/稀有30/传说10),且物品数量更多(3-4个 vs 2-3个)。保险箱开启后保留在地图上,玩家可返回拾取遗漏物品。(3)搜索动画:打开保险箱时,物品逐个"搜索"呈现——未搜索的物品显示灰色底色加白色旋转加载图标,每800ms揭示一个物品,搜索完成后才可拖拽拾取。这一设计增强了游戏的沉浸感和期待感。以下是核心代码:// game.js — 地图生成核心代码generateMap() {    const size = 10;    this.state.map = { size: size };     const walls = new Set();    for (let y = 0; y < size; y++) {        for (let x = 0; x < size; x++) {            if (Math.random() < 0.3 && !(x === 0 && y === 0)) {                walls.add(`${x},${y}`);            }        }    }     const exit = { x: size - 1, y: size - 1 };    walls.delete(`${exit.x},${exit.y}`);     // BFS验证可达性,不可达则逐步移除墙壁    let attempts = 0;    while (!this.isReachable(0, 0, exit.x, exit.y, walls, size) && attempts < 200) {        const wallArray = Array.from(walls);        if (wallArray.length === 0) break;        const randomWall = wallArray[Math.floor(Math.random() * wallArray.length)];        walls.delete(randomWall);        attempts++;    }     // 在可达区域随机放置保险箱    const reachable = this.getReachableCells(0, 0, walls, size);    const normalBoxCount = 3 + Math.floor(Math.random() * 3);    const quizBoxCount = 2 + Math.floor(Math.random() * 2);    // ... 放置逻辑省略     this.state.walls = Array.from(walls).map(key => {        const [x, y] = key.split(',').map(Number);        return { x, y };    });    this.state.boxes = boxes;    this.state.exit = exit;    this.buildWallSet();  // 预构建Set用于O(1)查找}3.2.3 背包系统背包系统是本应用最复杂的模块,参考"三角洲行动"的物品管理系统设计,关键技术难点及解决思路如下:(1)网格碰撞检测:背包为6×6网格,每个收藏品有不同尺寸(1×1到3×3),用slots二维数组描述占用形状。canPlace()函数遍历slots数组,检查目标区域所有格子是否均未被占用且不越界。(2)唯一实例ID管理:这是开发过程中遇到的核心技术难点。最初背包网格直接存储itemId,当两个同ID物品同时存在于背包时,removeItem()按ID删除会误删所有同ID实例,导致物品消失或数据错乱。解决方案是引入uid(唯一实例ID)机制——每次placeItem()时生成全局递增的uid,网格存储uid而非itemId,通过_uidMap(Map数据结构)映射uid到itemId。removeItem()按uid删除,精确移除单个实例,彻底解决同ID物品冲突问题。(3)拖拽交互:支持从保险箱拖拽物品到背包(拾取)、从背包拖拽物品到保险箱(丢弃)、背包内拖拽重排。拖拽时通过dataTransfer传递uid、itemId、context(来源标识)和originRow/originCol(原始位置),目标区域根据context判断操作类型。双击保险箱物品可自动拾取(autoPlace自动寻找空位)。(4)事件监听器重复绑定问题:在搜索动画期间,refreshLootUI()会重建DOM并重新绑定事件监听器。setupBoxDrop()对保险箱容器添加drop事件监听,若每次刷新都重新绑定,会导致同一容器上叠加多个监听器,拖拽丢弃时触发多次从而复制物品。解决方案是引入boxDropInitialized标志,确保drop监听器只绑定一次。以下是核心代码:// backpack.js — uid实例管理核心代码const Backpack = {    GRID_SIZE: 6,    CELL_PX: 50,    _uidCounter: 0,    _uidMap: new Map(),          // uid -> itemId 映射     getItemId(uid) {        return this._uidMap.get(uid) || null;    },     placeItem(grid, itemId, row, col) {        const item = DataManager.getCollection(itemId);        if (!item) return false;        if (!this.canPlace(grid, item, row, col)) return false;        const uid = ++this._uidCounter;       // 生成唯一实例ID        this._uidMap.set(uid, itemId);         // 建立映射        const slots = item.slots;        for (let r = 0; r < slots.length; r++) {            for (let c = 0; c < slots[r].length; c++) {                if (slots[r][c] === 1) {                    grid[row + r][col + c] = uid;  // 网格存uid而非itemId                }            }        }        return uid;    },     removeItem(grid, uid) {        for (let r = 0; r < this.GRID_SIZE; r++) {            for (let c = 0; c < this.GRID_SIZE; c++) {                if (grid[r][c] === uid) {                    grid[r][c] = null;        // 按uid精确删除,不影响同ID其他实例                }            }        }        this._uidMap.delete(uid);    },     getItemsInGrid(grid) {        const items = [];        const seen = new Set();        for (let r = 0; r < this.GRID_SIZE; r++) {            for (let c = 0; c < this.GRID_SIZE; c++) {                const uid = grid[r][c];                if (uid !== null && !seen.has(uid)) {                    seen.add(uid);                    const itemId = this._uidMap.get(uid);                    if (itemId !== undefined) items.push(itemId);                }            }        }        return items;    }};3.2.4 收藏品系统收藏品数据基于assets文件夹中的70个PNG图片文件定义,每项包含id、name、rarity(common/rare/legendary)、width、height、slots二维数组、image文件名和description。稀有度对应三种颜色边框:紫色(普通)、金色(稀有)、红色(传说)。物品在背包和仓库中均显示对应的PNG图片,而非文字名称。四、性能优化开发后期页面出现明显卡顿,经分析发现两个性能瓶颈并逐一解决:(1)渲染帧节流:原render()函数在每次按键移动时直接调用,长按方向键时每秒触发30-40次全量Canvas重绘。引入scheduleRender()方法,使用requestAnimationFrame将渲染调用合并到浏览器刷新周期,确保每帧最多重绘一次,显著降低CPU占用。(2)墙壁查找优化:原实现使用Array.some()遍历墙壁数组判断某格是否为墙,对于10×10地图每帧需100次查找,每次O(n)复杂度。改为在地图生成时构建Set数据结构(_wallSet),查找复杂度降为O(1),render()和movePlayer()均受益。五、技术难点与解决思路5.1 背包同ID物品冲突问题描述:当保险箱中刷出与背包中已有物品相同ID的收藏品时,双击拾取后物品消失;拖拽丢弃时物品被大量复制。根因分析:背包网格直接存储itemId,removeItem()按itemId遍历删除所有匹配格子。当两个同ID物品共存时,删除操作会误删另一个实例的所有格子,导致数据错乱。解决方案:引入uid(唯一实例ID)机制。每次placeItem()生成全局递增uid存入网格,通过Map映射uid→itemId。removeItem()改为按uid精确删除单个实例,getItemsInGrid()按uid去重后映射回itemId列表。此方案从数据模型层面彻底解决了同ID物品冲突,所有上层操作(拖拽、双击、丢弃)无需特殊处理。5.2 事件监听器重复绑定问题描述:将背包物品拖拽到保险箱丢弃时,物品被复制出多份。根因分析:refreshLootUI()每次调用时重建DOM并重新调用setupBoxDrop()绑定drop事件。由于drop监听器绑定在容器元素上(而非子元素),innerHTML重建子元素不影响容器上的监听器,导致每次刷新叠加一个新监听器,拖拽时触发N次回调。解决方案:引入boxDropInitialized状态标志,setupBoxDrop()首次绑定后设为true,后续调用直接返回,确保drop监听器只绑定一次。5.3 地图可达性保证问题描述:随机生成墙壁后,起点到终点可能不可达;简单清空所有墙壁会导致无遮挡的空旷地图。解决方案:使用BFS检测可达性,若不可达则随机移除一面墙壁,重复检测直到可达。设置200次上限防止死循环。此策略在保证可达性的同时最大程度保留墙壁,维持地图的探索性。5.4 Canvas渲染性能问题描述:页面操作时出现明显卡顿,尤其在长按方向键移动角色时。解决方案:①使用requestAnimationFrame将render()调用合并到浏览器刷新周期,避免同一帧内多次重绘;②将墙壁数据从Array改为Set,查找复杂度从O(n)降为O(1);③在地图生成时预构建wallSet,避免每次render重复计算。六、扩展资料6.1 源码仓库GitCode 公开仓库 lowfllly/huawei_internship默认分支:main6.2 开源内容边界公开仓库提供复现本案例所需的应用源码、测试、部署脚本和设计文档;训练营内部培训材料、个人凭据、本地开发工具配置及运行期数据按 .gitignore 与安全要求不纳入仓库。评审时建议先体验公网环境,再对照仓库中的实现、测试和设计文档核验关键功能。
  • [高校训练营] 【案例共创】学途 Navigator:码道搭台,确定性规则引擎与华为云 MaaS 协同的学分审计与选课规划实战(新版)
    一、概述1.1 案例介绍高校学生在毕业前普遍反复遇到三个缺少统一答案的问题:“现在毕业还差什么”“这学期选课是否冲突”“接下来几个学期该怎么安排”。本案例用华为云码道(CodeArts)代码智能体,以规范驱动开发方式构建了学途 Navigator——一套将学分审计、选课约束检查、多学期修读规划三项判断交由可逐条解释的确定性规则引擎完成,并接入华为云 MaaS 承担自然语言解释与多轮咨询职能的学生自助工具。审计、先修、冲突、风险的判断权始终在规则引擎手中,AI 只负责把结构化结论"翻译"成建议——这也是本案例区别于"成绩单管理 + 大模型问答"类项目的核心设计。系统已部署至华为云 Flexus 云服务器并提供公网访问,可用演示账号直接体验。1.2 适用对象高校学生个人开发者1.3 案例时间本案例完整开发周期约 8 天,覆盖设计、核心引擎开发、前端与部署、AI 接入、增强功能、测试与发布等阶段(详见 3.1 节开发计划);阅读本文并在本地运行核心功能(AI_PROVIDER=mock 模式,无需华为云凭据)预计需要 40–60 分钟。1.4 案例流程说明:先产出完整的详细设计文档体系(需求规格、系统架构、数据库设计、审计与约束引擎规则、AI 咨询降级链等 17 份文档),作为码道对话的结构化输入;码道生成数据模型、JWT 鉴权模块与管理端 API;码道按规则表生成学分审计引擎、约束检查器、先修图算法,人工核验并固化为单元测试;完成学生端四个核心页面,首次发布到华为云 Flexus 实例,实现公网访问;接入华为云 MaaS,联调 Provider 抽象、上下文组装与降级链;开发进度可视化、推荐排课、多学期修读规划等增强功能;完善单元与集成测试,完成端到端验收场景的测试;整理演示材料,发布案例文档与 GitCode 仓库。1.5 资源总览体验完成后请及时释放云端资源(详见第四节),避免产生多余费用。资源名称规格单价华为云码道(CodeArts)代码智能体通用体验版免费云服务器通用计算型 x1(Flexus X1),1 vCPU / 1 GiB / 40 GiB SSD,Ubuntu 22.04¥0.1228/小时(据实际购买页截图)弹性公网 IP全动态 BGP,5 Mbit/s 独享带宽¥0.80/GB 流量华为云 MaaS 推理服务ModelArts Studio DeepSeek 系列 Tokens 套餐包代金券覆盖(领取方式见 2.1 节)账户保证金满足按需计费账户余额下限要求1 元,实操完成后可提现实际总花费取决于云服务器运行时长与 MaaS 调用量,此处仅列出可核实的单价;如需核实完整费用总额,可在华为云费用中心导出账单进行核对。1.6 公网部署地址访问地址:http://121.37.156.117/GitCode 仓库:https://gitcode.com/2301_79350888/xuetu-navigator (默认分支 main)。演示账号:学生 demo / demo123(中等风险)、demo2 / demo123(高风险);管理员 admin / admin123。使用说明:该地址为部署在华为云 Flexus 云服务器上的训练营评审演示环境。演示账号密码已公开,请勿录入真实个人数据;评审结束并释放弹性公网 IP 后,该地址将停止访问。二、环境和资源准备2.1 领取华为云 MaaS 平台大模型 Tokens登录华为开发者空间,任选以下一种方式领取模型的 API 地址、模型名称与 API Key:方式一:参考案例《华为开发者空间 - ModelArts Studio大模型通用代金券领取使用指导》中的"二、开通MaaS平台大模型"章节内容领取代金券。方式二:参考案例《华为云MaaS平台大模型Tokens领取使用指导》中的"二、领取MaaS平台大模型Tokens"章节内容,领取MaaS平台DeepSeek V3系列大模型Tokens代金券,购买ModelArts Studio DeepSeek Tokens套餐包,开通模型服务。获取到的三项信息仅需填入服务器端的 .env 文件(对应 MAAS_BASE_URL/MAAS_MODEL/MAAS_API_KEY 三个环境变量),前端代码不涉及。后端请求日志仅记录追踪 ID、请求方法、路径、状态码与耗时,调用 MaaS 失败时也只记录截断后的错误信息,密钥本身不会出现在日志中。本案例不在文档中展示具体的 API 地址、模型名称与 Key 取值。2.2 华为云码道(CodeArts)安装部署登录华为云官网完成账号注册与实名认证,并在华为开发者空间内安装激活码道(CodeArts)代码智能体(官网入口见 1.5 节资源表)。本案例开发过程中码道模型选择 GLM 系列,工作模式为智能体对话模式。2.3 本地开发环境搭建无需任何华为云凭据,AI_PROVIDER=mock 模式即可在本地运行全部核心功能,包括学分审计、选课约束、多学期规划与 AI 咨询的降级路径:# 后端:Python 3.11 + FastAPI cd backend && python -m venv .venv && .venv/Scripts/activate # Linux/Mac: source .venv/bin/activate pip install -r requirements.txt cp .env.example .env # 填写 JWT_SECRET(如 openssl rand -hex 32),AI_PROVIDER=mock 即可全功能开发 python -m app.seeds.seed # 建表并写入种子数据(幂等,可重复执行) uvicorn app.main:app --reload --port 8000 # 前端:Vue3 + Vite cd frontend && npm i && npm run dev # localhost:5173,/api 代理到 8000 # 测试(须先激活 backend/.venv,再执行 python -m pytest,二者缺一均会报错,见下表) cd backend && .venv/Scripts/activate python -m pytest --cov=app/services --cov-report=term-missing测试命令有两个前提条件,缺一均会报错:报错现象根因解决方法ModuleNotFoundError: No module named 'app'使用了裸 pytest 命令而非 python -m pytest;裸 pytest(控制台脚本入口)在 Windows 上不会自动将当前目录加入 sys.path改用 python -m pytest,并确认当前目录为 backendModuleNotFoundError: No module named 'tests.conftest'在全局或共享 Python 环境(如 conda base)下运行,该环境的 site-packages 中恰好安装了同名的顶层 tests 包,屏蔽了项目本地的 backend/tests/改为激活并使用项目自身的 backend/.venv,不使用全局 Python 解释器三、构建学途 Navigator 应用3.1 总体架构与设计思路本案例采用规范驱动开发(Spec-Driven Development)方式:先撰写一套完整的详细设计文档,涵盖需求规格、系统架构、数据库设计、各引擎规则、AI 咨询模块、安全与异常处理、测试设计、部署方案与验收标准等方面,再以这套文档为结构化输入,逐阶段驱动码道生成代码,代码经人工核验后固化为测试用例。整个开发周期约 8 天,按四个关键节点推进:完成数据模型冻结、完成首次云端部署、开展增强功能的集中开发、完成代码冻结,随后进入测试与发布收尾。实际提交记录与该计划安排基本吻合,可在版本提交历史中找到同期的开发记录。系统架构:系统架构遵循自上而下的单向依赖约束:routers → services → models,services 层不允许导入 FastAPI(以保证引擎可脱离 HTTP 直接单测),models 层不允许依赖 services。该规则由代码审查维持,可通过"能否在不依赖 FastAPI/TestClient 的情况下直接单测引擎"验证:test_audit.py、test_checker.py、test_planner.py、test_recommender.py、test_prereq_graph.py 五份测试文件均仅导入 app.services 下的模块,构成该约束成立的可执行证据。系统数据模型共设计 11 张表,涵盖用户与鉴权、专业与培养模块、课程与先修关系、学期与开课计划、选课方案、修读规划、AI 对话记录等业务实体,实体关系如下:其中三处相对初版高层架构设计的修正均记录为正式架构决策:先修关系改用关联表而非 JSON 数组(原因:环检测与链深度计算需要结构化的边表)、开课时段改为 JSON 数组以支持一课多时段、学期独立建立字典表(原因:多学期规划需要有序的学期序列)。模块职责:目录职责关键约束backend/app/routers/鉴权、参数校验、调用 services、组装统一响应 {code,message,data}不承载业务规则backend/app/services/审计、约束、推荐、规划、导入、AI 咨询的全部业务规则以纯函数为主,不依赖 FastAPIbackend/app/services/prereq_graph.py先修图的环检测、链深度、后继链长度、最长路径的唯一实现由审计、检查、推荐、规划、导入五处调用,不允许重复实现backend/app/services/advisor/AI Provider 抽象、MaaS 实现、Mock 实现、上下文组装详见 3.4 节backend/app/models/SQLAlchemy ORM 模型,为数据库 schema 的唯一事实来源不依赖 servicesfrontend/src/stores/Pinia 全局状态,登出时统一调用 $reset()详见 3.5.3 节码道 CodeArts Doer 在本地工作区(.codeartsdoer/ 目录,为机器本地配置,不纳入仓库版本控制)针对开发前期的各阶段任务,生成了 spec.md(需求规格)、design.md(设计方案)、tasks.md(编码任务清单)三段式产出,合计约 8700 行。任务清单中的条目与实际代码逐一对应:例如某阶段的 tasks.md 要求"在 backend/app/services/audit.py 中实现 build_passed_map(records, courses)",对照实际实现(audit.py 第 25–44 行),函数名、签名与行为完全一致,说明码道在需求分析、任务拆解与代码生成环节确实发挥了实际作用。进一步核查显示,该项目未配置 MCP(模型上下文协议,用于让智能体调用外部工具与数据源)服务,也未使用自定义 Skill 或 Project Expert 等扩展能力,码道的使用范围限定在规范驱动开发流程中的规格生成与代码生成环节。设计文档体系要求任何引擎规则的改动均先补充或修改单元测试用例、再修改实现,以避免规则口径漂移。开发过程中人工核验阶段发现并按此流程修复的若干问题,详见 3.5 节"技术难点与解决思路"。3.2 核心功能与用户流程功能完备性矩阵(下表基于源码与路由表逐项核查,按"已实现/未实现"如实标注各功能的完成状态):功能重要程度端状态证据认证与账号核心功能通用已实现routers/auth.py、security.py(PyJWT + bcrypt)培养方案与课程库管理(含 JSON 导入校验,覆盖 8 类校验规则)核心功能管理端已实现services/importer.py、test_importer.py学期与开课计划管理核心功能管理端已实现routers/admin/semester.py、routers/admin/offering.py已修课程录入与学分审计(含毕业风险)核心功能学生端已实现services/audit.py(行覆盖率 98%)选课模拟与约束检查(含方案保存)核心功能学生端已实现services/checker.py(行覆盖率 96%)AI 学业咨询核心功能学生端已实现services/advisor/*,详见 3.4 节AI 建议一键应用增强功能学生端已实现详见 3.4.6 节学业进度可视化(雷达图/环形图)重要功能学生端已实现components/ModuleRadar.vue、ProgressRing.vue推荐排课(一键推荐)重要功能学生端已实现services/recommender.py(行覆盖率 90%)多学期修读规划重要功能学生端已实现services/planner.py(行覆盖率 92%)审计报告打印友好导出锦上添花学生端未实现已检索前端全目录,未发现打印或导出相关代码操作日志写入核心功能后端已实现models/log.py::log_operation()操作日志查看界面锦上添花管理端未实现路由中无 admin/logs,无对应页面组件小程序壳不适用—明确不做属产品范围裁剪,本案例不涉及移动端小程序项目验收标准明确规定,"重要功能"与"锦上添花"级别中未实施的部分不影响项目整体验收,但需要如实标注其状态;审计报告打印导出与操作日志查看界面均属于这一情况。四个典型使用场景:① 大四学生登录后查看总进度与毕业风险;② 在校学生选课模拟页勾选候选课程,即时查看冲突/缺先修/学分上限提示;③ 在校学生向 AI 咨询排课建议并一键应用;④ 管理员导入培养方案 JSON 或开课 CSV,查看结构化校验报告。典型操作路径(演示学生账号 demo/demo123,软件工程 2023 级第 7 学期):登录后进入审计看板:环形进度 106/160(66%)、风险徽标"中等风险"、五模块雷达图;2. 进入"选课模拟"页勾选课程,约 300 毫秒内获得逐课冲突/缺先修提示;3. 进入"AI 咨询"页提问选课建议,AI 结合真实缺口数据分点作答并给出"一键应用"建议卡片;4. 进入"修读规划"页生成分学期时间线。3.3 部署项目代码项目结构说明:xuetu-navigator/ ├── backend/ │ ├── app/ │ │ ├── models/ # SQLAlchemy 模型 │ │ ├── schemas/ # Pydantic 请求/响应契约 │ │ ├── routers/ # API 路由(auth/plan/selection/chat/admin等) │ │ ├── services/ # 领域层:audit/checker/planner/recommender/prereq_graph/advisor │ │ └── seeds/ # 种子数据脚本 │ └── tests/ # pytest 测试(158 项) ├── frontend/ │ └── src/{api,stores,router,views,components} ├── deploy/ # setup.sh / deploy.sh / nginx.conf / systemd unit └── docs/ # 17 份详细设计文档 + 案例文档下载源码:git clone https://gitcode.com/2301_79350888/xuetu-navigator.git关键代码讲解——毕业风险五级确定性规则(backend/app/services/audit.py:204-254),按序评估、首个命中即定级,每条结论均带机器可读的 rule 编码:def assess_risk(missing_required, total_gap, remaining_semesters): if remaining_semesters is None: return RiskResult(level="unknown", reasons=[]) reasons, level = [], "low" for mc in missing_required: if mc.chain_remaining_len &gt; remaining_semesters: reasons.append(RiskReason(rule="R1-先修链", message=f"...")) level = "high" if total_gap &gt; remaining_semesters * MAX_SEMESTER_CREDITS: reasons.append(RiskReason(rule="R2-容量", message=f"...")) level = "high" # R3-紧容量 / R4-链贴线 / R5-默认 略 return RiskResult(level=level, reasons=reasons) 约束检查器为四级管道、按序执行且互不短路(backend/app/services/checker.py):依次执行重复修读检查、时间冲突检查、先修依赖检查、学分上限检查,用户一次性看到全部问题而非逐条重新提交。先修图的环检测、链深度、后继链长度仅在 backend/app/services/prereq_graph.py 一处实现,供审计、检查、推荐、规划、导入校验五处共用,避免多处口径分歧。运行调试:见 2.3 节本地启动命令;实测 python -m pytest --cov=app/services --cov-report=term-missing:3.4 MaaS 融合方案> 本节说明 MaaS 在系统中的协同边界:审计、先修、冲突、风险的判断权始终在确定性规则引擎手中,MaaS 负责结构化结果的自然语言解释与多轮交互。二者的协同机制、失败处理与成本控制均有明确设计与代码实现。3.4.1 判断权与解释权的分离系统设计明确了这样一条架构原则:AI 只做解释、建议、自然语言交互;学分够不够、课冲不冲突、风险几级,一律由确定性引擎产出并作为事实注入;AI 模块不可用时,学分审计与选课约束等核心功能不受影响。该原则体现在三处具体设计中:AI 的输入是审计引擎 run_audit() 计算完成的结构化 JSON,不参与学分或先修关系的计算;AI 的输出如涉及选课建议,采用标记信号加确定性推荐引擎复用的方式(3.4.6 节),不解析 AI 自由文本以获取课程号;AI 完全不可用时,Mock 作为降级路径直接复用同一份结构化审计数据组装摘要。3.4.2 Provider 抽象与降级链# backend/app/services/advisor/base.py @runtime_checkable class AIProvider(Protocol): @property def name(self) -&gt; str: ... def generate(self, messages: list[dict]) -&gt; AIReply: ... def get_provider() -&gt; AIProvider: settings = get_settings() if settings.AI_PROVIDER == "maas": from app.services.advisor.maas import MaaSProvider return MaaSProvider() from app.services.advisor.mock import MockProvider return MockProvider() MaaSProvider.generate()(maas.py:18-56)使用 httpx 以 OpenAI 兼容协议向 {MAAS_BASE_URL}/chat/completions 发起 POST 请求,超时时间读取自 settings.AI_TIMEOUT。请求过程中的任何异常(超时、非 2xx 响应、JSON 解析失败)均被捕获,并按 AI_FALLBACK 配置决定后续处理:默认值 mock 会实例化 MockProvider 作为降级方案并标记 degraded=True。AI_PROVIDER 与 AI_FALLBACK 的默认值均为 mock,即 Mock 是开发阶段的默认路径,而非后续补充的降级分支。3.4.3 上下文组装与裁剪context.py::build_context() 在每次对话时动态组装:固定 System Prompt、一条携带【学生数据】JSON 的 user 消息,以及最近 6 轮历史对话。裁剪策略(预算约 6000 tokens)按影响程度由小到大依次执行:开课列表仅保留当前存在学分缺口的模块对应课程(context.py:184-206),且已通过或在修的具体课程会被显式排除(第 192 行:if c.code in passed_codes or c.code in enrolled_codes: continue);先修链仅展开缺失必修课对应的部分;历史对话按 6 轮、4 轮、2 轮逐级压缩;审计摘要不参与裁剪,作为回答质量的底线保留。上下文数据均来自当次对 run_audit() 的实时查询结果,而非缓存或静态文本。3.4.4 防幻觉机制的验证System Prompt(context.py:27-37)明确规定"只依据【学生数据】回答;数据中没有的课程、学分、政策,需说明系统数据中没有"“涉及学分缺口、先修关系、毕业风险时,必须引用数据中的数字与课程号,不得自行推算修改”。浏览器实测截图记录了一次验证:学生提问"我想辅修人工智能,我这学期该如何选课?“,AI 回复第一句明确说明"系统数据中没有辅修人工智能的相关课程、学分要求或政策信息,请咨询教务部门了解辅修方案的具体要求”,随后针对"本学期选课建议"给出的课程号均为学生当前学期的实际开课数据。截图如下:同一次对话中,对超出数据范围的问题作出明确说明、对数据范围内的问题引用真实课程号,两种行为均被截图记录。3.4.5 成本与滥用控制措施值代码位置限流5 次/分钟/用户,内存滑动窗口routers/chat.py::check_rate_limit()超时45 秒(调整过程见 3.5.2 节)config.py::AI_TIMEOUT单次回答长度上限1024 tokensconfig.py::AI_MAX_TOKENS用户输入长度上限2000 字符schema 校验,超长返回 40001历史窗口最近 6 轮config.py::AI_HISTORY_ROUNDS密钥存放仅存于服务器端 .env 文件,前端不涉及;请求日志与错误日志均不记录密钥字段middleware.py(请求日志)、advisor/maas.py(调用失败日志)3.4.6 AI 建议一键应用:AI 与确定性推荐引擎的协同该功能体现了 AI 与确定性推荐引擎的协同关系,而非相互替代。AI 在自然语言中给出选课建议后,学生希望能够一键应用到选课模拟页;若直接解析 AI 输出文本以获取课程号,存在模型编造不存在课程号的风险。解决方式是:System Prompt 要求模型仅在本次回答确实给出具体选课建议时,在回答末尾单独一行输出标记 [[SHOW_COURSE_SUGGESTIONS]];后端仅识别并剥离该标记(不解析建议内容本身),命中后复用与"一键推荐"相同的确定性推荐引擎,重新计算一份结构化候选:# backend/app/routers/chat.py:44-84(节选) def _build_suggested_offerings(user, db) -&gt; list[SuggestedOffering] | None: """AI 判断本次回答给出了选课建议时,复用确定性推荐引擎现算一份结构化候选, 而不是解析 AI 自由文本猜课程号。任何异常都只记日志并返回 None, 绝不能让这个附加功能打断主聊天回复。""" try: ... result = run_recommend(audit=bundle.result, courses=..., offerings=..., target_credits=24.0, credit_limit=settings.MAX_SEMESTER_CREDITS) return [SuggestedOffering(...) for item in result.items] except Exception: logger.warning("构建聊天选课建议失败,已跳过", exc_info=True) return None 前端消费逻辑位于 SelectionView.vue:418-456(applyExternalOfferings)与 RoadmapView.vue:217-263(applyCurrentSemesterCourses):回复下方渲染建议卡片,点击后跳转选课模拟页,目标页逐条校验课程状态(已通过/已在修/已在候选中的会被过滤并提示),合法课程与已保存候选合并(而非覆盖),自动执行约束检查,由学生自行确认保存。suggested_offerings 不写入数据库,仅在当次新回复中出现,历史消息重新加载后不再显示按钮。AI 相关的测试要求均已在 test_ai_api.py(22 个测试函数)中实现,包括故障注入模拟 httpx 超时、验证 degraded=True 且响应结构不受影响的测试,在代码层面验证了"AI 不可用时核心功能仍可运行"这一架构设计的成立。3.5 技术难点与解决思路以下问题是开发过程中人工核验环节发现的实际问题,均有明确的根因分析与修复方式。3.5.1 PyJWT 与 python-jose 的依赖声明不一致依赖声明与实际导入不一致的问题仅在全新云服务器上首次暴露:本机开发环境与当时较早阶段的 148/150 项测试均未触发该问题(测试总数其后随功能增补持续增加,最终为 158 项,见 3.6 节),但在全新服务器执行 pip install -r requirements.txt 后,seed.py 因 import app.security 触发 ModuleNotFoundError: No module named 'jwt' 而失败。根因在于 requirements.txt 声明的依赖是 python-jose[cryptography],而 security.py 实际导入的是 jwt——这是 PyJWT 包的模块名,与 python-jose 是两个不同的第三方库。本机 .venv 因历史遗留同时安装了两个包,掩盖了依赖声明的错误。修复方式是将依赖改为直接声明 PyJWT&gt;=2.8。该问题表明,仅在全新、纯净的目标环境完整执行一次部署脚本,才能验证依赖声明的正确性。3.5.2 MaaS 调用超时阈值的调整Mock 模式开发阶段未暴露相关问题,但接入华为云 MaaS 后,浏览器实测显示多数提问被降级为"离线建议"。排查调用耗时发现,失败请求的耗时精确落在超时阈值上——先后精确落在 15230 毫秒、25230 毫秒,表明是 httpx 的超时机制主动截断了仍在进行的生成过程,而非网络波动所致。改用不设超时的方式直连 MaaS 探测,实测生成耗时约 32.23 秒。超时阈值先后调整为 25 秒(仍不充分)、最终调整为 45 秒(结合探测数据确定),前端超时同步调整为 55000 毫秒并预留余量。3.5.3 前端跨用户状态残留同一 SPA 会话内登出学生账号 A、免刷新登录学生账号 B 后,审计看板短暂显示账号 A 的旧数据,直至强制刷新页面才恢复正确;直接使用账号 B 的 token 调用接口验证,后端返回结果始终正确,确认问题源于前端。根因是审计数据的前端缓存带 5 分钟 TTL,登出逻辑此前仅清除 token 与用户信息,未重置任何按用户维度缓存的状态。修复方式是在登出逻辑中对相关 Pinia store 显式调用 $reset()。3.5.4 AI 自由文本对已修课程的误推荐AI 一键应用功能上线后,浏览器实测发现:AI 针对"专业选修还差多少学分、应如何选课"给出的自然语言建议中,包含了学生已经修过并通过的课程,将其作为"本学期可选课程"推荐。根因在于提供给 AI 的开课列表仅按"课程所属模块是否仍有学分缺口"过滤,未排除学生已通过或在修的具体课程——模块整体存在缺口不代表模块内每一门课程都尚未修读。修复方式是补充已通过/在修课程的排除逻辑,并新增回归测试。该问题也印证了"AI 不解析自由文本、结构化推荐单独由确定性引擎生成"这一架构原则的必要性:同期的结构化推荐卡片有独立的过滤逻辑,未受此问题影响。3.5.5 部署环境差异本机(Windows + Git Bash)安装的 rsync 与远程 SSH 子进程交互存在已知兼容性问题,修复方式是改用 tar czf - | ssh ... tar xzf - 管道方式同步文件;远端 pip install 直连境外源时反复超时,修复方式是加入华为云 PyPI 镜像源。两处问题均在首次云端部署时暴露。3.6 测试、异常处理与降级测试设计将测试划分为四层:单元测试(五个纯函数引擎及导入校验)、集成测试(pytest + TestClient + 临时 SQLite)、安全专项(越权访问、无 token 访问、注入类攻击)、端到端(手动清单辅以 Playwright)。测试结果见 3.3 节"运行调试",158 项全部通过、核心引擎覆盖率 90% 以上。早期测试套件未显式隔离 AI_PROVIDER 环境变量,存在测试执行过程中意外发起 MaaS 请求的风险,修复方式是在 tests/conftest.py 中强制默认使用 Mock。学分审计与选课约束两个核心功能的路由与服务层代码均不依赖 advisor 模块,可通过 import 语句直接核查。安全与异常处理设计的要点包括:鉴权分层(get_current_user/require_student/require_admin)、越权访问返回 40401 而非 403(避免泄露资源存在性)、输入校验分 Schema 层与业务层两层、CSV 公式注入防护、AI 回复 Markdown 渲染使用白名单不允许原始 HTML、统一异常体系携带 trace_id、请求与错误日志不记录密钥等敏感字段。3.7 华为云部署与运行效果部署拓扑:1 GiB 内存机型属于低规格实例,deploy/setup.sh 在部署流程中自动创建 1 GiB swap 作为内存安全余量。首次初始化通过 setup.sh(幂等)完成 Nginx 与 Python 环境安装、systemd 服务注册;日常发布通过 deploy.sh 完成前端构建、tar+ssh 文件同步、远端依赖安装、种子数据初始化与健康检查。访问验证:项值公网地址http://121.37.156.117/MaaS 云端调用AI_PROVIDER=maas 配置下实测 POST /me/chat:HTTP 200,耗时 15.1 秒,degraded:false,回复引用 demo 账号的实际数据账号密码角色用途adminadmin123管理员培养方案与开课计划导入及校验演示demodemo123学生(中等风险场景,第 7 学期)模块缺口与选课冲突演示demo2demo123学生(高风险场景,第 5 学期)多学期规划演示演示账号密码直接展示在公网登录页,便于访问者体验完整功能,请勿在演示账号下录入真实个人数据。根据安全组规则截图核查,入方向规则实际允许 TCP:22(SSH)、TCP:80、TCP:443、TCP:3389(RDP,在 Ubuntu 服务器上无实际用途)及全部 ICMP,源地址均为 0.0.0.0/0,即不限来源 IP,与设计阶段"入方向仅开放 22 端口(限本机 IP)与 80 端口"的规划不符,推测是华为云"Sys-WebServer"默认安全组模板未作收紧所致。该配置存在明显的收紧空间,正式使用前建议将入方向 22、3389 端口的源地址限制为运维人员的固定出口 IP 段。3.8 总结与展望学途 Navigator 使用三个确定性规则引擎(学分审计、约束检查、多学期规划)与一个共用的先修图算法模块,解决"学分是否达标、选课是否冲突、后续如何安排"三个可审计的问题;华为云 MaaS 承担自然语言层面的解释、多轮交互与选课建议信号判断,通过 Provider 抽象与 Mock 优先的降级链,保证 AI 不可用时核心功能不受影响。开发过程以完整的详细设计文档体系为输入,驱动华为云码道(CodeArts)代码智能体逐模块生成代码,经人工核验、158 项单元与集成测试及浏览器实测完成验证,最终部署在华为云 Flexus 实例上提供公网服务,并经云端 MaaS 调用验证。已知局限:审计报告打印导出与操作日志查看界面尚未实现:二者均属"锦上添花"级别的功能,按项目验收标准,该级别的未实施项不影响项目整体验收。安全组配置范围偏宽:22、3389 端口对全部来源地址开放,详见 3.7 节。当前为 HTTP 而非 HTTPS:尚未配置 SSL 证书,属训练营演示场景下的已知取舍。单实例部署,SQLite 单写者模式:并发能力面向演示场景(约 10 并发以内)设计,非面向生产环境的高并发方案。suggested_offerings 不持久化:AI 一键应用建议仅在当次新回复中出现,页面刷新后不再显示。多学期规划不检查未来学期的时间冲突:因未来学期的开课表尚不存在,选修缺口以模块占位学分表达,而非具体课程。后续改进方向:① 收紧安全组入方向规则,评估启用 HTTPS 的可行性;② 完成审计报告打印导出与操作日志查看界面的开发,后端数据已就绪,主要待补充前端页面实现;③ 如后续出现稳定并发需求,评估已预留的 RDS for MySQL 迁移路径。四、释放资源体验完成后应及时释放全部云资源(云服务器、弹性公网 IP),避免产生持续费用;释放前建议导出数据库备份与操作日志归档至本地。释放路径:进入 ECS 实例列表,选择目标实例,点击"更多 → 删除",在对话框中选择"释放云服务器绑定的公网 IP 地址",确认释放。五、扩展资料与复现指引5.1 源码仓库GitCode 公开仓库:https://gitcode.com/2301_79350888/xuetu-navigator默认分支:main仓库内容:backend/ 与 frontend/ 包含前后端源码,backend/tests/ 包含自动化测试,deploy/ 包含华为云部署脚本,docs/ 包含完整设计与验收文档。5.2 推荐阅读与复现路径先体验:使用 1.6 节的公网地址和演示账号查看审计看板、选课模拟、AI 咨询与多学期规划。再运行:阅读仓库根目录 README.md,按快速开始说明启动后端与前端;无华为云 MaaS 凭据时可使用 AI_PROVIDER=mock 完成本地体验。理解设计:从 docs/README.md 进入 01–17 号设计文档,依次查看需求、架构、数据模型、API、规则引擎、AI 咨询、安全、测试与部署设计。验证质量:运行 backend/tests/ 自动化测试,并结合 docs/bugs.md 查看已确认缺陷的现象、根因、修复与回归证据。复现部署:参考 deploy/ 脚本和部署设计文档,在华为云 Flexus 云服务器上完成 Nginx、systemd、SQLite 与应用服务配置。5.3 开源内容边界公开仓库提供复现本案例所需的应用源码、测试、部署脚本和设计文档;训练营内部培训材料、个人凭据、本地开发工具配置及运行期数据按 .gitignore 与安全要求不纳入仓库。评审时建议先体验公网环境,再对照仓库中的实现、测试和设计文档核验关键功能。
  • [高校训练营] 基于华为云码道(CodeArts)代码智能体的AI智能学习助教系统开发实践
    码道赋能:EduGuide AI智能学习助教系统开发(FastAPI + React)案例介绍本案例依托华为云码道(CodeArts)代码智能体快速完成 EduGuide AI智能学习助教系统项目开发。EduGuide 是一款基于双层数据架构的 AI 驱动学习助手:全局资料库层支持用户上传 PDF/PPTX/DOCX/EPUB 等学习材料或搜索在线书籍,系统自动构建向量知识库(ChromaDB);项目学习空间层由 AI Agent 自动生成个性化学习路径,通过多轮互动问答、自动测验出题评分、错题追踪与掌握度分析,驱动自适应学习闭环。项目采用 FastAPI + React 19 前后端分离架构,集成 DeepSeek(文本LLM)与 Qwen-VL(视觉模型)双 AI 通道,熔断器保障服务高可用。案例内容 完整案例在线体验地址: EduGuide AI智能学习助教系统(https://kayson.tech/)EduGuide 案例基于 AI 生成搭建,每次生成的项目效果略有不同,如需完整体验该案例可在源码处下载并体验。一、概述1.1 案例介绍本案例依托华为云码道快速完成 EduGuide AI智能学习助教系统项目开发。系统核心能力包括:智能资料管理:支持 PDF/PPTX/DOCX/EPUB/图片上传,AI自动解析提取文本;支持 Z-Library、Project Gutenberg、Library Genesis 三大书源在线搜索导入AI Agent 自适应学习:Agent 多阶段工作流(研究→大纲→教学方案→互动授课),SSE流式实时输出讲解内容多风格互动教学:支持苏格拉底式、隐喻式、严谨式、鼓励式四种教学风格,多轮问答+自由提问智能测验与评分:AI自动生成章节测验(选择/填空/简答/判断/论述),支持图片题AI评分学习报告与错题追踪:掌握度雷达图、薄弱知识点分析、错题重做、学习数据导出案例技术选型:层技术后端FastAPI (Python 3.11+) + SQLAlchemy 2.0 async + Alembic数据库PostgreSQL 16 + Redis 7 + ChromaDBAIDeepSeek (文本) + Qwen-VL (视觉) + sentence-transformers (嵌入)前端React 19 + TypeScript 6 + Ant Design 6 + Zustand 5 + Vite 8部署Nginx (反向代理) + Docker Compose华为云码道(CodeArts)代码智能体:一个理解项目需求,懂得编码之道,善用百器的实干派AI研发专家,开启你的编码自动驾驶模式。本案例中作为核心开发工具,通过智能体模式快速构建 EduGuide AI智能学习助教系统项目代码。EduGuide系统首页效果图1.2 适用对象企业个人开发者高校学生1.3 案例时间本案例总时长预计2小时。1.4 案例流程说明:开发者安装华为云码道 CodeArts 代码智能体;Python 3.11+ 安装配置;Node.js 20+ 安装配置;安装 Docker Desktop(含 Docker Compose);从 GitHub 克隆 EduGuide 源码到本地;使用华为云码道理解项目代码结构与功能,完成环境配置、数据库初始化与项目启动验证;使用华为云码道进行功能扩展与调试优化。案例流程图1.5 资源总览本案例预计花费如下。资源名称规格 华为云码道(CodeArts)代码智能体专业版 DeepSeek API按量计费 Qwen-VL API按量计费 二、基础环境与资源准备2.1 AI IDE 华为云码道安装部署访问此专属开通链接,免费开通华为云码道(CodeArts)代码智能体体验版,无需复杂配置:一键开通华为云码道体验版!参考案例《AI IDE华为云码道(CodeArts)代码智能体安装部署》完成 Windows 版 AI IDE 华为云码道(CodeArts)代码智能体安装部署。华为云码道IDE主界面2.2 Python 3.11+ 安装配置FastAPI 后端运行依赖 Python 环境。使用快捷键 `Ctrl+Shift+``,新建 AI IDE 终端,输入以下命令查看 Python 环境是否安装?python --version如果没有安装,请参考案例《SKILL快速构建你的Java、Python和Node.js开发环境》一键配置 Python 开发环境。2.3 Node.js 20+ 安装配置React 19 + Vite 8 前端构建依赖 Node.js 环境(需 Node >= 20.19,因为 rolldown 导入 node:util 的 styleText,Node 18 不可用)。使用快捷键 `Ctrl+Shift+``,新建 AI IDE 终端,输入以下命令查看 Node.js 环境是否安装?node --version如果没有安装,请与码道对话,让码道帮助我们下载 Node.js 并配置环境变量。2.4 安装 Docker DesktopDocker Desktop 提供 Docker 引擎与 Docker Compose 编排能力,用于一键启动 PostgreSQL、Redis、后端、前端四个服务。参考 Docker 官方文档安装 Docker Desktop for Windows。安装完成后,在终端验证:docker --versiondocker compose version2.5 获取 AI API 密钥EduGuide 集成双 AI 通道,需分别获取 API 密钥:DeepSeek(文本LLM):访问 DeepSeek 开放平台注册并获取 API Key,用于讲解生成、测验出题、评分、对话等文本智能能力。Qwen-VL(视觉模型):访问阿里云百炼平台开通 Qwen-VL 模型并获取 API Key,用于图片资料的文字提取与理解。提示:两个平台均提供免费额度,足够开发调试使用。三、构建 EduGuide 应用3.1 部署项目代码项目技术栈层技术后端FastAPI (Python 3.11+) + SQLAlchemy 2.0 async + Alembic数据库PostgreSQL 16 + Redis 7 + ChromaDBAIDeepSeek (文本) + Qwen-VL (视觉) + sentence-transformers (嵌入)前端React 19 + TypeScript 6 + Ant Design 6 + Zustand 5 + Vite 8部署Nginx (反向代理) + Docker ComposeEduGuide系统架构图从 GitHub 克隆源码在码道终端中,输入以下命令将 EduGuide 源码克隆到本地:git clone https://github.com/HJGisfh/eduguide.gitcd eduguide3.2 码道理解项目代码结构与功能克隆完成后,让码道帮助我们理解整个项目的代码结构与功能。在码道对话框中,输入以下提示词:请分析当前 EduGuide 项目的完整代码结构,包括:1. 后端目录结构(api/models/services/schemas/ai/knowledge/infra 各模块职责)2. 前端目录结构(pages/components/store/services 各模块职责)3. 核心业务流程(用户上传资料→创建项目→Agent工作流→互动学习→测验评估的完整数据流)4. AI双通道架构(DeepSeek文本+Qwen-VL视觉+熔断器机制)5. 向量知识库RAG检索流程码道将自动分析项目代码,输出完整的项目架构说明,帮助开发者快速理解系统设计。码道对话:分析项目代码结构3.2.1 后端代码结构讲解项目入口 backend/main.py:app = FastAPI(title="EduGuide API", version="0.1.0", lifespan=lifespan)app.include_router(materials.router, prefix="/api/v1/materials", tags=["materials"])app.include_router(projects.router, prefix="/api/v1/projects", tags=["projects"])app.include_router(learning.router, prefix="/api/v1/learning", tags=["learning"])app.include_router(quizzes.router, prefix="/api/v1/quizzes", tags=["quizzes"])app.include_router(records.router, prefix="/api/v1/records", tags=["records"])app.include_router(books.router, prefix="/api/v1/books", tags=["books"])app.include_router(user.router, prefix="/api/v1/user", tags=["user"])app.include_router(auth.router, prefix="/api/v1/auth", tags=["auth"])8 个路由模块注册到 FastAPI 应用,统一使用 /api/v1 前缀。lifespan 生命周期管理中启动时检查书源健康状态,关闭时释放数据库连接池。API 路由层 backend/api/(9个文件):文件职责auth.py用户注册/登录,返回 JWT 令牌user.py用户资料查询、教学风格更新materials.py资料 CRUD、文件上传、重试解析projects.py项目 CRUD、资料关联、Agent 工作流、聊天、测验、报告learning.py学习会话、讲解生成、互动问答、章节总结quizzes.py测验创建/提交/评分records.py学习进度、错题、薄弱点、统计、导出books.py书源搜索/导入deps.py依赖注入(JWT 认证获取当前用户)Service 业务层 backend/services/(11个文件):文件核心功能auth_service.pybcrypt 密码哈希、JWT 令牌生成、注册/登录user_service.py用户资料查询、教学风格更新material_service.py资料 CRUD、文件解析调度、ChromaDB 清理project_service.py项目 CRUD、资料关联、活动记录、级联删除agent_service.py核心:Agent 多阶段工作流(研究→大纲→方案→授课)learning_service.py学习会话管理、讲解生成、互动问答、进度导航quiz_service.py测验创建/提交、AI 评分、掌握度计算、错题记录project_quiz_service.py项目级测验生成/评分、学习报告(雷达图+薄弱点)record_service.py学习记录查询、错题重做、薄弱点分析、数据导出chat_service.py项目聊天历史、SSE 流式对话(RAG 上下文+教学风格)book_service.py多源书籍搜索(并行)、书籍下载导入(CDN 回退)AI 模块 backend/ai/(7个文件):文件功能maas_client.py统一 MaaS API 客户端(DeepSeek + Qwen-VL)circuit_breaker.py熔断器模式,AI 通道故障自动切换agent_prompts.pyAgent 工作流 Prompt 模板prompt_manager.pyPrompt 管理器edu_agent.py教育 Agent 编排stream_handler.pySSE 流式响应处理content_filter.py内容安全检查Knowledge 知识库模块 backend/knowledge/(5个文件):文件功能kb_client.pyChromaDB 向量知识库客户端local_rag.py本地 RAG 检索增强生成text_extractor.py文档文本提取text_splitter.py文本分块处理parse_scheduler.py文档解析调度Models 数据模型层 backend/models/(21个模型类):核心模型关系链:User → Material → Project → ProjectMaterial → AgentSession → ProjectOutline → TeachingPlan → LessonDesign,以及 Project → Quiz → Question → AnswerRecord → MistakeRecord,Outline → Chapter → Topic → LearningStep → Interaction,Chapter → Mastery。枚举定义 backend/models/enums.py:MaterialFormat:PDF/PPTX/DOCX/TXT/EPUB/PNG/JPG/JPEG/WEBP/BMPMaterialStatus:PARSING/READY/FAILEDSourceType:LOCAL/EXTERNAL/UPLOAD/ZLIBRARY/GUTENBERG/LIBRARY_GENESISTeachingStyle:SOCRATIC/METAPHOR/STRICT/ENCOURAGINGQuizStatus:IN_PROGRESS/COMPLETEDQuestionType:CHOICE/FILL/SHORT_ANSWER/TRUE_FALSE/ESSAYMasteryTrend:IMPROVING/STABLE/DECLININGTopicStatus:主题状态(DRAFT/LEARNING/COMPLETED)ChapterStatus:章节状态(NOT_STARTED/IN_PROGRESS/COMPLETED)StepStatus:学习步骤状态(PENDING/COMPLETED)InteractionType:互动类型(QUESTION/FREE_ASK)3.2.2 前端代码结构讲解路由配置 frontend/src/App.tsx:/login → 登录/注册页/learnspace → 学习空间(默认首页,项目列表)/library → 资料库/booksource → 书源广场/profile → 个人资料/project/:id → 项目详情(Tab导航)页面组件 frontend/src/pages/(6个页面目录):页面功能Auth/登录/注册页面LearnSpace/学习空间主页(项目列表+新建项目)Library/资料库页面(上传/管理学习资料)BookSourcePlaza/书源广场(搜索/导入在线书籍)Profile/个人资料页面(教学风格设置)ProjectDetail/项目详情(GuidedLearning/LearningOutline/QuizCenter/LearningReport)核心组件 frontend/src/components/(8个组件目录):组件功能Layout/AppLayout.tsx应用主布局(侧边栏+内容区)ChatWidget/聊天组件(消息列表+输入框+SSE流式显示)QuizRenderer/测验渲染器(5种题型交互式答题)BookSourceModal/书源搜索弹窗OutlineView/学习大纲视图(章节列表+确认/重新生成)NewProjectDrawer/新建项目抽屉StyleModal/教学风格选择弹窗MasteryRadar/掌握度雷达图组件状态管理 frontend/src/store/(7个 Zustand Store):Store管理状态authStoretoken/userId/username/loading/errormaterialStorematerials[]/loadingprojectStoreprojects[]/currentProjectId/selectedTablearningStorechatMessages[]/isStreaming/agentPhase/agentProgress/outlinequizStorequestions[]/answers{}/results/currentQuizIdrecordStorereport(radarData/weakPoints/suggestions)userStoreprofile/teachingStyle/styleModalVisibleAPI 客户端 frontend/src/services/:apiClient.ts:axios 实例,自动注入 Bearer Token,401 时清除本地 TokensseClient.ts:SSE EventSource 封装,支持自定义事件监听api/index.ts:所有 API 函数集合(Auth/User/Materials/Projects/Books/Chat/Agent/Quiz/Report)3.3 环境配置与项目启动3.3.1 配置环境变量根据 backend/.env.example 创建 backend/.env,需配置以下变量:DATABASE_URL / REDIS_URL:数据库与缓存连接MAAS_API_KEY / MAAS_API_URL / MAAS_MODEL:DeepSeek API 密钥与地址QWEN_API_KEY / QWEN_API_URL / QWEN_MODEL:Qwen-VL API 密钥与地址JWT_SECRET:JWT 签名密钥注意:请将 API Key 替换为步骤 2.5 中获取的实际密钥。3.3.2 Docker Compose 一键启动在码道终端中,输入以下命令启动所有服务并验证:docker compose up -ddocker compose pscurl http://localhost:8000/health预期返回 {"status":"ok"}。终端:Docker Compose启动并验证Docker Compose 将自动启动 4 个服务:服务端口说明backend8000FastAPI 后端frontend80React 前端(Nginx)db15432PostgreSQL 16redis16379Redis 7启动后,后端 Dockerfile 会自动执行: 1. 使用 tomllib 从 pyproject.toml 提取依赖列表并安装 2. 等待 PostgreSQL 就绪 3. 运行 Alembic 数据库迁移(自动创建所有表) 4. 启动 Uvicorn 服务在浏览器中访问 http://localhost 验证前端页面是否正常加载。浏览器:EduGuide前端首页加载成功查看后端日志确认数据库迁移与书源健康检查:docker compose logs backend3.4 核心功能验证3.4.1 用户注册与登录验证在码道对话框中,输入以下提示词:请测试验证用户注册和登录功能:1. 注册用户:用户名 testuser,邮箱 test@example.com,密码 test1234562. 使用注册的用户登录,获取 JWT 令牌3. 使用令牌获取用户资料码道将调用 API 接口完成注册→登录→获取资料的完整流程验证。码道对话:测试注册登录接口浏览器:EduGuide登录页面3.4.2 学习资料上传与解析验证在码道对话框中,输入以下提示词:请测试验证学习资料上传功能:1. 使用上一步获取的 JWT 令牌2. 上传一个 PDF 文件到资料库3. 查看资料列表,确认资料状态从 parsing 变为 ready4. 查看资料详情,确认解析成功注意:资料上传后系统会自动解析并构建向量知识库,首次加载 sentence-transformers 嵌入模型需要下载约 90MB 模型文件。浏览器:资料库页面上传PDF浏览器:资料解析状态从parsing变为ready3.4.3 在线书源搜索验证在码道对话框中,输入以下提示词:请测试验证书源搜索功能:1. 搜索关键词 "machine learning"2. 确认返回搜索结果(书名、作者、来源)3. 选择一本书导入到资料库浏览器:书源广场搜索结果3.4.4 项目创建与 Agent 工作流验证在码道对话框中,输入以下提示词:请测试验证项目学习功能:1. 创建学习项目 "机器学习入门",关联之前上传的资料2. 启动 Agent 工作流,观察 SSE 流式输出3. 确认 Agent 阶段从 idle → researching → outline_generated4. 确认学习大纲生成成功5. 确认大纲后,观察 Agent 进入 planning → ready 阶段6. 开始章节授课,观察 SSE 流式讲解输出这是系统最核心的功能验证,Agent 工作流完整阶段为:idle → researching → outline_generating → outline_generated → planning → ready → teaching → complete浏览器:学习空间-项目列表浏览器:Agent工作流启动-SSE流式输出浏览器:AI生成学习大纲3.4.5 互动问答与教学风格验证在码道对话框中,输入以下提示词:请测试验证互动问答功能:1. 在授课过程中,发送自由提问 "请解释一下梯度下降的原理"2. 观察 AI 流式回答3. 切换教学风格为 "metaphor"(隐喻式)4. 再次提问,观察回答风格变化浏览器:AI互动授课-SSE流式讲解浏览器:教学风格切换3.4.6 测验生成与评分验证在码道对话框中,输入以下提示词:请测试验证测验功能:1. 为第一章生成测验2. 查看生成的题目(确认包含多种题型)3. 提交答题结果4. 查看 AI 评分结果与评语5. 查看学习报告,确认雷达图数据与薄弱点分析浏览器:AI生成测验-多种题型浏览器:测验评分结果3.4.7 错题追踪与学习记录验证在码道对话框中,输入以下提示词:请测试验证学习记录功能:1. 查看学习进度概览2. 查看错题列表3. 重做一道错题4. 查看薄弱知识点分析5. 查看学习统计数据6. 导出学习数据浏览器:学习报告-掌握度雷达图浏览器:错题列表与薄弱知识点3.5 测试验证3.5.1 后端测试在码道终端中,运行后端测试(108个测试用例):docker compose exec backend python -m pytest -v --tb=short终端:后端108个测试用例全部通过关键测试文件:文件测试内容tests/test_auth_service.py认证服务测试tests/test_api_auth.py认证 API 测试tests/test_material_service.py资料服务测试tests/test_project_service.py项目服务测试tests/test_api_projects.py项目 API 测试tests/test_chat_service.py聊天服务测试tests/test_quiz_service.py测验服务测试3.5.2 前端测试在码道终端中,运行前端测试(50个测试用例):cd frontend && npm test终端:前端50个测试用例全部通过关键测试文件:文件测试内容store/__tests__/authStore.test.ts认证 Store 测试store/__tests__/materialStore.test.ts资料 Store 测试store/__tests__/projectStore.test.ts项目 Store 测试store/__tests__/learningStore.test.ts学习 Store 测试store/__tests__/quizStore.test.ts测验 Store 测试components/ChatWidget/__tests__/ChatWidget.test.tsx聊天组件测试components/QuizRenderer/__tests__/QuizRenderer.test.tsx测验渲染器测试3.5.3 前端类型检查docker compose exec frontend npx tsc -b注意:tsc -b 在 Docker 中捕获的类型错误比 vite dev 更严格,务必在推送前端代码前运行。至此,EduGuide 应用的构建与验证已全部完成。四、释放资源本案例所有服务均在本地 Docker 环境中运行,不涉及华为云付费资源。完成后在终端执行以下命令释放本地资源:docker compose down五、扩展资料说明如您在案例实操过程中遇到问题或有改进建议,欢迎到论坛帖评论区反馈,我们会及时响应处理。扩展学习资料: - FastAPI 官方文档:https://fastapi.tiangolo.com/ - React 官方文档:https://react.dev/ - Ant Design 组件库:https://ant.design/ - Docker Compose 文档:https://docs.docker.com/compose/ - 华为云码道(CodeArts)代码智能体:https://codearts.huaweicloud.com/案例关键亮点总结:维度说明构建方向学习智能助教、个性化学习计划核心创新双层架构(资料库+学习空间)+ AI Agent 多阶段工作流 + 双 AI 通道熔断技术特色FastAPI async + SQLAlchemy 2.0 + ChromaDB RAG + SSE 流式 + React 19码道价值代码结构理解、环境配置、功能验证、扩展开发全流程赋能源码获取GitHub 克隆到本地,码道直接分析已有代码进行改造调试 
  • [高校训练营] 智车云——基于华为云码道的汽车经销商智能经营平台
    1 概述1.1 案例介绍汽车经销商的业务链路横跨车辆采购、库存周转、客户跟进、报价审批、订单履约、金融保险、收款结算和车辆交付。传统系统通常按部门分散建设,容易出现数据重复录入、库存状态不一致、利润口径不统一、客户跟进遗漏及交付检查不完整等问题。智车云以 VIN 为车辆唯一主线、以客户旅程为成交主线、以单车利润为经营主线,构建覆盖 DMS、CRM、F&I 和 BI 的一体化汽车经销商智能经营平台。系统提供经营驾驶舱、车辆库存、车型商品、客户线索、报价管理、销售订单、金融保险、收款结算、交付中心、经营分析和 AI 助手等功能,实现从车辆入库、客户跟进、成交签约到交付复盘的完整业务闭环。1.2 建设目标建立以 VIN 为核心的车辆全生命周期台账。实现客户线索分级、跟进计划、公海流转和销售漏斗管理。通过利润防火墙控制低价报价和负利润订单。通过订单状态机规范审批、签约、收款、结算和交付流程。使用幂等机制避免重复创建订单和重复收款。通过标准化检查清单降低车辆交付遗漏风险。通过经营驾驶舱和 AI 助手辅助管理者发现经营异常。借助华为云码道代码智能体提高开发、测试和调试效率。1.3 适用对象本案例适合企业开发者、高校学生、个人开发者以及需要建设汽车经销商 DMS、CRM、进销存或多门店经营系统的研发人员学习和实践。2 系统架构设计2.1 总体架构智车云采用前后端分离和容器化部署架构:前端采用 Vue 3、TypeScript、Element Plus 和 ECharts。后端采用 Java 17、Spring Boot 3、Spring Data JPA 和 Sa-Token。PostgreSQL 负责保存交易和业务数据。Redis 负责登录会话、缓存和临时状态。RabbitMQ 负责领域事件和异步通知。MinIO 负责附件及影像对象存储。Flyway 负责数据库结构和演示数据版本管理。Nginx 负责前端静态资源和 API 反向代理。Docker Compose 负责六个服务的一键编排和启动。2.2 核心业务模块经营驾驶舱:展示在库车辆、新增线索、待交付订单、超期库存、销售漏斗和 AI 经营建议。车辆库存:管理 VIN、库存状态、库龄、采购成本、总成本、合格证和库位信息。客户线索:管理客户来源、意向等级、跟进记录、下一步计划和公海认领。报价与订单:完成报价、利润计算、价格审批、订单签约、结算和交付状态推进。财务结算:管理金融申请、保险、收款、退款、发票和单车利润。交付中心:通过标准化检查清单控制交付过程。经营分析:使用销售漏斗和库存分布图辅助经营决策。AI 助手:支持自然语言查询库存、线索、销售和交付数据,并生成库存优化建议。3 使用华为云码道辅助开发及调试3.1 项目理解与任务拆解项目开发初期,首先使用华为云码道代码智能体读取 README、后端依赖、前端依赖、数据库迁移脚本和 Docker Compose 配置,梳理系统架构、业务域、状态机、金额计算及多租户边界。智能体将复杂需求拆分为“领域实体—数据访问—业务服务—接口—前端页面—自动化测试”等可验证任务,避免一次性生成大量缺少业务约束的代码。3.2 跨文件代码开发在车辆、客户、报价、订单和交付等模块开发中,码道代码智能体辅助完成:Java 领域实体、Repository、Service 和 Controller 的生成与修改。Vue 3 页面、API 封装和 TypeScript 类型定义。前后端字段和统一响应结构校对。车辆与订单状态转换规则补充。报价利润计算和审批前置条件实现。Docker Compose、Nginx 和应用配置检查。3.3 测试补齐与问题定位项目使用码道代码智能体辅助生成和完善 JUnit 5 单元测试,覆盖车辆状态机、订单状态机、客户公海、报价利润和库存成本等关键规则。在前后端联调过程中,将浏览器 Network 请求、响应数据和后端异常日志提供给智能体,定位并解决了 API 路径重复、鉴权头缺失、分页结构不一致、枚举显示异常以及图表数据映射错误等问题。3.4 容器化调试码道代码智能体辅助检查 PostgreSQL、Redis、RabbitMQ、MinIO、后端和前端之间的容器依赖及网络配置,并按照“基础设施—数据库迁移—后端服务—代理转发—前端页面”的顺序定位启动问题。4 核心技术难点与解决思路4.1 车辆与订单状态一致性系统使用显式状态机集中维护车辆和订单的合法转换规则,禁止页面或接口直接修改状态。业务服务在事务中同时更新业务对象和状态日志,防止车辆越级交付、订单终态回退或取消后未释放车辆。4.2 金额精度与利润防火墙交易金额统一使用 PostgreSQL DECIMAL 和 Java BigDecimal,避免浮点误差。系统集中计算车辆成交总额、采购成本、资金占用成本、附加收入、佣金和综合利润。当报价低于保护价或综合利润为负时,系统自动标记风险,并要求填写降价原因和进入价格审批流程。4.3 多租户与数据权限核心业务对象包含 tenantId 和 storeId,通过租户、门店、角色和数据范围控制用户可见数据。Sa-Token 负责登录认证,服务层校验业务对象归属,关键操作同时记录审计日志。4.4 幂等与重复请求订单和收款接口使用 idempotencyKey 识别重复请求,并结合数据库唯一约束和事务避免重复创建订单、重复收款及金额重复累计。4.5 消息可靠性车辆和订单状态变化通过 RabbitMQ 发布领域事件。普通通知发送失败时不阻塞主交易;对于生产环境中的关键事件,可进一步使用 Outbox、消费幂等、失败重试和死信队列提高可靠性。4.6 演示数据设计演示数据不是简单随机生成,而是围绕真实业务故事构建:超过 90 天的车辆触发库存预警。高意向但长期未跟进的客户触发跟进建议。低价报价触发利润审批。待交付订单展示检查清单。已完成订单支撑销售漏斗和经营分析。5 构建与运行5.1 环境要求JDK 17Maven 3.8+Node.js 20Docker DesktopDocker Compose v2建议配置为 4 核 CPU、8 GB 内存5.2 启动项目cd deploy/docker docker compose up -d --build ‘’‘ #6 demo视频链接 https://www.bilibili.com/video/BV1dFg96MEPL/?vd_source=ac4e7ae16e8e5366bd0510ab13603e8d
  • [高校训练营] 基于华为云码道(CodeArts)代码智能体的KPL赛段晋级推演工具开发实践
    案例介绍本项目为基于 Vue3+TS 开发的 KPL 赛段晋级推演 Web 工具,内置官方完整赛制规则,适配 18 支战队两阶段常规赛、卡位赛及季后赛对阵链路。支持录入各轮对战大小场战绩,自动核算积分,遍历剩余赛程计算战队组别升降、出线概率。搭载可视化图表展示积分榜单与晋级形势,可完整模拟胜者组、败者组淘汰赛进程。数据本地持久化存储,支持多分组独立运算、推演记录管理,为电竞爱好者提供赛事形势量化分析工具。案例内容一、概述1.1 案例介绍KPL 职业联赛受众规模持续增长,大量电竞爱好者热衷于分析赛事积分走势、推演战队晋级形势,但目前缺少贴合官方完整赛制、可自主录入赛果的可视化推演工具,爱好者仅能依靠人工统计积分,推演门槛高、容易出错。 市场潜力:国内电竞用户规模持续攀升,电竞赛事数据推演、观赛分析类工具需求持续上涨。 用户习惯:赛事周期内大量观众热衷于讨论小组排名、卡位赛形势、季后赛出线可能性,具备较强的数据讨论与分享需求。 场景拓展:满足普通观众观赛推演、赛事博主数据复盘、电竞爱好者赛前形势模拟等多元场景。 依托华为云码道(CodeArts)代码智能体快速生成 Vue3+TS+Vite 全套前端工程,打造 KPL 赛段晋级推演 Web 应用。项目内置官方完整赛制规则,适配 18 支战队常规赛分组循环、卡位赛升降级、胜者组与败者组完整季后赛链路;支持录入对局大小场战绩、自动核算积分、批量推演战队晋级概率,搭配可视化图表展示积分榜单与晋级形势,数据本地持久化存储,为电竞爱好者提供量化赛事分析工具。案例技术选型:华为云码道(CodeArts)代码智能体是基于智能生成、智能问答两大核心能力构建起一套全方位、多层次的智能开发体系。在智能生成方面,它能够依据开发者输入的需求描述,准确且高效地生成高质量代码;智能问答功能则如同开发者身边的专属技术顾问。1.2 适用对象个人开发者 高校学生 电竞数据爱好者1.3 案例时间本案例总时长预计 180 分钟。1.4 案例流程说明: 本地安装华为云码道(CodeArts)代码智能体; 通过码道规范驱动模式开发 KPL 赛段晋级推演 Web 应用。1.5 资源总览本案例预计花费 0 元。  二、基础环境与资源准备2.1 华为云码道安装部署本案例基于华为云码道代码智能体完成开发改造,案例开始前请按照以下两步操作开通并使用工具:2.1.1 一键开通华为云码道体验版2.1.2 AI IDE 华为云码道安装部署参考案例《AI IDE 华为云码道(CodeArts)代码智能体安装部署》完成 Windows 版 AI IDE 华为云码道(CodeArts)代码智能体安装部署。三、通过码道分阶段搭建 KPL 赛段晋级推演 Web 应用3.1 通过规范驱动模式创建需求文档首先我们进入 AI IDE 后点击码道对话框的 规范驱动模式 (Spec-Driven Mode):根据 需求规格设计 -> 实现方案创建 -> 编码任务规划 -> 任务执行 进行项目开发。3.1.1 需求规格设计接着我们在码道对话框输入以下提示词,让码道进行需求规格说明书的创建   此时,码道会根据步骤首先创建需求规格说明书。这时如果我们对项目有要求,可以对 spec.md 需求规格说明书文件进行编辑。 我们可以把需求内容复制并粘贴进 spec.md 文件内并进行保存。保存过后,我们点击码道对话框的开始实现方案创建。3.1.2 实现方案设计可以看到,码道正在根据我们给出的需求规格文档设计实现方案文档。在创建完成后,我们可以在左侧找到 design.md 实现方案设计文档进行查看,如果有需要调整的地方可以直接进行修改,无问题后点击 全部接受 -> 开始编码任务。3.1.3 编码任务规划在实现方案设计书创建完成后,我们可以在左侧找到 tasks.md 编码任务文档进行查看,如果有需要调整的地方可以直接进行修改,无问题后点击 全部接受 -> 开始任务执行。生成完毕后我们可以看到相应的任务执行顺序。3.1.4 任务执行这时我们可以任意的去修改需求规格设计、实现方案创建、编码任务规划,在以上设计书都完成的前提下,我们可以根据码道提示进行编码任务。可以看到,码道正在进行编码任务。当编码任务全部完成后我们可以点开左侧文件进行查看,如无问题,在码道对话框点击 全部接受。四、启动项目并反馈可能出现的问题在编码任务完成后,我们可以根据启动说明或在码道对话框输入提示词启动项目,此时码道会提示我们如何启动,根据指引进行项目启动即可。注意:在启动项目中或项目运行中可能会出现一些错误,遇到问题的时候我们通过自然语言描述,或者截图的方式把错误直接反馈给码道,让码道帮我们解决就可以了。当我们成功启动后可以看到刚刚创建好的应用了! 五、反馈改进建议如您在案例实操过程中遇到问题或有改进建议,可以到开发者论坛评论区反馈即可,我们会及时响应处理,谢谢!源码仓库地址:https://github.com/Aria1102/HuaWeiProject
  • [问题求助] 执行完毕后不显示Diff视图和“全部接收/全部拒绝”按钮了(v26.6.1版本)
    请教一下各位大佬,我从v26.3.4版本升级到v26.6.1版本,发现修改.m文件后不再弹出Diff视图和“全部接收/全部拒绝”按钮,但是修改.py文件还有。请问这个现象是bug吗,后续会不会修复?我要不要退回v26.3.4版本?
  • [热门活动] SORT / LAB 排序算法可视化实验室:使用 CodeArts Agent 完成设计、开发与验证
    SORT / LAB 排序算法可视化实验室:使用 CodeArts Agent 完成设计、开发与验证案例介绍:本案例采用华为云码道(CodeArts)代码智能体作为核心开发工具,以算法过程可观察、实验条件可复现、学习效果可验证为目标,按照 SDD(Spec-Driven Development)规范驱动方式完成 SORT / LAB 排序算法可视化实验室。项目将 45 种排序算法统一为事件流,并结合 Canvas、Web Worker、双算法同步对比和教学工作台,覆盖需求分析、系统设计、功能实现和自动化验证。案例属性内容案例类型AI 辅助开发 / Web 前端 / 算法教学难度中级建议用时150~180 分钟核心工具华为云码道(CodeArts)代码智能体技术栈Next.js 16、React 19、TypeScript 5.9、Canvas、Web Worker、vinext、Vite、Cloudflare Worker最终成果45 种算法、9 种数据分布、5 条学习路径、130 项自动化测试案例展示SORT / LAB 排序算法可视化实验室一、概述1.1 案例介绍排序算法是数据结构与算法课程的重要基础。传统教材中的静态数组、箭头和伪代码难以连续呈现读取、比较、交换、写入和归位等状态变化。简单动画通常只展示执行结果,缺少操作原因说明和同源数据对比。本案例对话华为云码道(CodeArts)代码智能体,从学习场景出发,将需求逐层收敛为四个建设目标:过程展示:通过 Canvas、状态色、活动区间、Pivot、辅助存储区和算法专用结构视图呈现排序过程。实验控制:支持播放、暂停、语义单步、微操作单步、上一步、时间轴、对数速度和安全上限。同源对比:双算法使用同一份输入,可按微操作、语义阶段或确认进度同步,并分别统计读取、比较、写入与交换次数。教学组织:每种算法配置目标、概念、不变量、四行伪代码、角色、教学预设和检查点,提供讲解、预测、实验、课程与教师工具五类教学活动。最终完成的 SORT / LAB 是一套可扩展、可复现、可验证的排序算法教学实验室。1.2 适用对象个人开发者;企业开发者;高校学生与教师;希望了解 CodeArts Agent 如何参与需求、设计、实现和测试流程的开发团队。1.3 案例时间建议总用时为 150~180 分钟。阶段建议用时主要成果需求定义20 分钟产品目标、用户场景、验收标准架构设计25 分钟事件模型、播放器状态机、模块边界核心实现60 分钟算法引擎、Canvas、控制器、对比模式教学扩展30 分钟教学内容、实验、课程路线、教师工具验证交付25 分钟构建、130 项测试、Lint、类型检查、16:9 截图1.4 案例流程图 1:案例实施流程。六个编号与下方流程说明逐项对应。说明:在 CodeArts 中打开项目,按照目标与约束、方案确认、实现、验收四个步骤建立 Agent 协作方式;将排序算法抽象为统一的 SortStep 事件协议和播放器状态机;分批接入 45 种算法、9 种数据分布及其输入约束;构建 Canvas 主视图、专用辅助视图和双算法同步对比;增加教学工作台、高速 Worker、历史回放和安全保护;执行构建、自动化测试、类型检查与浏览器验收,完成交付。开发过程按阶段迭代。每轮先说明需要解决的问题和必须保持的边界,Agent 阅读相关代码后给出方案。我确认数据模型和交互取舍后,再让 Agent 修改代码。项目脚本和浏览器验收结果用于判断本轮是否完成。测试失败、边界输入或视觉问题会作为下一轮输入继续交给 Agent。1.5 资源总览本案例使用 CodeArts 体验版和本地开发环境,预计花费 0 元,不创建 ECS、CCE、EIP 等付费云资源。资源名称规格单价(元)华为云码道(CodeArts)代码智能体通用体验版免费本地开发环境arm64,Node.js v25.7.0,npm 11.10.1免费Chromium/Chrome支持 Canvas、Web Worker 与 ResizeObserver免费二、环境和资源准备2.1 准备 CodeArts 与项目工作区打开 CodeArts,将 sort-visualization 作为项目目录。项目代码、依赖安装、测试和案例截图均保存在该目录中。本案例不需要创建数据库、云服务器或容器集群。开始前确认 CodeArts 能够读取项目文件,并保留一份未修改的源码版本,便于对照 Agent 的修改范围。2.2 准备本地运行环境项目要求 Node.js 版本不低于 22.13.0。本次验证环境如下:项目版本Node.jsv25.7.0npm11.10.1系统架构arm64使用以下命令确认环境:node --version npm --version 浏览器建议使用较新的 Chromium 或 Chrome,以确保 Canvas、Web Worker 和 ResizeObserver 正常工作。2.3 准备验收标准在让智能体大规模生成代码之前,先确定以下质量门槛:45 种算法均能通过事件回放得到正确升序结果;同一种子和数据分布必须生成相同输入;算法声明的重复值、负数、2 的幂和辅助区能力必须与实现一致;播放器支持暂停、单步、回退和时间轴;高速播放不能长时间阻塞主线程;极慢算法必须有规模和最大步数保护;每种算法必须有完整教学内容;页面支持桌面、平板、手机和减少动画偏好;案例成果截图统一采用 16:9。三、构建 SORT / LAB 排序算法可视化实验室3.1 打开项目并建立 Agent 协作方式在 CodeArts 中打开项目目录后,先让 Agent 阅读 README.md、package.json、app/ 和 tests/,并说明现有工程的入口、依赖、数据流和风险。第一轮执行代码分析,不修改文件:请先阅读当前项目,不要修改文件。 请说明: 1. 页面入口、算法逻辑、状态管理和测试分别位于哪里; 2. 当前实现中最适合继续扩展的接口是什么; 3. 如果要支持播放、回退、对比和教学,哪些状态必须统一建模; 4. 哪些算法或输入容易造成性能和正确性风险。 最后给出分阶段实施建议,等我确认后再开始修改。Agent 完成代码库分析后,我把协作约定固定下来:每轮只解决一个明确主题,先说明方案和影响文件;算法注册表和事件协议作为播放器、教学内容与测试的共同依据;不绕过现有工程结构,不覆盖与本轮无关的改动;完成功能后同时补充测试,并运行对应的项目脚本;对视觉结果无法仅凭代码判断时,启动页面进行浏览器验收;如果测试失败,先解释原因,再根据失败信息修正。这样做的好处是,我始终掌握产品边界和验收口径,Agent 则持续掌握具体代码上下文。后续每个阶段都沿用这套协作方式。3.2 部署并运行项目代码3.2.1 项目结构sort-visualization/ ├── app/ │ ├── page.tsx # 页面、播放器状态机、Canvas 与教学交互 │ ├── sort-engine.ts # 算法定义、事件协议、数据分布 │ ├── sort.worker.ts # 高速事件预取 Worker │ ├── teaching-content.ts # 45 种算法的教学内容与课程路径 │ └── globals.css # 视觉系统与响应式布局 ├── tests/ │ ├── algorithms.test.mjs # 算法、事件、教学内容和专用视图测试 │ └── rendered-html.test.mjs # 服务端渲染冒烟测试 ├── output/playwright/ # 1600×900 案例截图 ├── docs/ # 需求、设计、验证与案例文档 ├── worker/index.ts # Cloudflare Worker 入口 └── package.json # 工程脚本和依赖3.2.2 准备源码案例源码已经置于 CodeArts 打开的项目工作区,因此无需重复下载。确认项目根目录包含 package.json、package-lock.json、app/ 和 tests/ 后,再继续安装依赖。如果从案例附件获取源码,请先将源码完整解压到 sort-visualization 目录,并使用 CodeArts 打开该目录;不要只打开其中的 app/ 子目录,否则依赖脚本和测试文件无法被正确识别。3.2.3 安装依赖在项目根目录执行:npm install package.json 已锁定 Next.js、React、TypeScript、Vite、vinext 和测试工具版本,package-lock.json 用于保证安装结果可复现。3.2.4 运行调试执行以下命令启动开发服务器:npm run dev终端输出本地访问地址后,在浏览器中打开页面。开发过程中保持终端运行,修改代码后页面会自动刷新。图 2:项目成功启动后的主操作台。左侧为算法属性和伪代码,中间为 Canvas 与时间轴,右侧实时统计读取、比较、写入和交换次数。3.3 对话码道:将排序动画需求整理为教学实验室规格目标将排序可视化页面需求整理为可实现、可追踪、可验收的产品规格。我的输入请构建一个排序算法可视化实验室,不要只实现单一算法动画。 核心要求: 1. 覆盖基础、高效、非比较、特色和极慢排序算法; 2. 输入可使用随机种子复现,并支持多种典型数据分布; 3. 支持播放、暂停、单步、上一步、时间轴和速度调整; 4. 展示读取、比较、写入、交换和已确认位置; 5. 支持两个算法使用相同输入同步对比; 6. 加入教学目标、伪代码、不变量、练习和课程路径; 7. 对极慢算法和高数据规模提供性能保护; 8. 使用 TypeScript,完成自动化测试、Lint 和类型检查。 先分析需求和架构,再开始实现。每个阶段完成后给出验证结果。Agent 的反馈与我的确认这一轮要求 Agent 先完成范围分析。Agent 将需求拆成产品层、算法层、事件协议层、播放控制层、可视化层、教学层和验证层,并指出算法数量和播放器通用性会直接影响后续维护成本。我确认了两个关键取舍:第一,45 种算法统一使用同一事件协议,避免分别维护播放器;第二,教学功能必须与播放器状态联动,避免使用与执行过程分离的静态说明。在这两个前提下,双方确定了最终产品范围:项目指标数量或结果排序算法45 种算法分类5 类数据分布9 种教学路径5 条播放速度1~4096 步/秒安全上限5 万 / 25 万 / 100 万步自动化测试130 项3.4 对话码道:设计统一事件协议背景45 种算法的内部机制差异很大:归并排序需要辅助数组,计数与桶排序需要分组结构,树排序需要节点关系,珠排序则需要模拟放置、重力和读出阶段。如果每种算法直接操作页面,播放器、回放、统计和测试都会重复实现。我的输入请把排序逻辑与界面渲染解耦。 每个算法使用 Generator 产生统一的语义事件,事件至少覆盖: read、compare、swap、write、auxWrite、mark、pivot、range、 bucket、code、visual 和 done。 页面只消费事件并更新状态,不允许算法直接操作 DOM 或 Canvas。 同一事件流必须同时服务于动画、统计、回放、教学解释和自动化测试。协作过程Agent 首先提出以 Generator 产出事件、Reducer 消费事件的方案。我在审查时补充了两个容易遗漏的要求:重复值需要独立身份以观察稳定性,树、桶、排序网络和珠排序不能被强行压缩成普通数组动画。Agent 据此扩展 SortStep 联合类型,并让 playerReducer 统一处理主数组、辅助区、活动范围、专用视图和统计值。完成首版后,我通过单步、交换和归并写回三个场景检查事件语义,再让 Agent 处理身份传递与辅助区清理的边界情况。核心设计图 3:排序算法、统一事件协议、播放器状态机与展示层之间的关系。关键代码:统一事件协议app/sort-engine.ts 使用 TypeScript 联合类型描述排序操作。下面节选其中的核心事件;算法只负责产生事件,不直接操作页面:export type SortStep = | { type: "read"; index: number; message?: string } | { type: "compare"; indices: [number, number]; message?: string } | { type: "swap"; indices: [number, number]; message?: string } | { type: "write"; index: number; value: number; message?: string } | { type: "auxWrite"; index: number; value: number; message?: string } | { type: "mark"; indices: number[]; message?: string } | { type: "pivot"; index: number | null; message?: string } | { type: "bucket"; bucket: number; values: number[]; message?: string } | { type: "code"; line: number; variables?: Record<string, string | number>; message?: string; } | { type: "visual"; state: AlgorithmVisualState; message?: string } | { type: "done"; message?: string }; export type SortGenerator = Generator<SortStep, void, unknown>; 算法注册表则集中声明复杂度、稳定性、输入约束、辅助视图和 Generator 入口,使页面与测试可以读取同一份元数据。事件模型带来了四项关键收益:算法与视图解耦:新算法只需注册元数据和 Generator,不必重写播放器。播放能力复用:暂停、单步、回退和时间轴对全部算法生效。统计口径统一:读取、比较、写入和交换来自同一事件源。验证范围统一:测试检查最终结果、事件下标、辅助存储和完成状态。3.5 对话码道:构建算法与数据实验能力我的输入请在统一事件协议上扩展算法注册表: - 分类覆盖基础排序、高效排序、非比较排序、特色排序和极慢排序; - 每个算法声明中英文名、复杂度、空间、稳定性、是否原地、 最大规模、输入约束和辅助视图类型; - 数据分布覆盖随机、逆序、有序、接近有序、少量不同值、 山峰、锯齿、分段有序和含负数; - 所有随机数据由 seed 驱动; - 不支持重复值、负数或非 2 的幂规模时,界面必须前置提示或自动调整; - 极慢算法设置小规模默认值与最大事件步数。协作过程这一阶段要求 Agent 以注册表为中心分批接入算法。先完成基础排序并验证事件协议,再扩展高效排序、非比较排序和专用结构,最后处理极慢算法。每批实现后,使用相同的回放器比较标准升序结果。测试发现重复值、负数、辅助区声明和 2 的幂规模等差异后,Agent 同步修正实现或能力元数据,确保界面声明与算法实际能力一致。阶段成果分类数量代表算法基础排序10冒泡、鸡尾酒、选择、插入、梳排序高效排序14归并、快速、堆、内省、TimSort非比较排序7计数、基数、桶、美国旗、珠排序特色排序8煎饼、循环、双调、树、耐心排序极慢排序6Stooge、Slow、Bogo、Bozo、排列、睡眠排序每个算法通过 AlgorithmDefinition 声明能力边界。数据生成由 seed + distribution + size 决定,并可写入 URL,使教师分享的课程链接能够恢复算法、规模、数据形态和教学模式。3.6 对话码道:定义排序过程的视觉语义我的输入请把事件状态映射为清晰的视觉语义: - 未读取、已读取、已确认排序和当前操作使用不同颜色; - 显示活动区间、Pivot、手持值、扫描候选和比较关系; - 归并、基数等算法展示辅助存储区; - 桶、树、锦标赛、排序网络和珠排序使用专用辅助视图; - Canvas 适配高分屏与容器尺寸; - 低速提供平滑位移,高速和大规模时减少过渡以保证性能。协作过程Agent 先根据事件类型建立颜色和图形映射。我在浏览器中检查后,反馈了三个问题:高分屏下线条发虚、容器缩放后画布尺寸不同步、重复值移动时难以辨认来源。Agent 随后补充基于 devicePixelRatio 的渲染、ResizeObserver 自适应和 requestAnimationFrame 插值,并使用元素身份表现真实移动轨迹。我再次用快速排序、归并排序和少量重复值数据进行验收,确认 Pivot、活动区间、辅助存储和稳定性身份均能被观察后,才保留这套视觉语义。快速排序在 72 项同源数据上的运行效果见图 2。画面中的 Pivot、活动区间和实时操作量均由同一事件流驱动。3.7 对话码道:实现可信的双算法对比背景动画结束时间会受到播放速度、设备性能和单步粒度影响。为保证实验条件一致,两个算法必须使用完全相同的输入,并采用明确的同步规则。我的输入请实现双算法同步对比: 1. 两个算法共享同一份初始数据; 2. 支持按微操作数、语义阶段或确认进度同步; 3. 两侧分别显示比较、写入等操作量; 4. 完成后自动给出差异结论; 5. 对比模式仍要支持暂停、单步、回退和时间轴。协作过程Agent 的首个方案按照相同微操作数推进两侧播放器。试用结果表明,一次归并写回操作和一次快速排序比较操作会被视为同等进度,无法表示两种算法的语义差异。因此保留微操作同步,并增加语义阶段和确认进度两种口径。Agent 重构同步控制后,我用快速排序与归并排序在同一随机种子下运行,检查两侧初始数组、统计和历史快照是否独立。确认三种同步方式都能解释其含义后,再加入完成后的操作量结论。图 4:快速排序与归并排序使用相同的 72 项数据,并按语义阶段同步。归并排序的辅助存储区与两侧独立统计同时可见。学习者可以分别比较比较次数、写入次数和最终位置确认进度,避免仅根据动画结束时间判断算法表现。3.8 对话码道:从演示工具扩展为教学工作台我的输入请为 45 种算法分别补充教学内容: - 学习目标、核心概念和循环不变量; - 固定四行、可随事件高亮的伪代码; - 当前操作及其执行原因; - 算法角色、推荐教学数据和检查点题目; - 教学实验、课程路径、术语表和教师工具; - 学习记录保存到本地,课程链接可分享复现。协作过程Agent 最初可以直接在组件中加入讲解文本,但我要求先把教学内容抽成独立配置,因为 45 种算法如果散落在 JSX 中将难以审查和补齐。Agent 因此建立 TeachingContent 契约,把目标、概念、不变量、伪代码、角色、预设和检查点统一组织。我重点审查每种算法的推荐数据是否符合其能力边界,以及检查点是否能够帮助理解算法。检查点内容需要包含推理要求,避免复述页面文字。Agent 根据这些反馈调整内容,并补充完整性测试,保证算法注册表、教学内容和课程路径一一对应。最终教学工作台包含五个页签:逐步讲解:当前操作、原因、不变量、角色、推荐观察与下一课;预测练习:在关键步骤前预测结果,即时反馈并记录正确率;教学实验:稳定性身份追踪和复杂度增长实验;课程路径:五条由浅入深的学习路线;教师工具:全屏、书签、分享链接、减少动画、文本数组和事件日志。图 5:快速排序的教学工作台在同一视图中展示当前操作、执行原因、算法不变量、算法角色和后续课程。3.9 对话码道:兼顾高速运行、回放与安全边界我的输入请检查大规模和高速场景: - 低速保留平滑动画; - 高速播放不要让主线程同步生成全部事件; - 支持历史快照、上一步和时间轴跳转; - 切换算法或重置后,旧任务不能继续写入; - 极慢算法达到限制时安全停止并给出提示; - 根据数组规模限制历史数量,避免内存持续增长。协作过程在大规模高速播放中,我观察到事件生成、状态更新和 Canvas 绘制都集中在主线程。Agent 分析调用链后提出使用 Web Worker 分批预取,并将速度达到 512 步/秒及以上的任务切换到 Worker。Worker 每批预取 2000 个事件,主线程按帧消费并在低水位时继续请求。首次切换算法测试时,我又发现旧任务可能晚到。将这个现象反馈给 Agent 后,它增加 generation 标识隔离过期消息。随后双方继续根据数组规模调整历史快照数量,最终控制在约 220~2400 份,兼顾可回退性与内存占用。关键代码:Worker 分批预取app/sort.worker.ts 只在当前任务的 generation 仍然有效时继续生成事件,并按批次返回主线程:const sendBatch = (generation: number, count: number) => { if (generation !== activeGeneration || !iterator) return; const steps: SortStep[] = []; let done = false; while (steps.length < count && emitted < maximum) { const next = iterator.next(); if (next.done) { done = true; iterator = null; break; } steps.push(next.value); emitted += 1; } self.postMessage({ type: "batch", generation, steps, done }); }; 对于极慢算法,系统同时采用:算法级 maxSize 与推荐默认规模;2 的幂等输入约束自动归一化;5 万、25 万或 100 万步运行上限;达到上限后的 limited 状态和用户提示。3.10 对话码道:建立自动化质量验证我的输入请为项目建立可重复执行的验证流程: 1. 构建生产版本; 2. 回放每一种算法并与标准升序结果比较; 3. 校验事件索引、范围、辅助存储和 done 事件; 4. 校验带种子数据、九种分布、重复值与负数; 5. 校验教学内容、课程路径和专用可视状态; 6. 校验 SSR 页面包含完整产品内容,不含模板占位内容; 7. 执行 ESLint 和 TypeScript 类型检查。协作过程Agent 根据算法注册表生成逐项回放测试,并补充数据分布、重复值、负数、教学契约和专用视图测试。我负责实际运行项目脚本并阅读失败输出,再把失败用例和预期行为交回 Agent 修正。构建、测试、Lint、类型检查和浏览器验收全部通过后,本轮开发任务才完成。算法元数据、教学预设或 SSR 页面结构发生修正后,需要重新执行同一验证流程。最终验证在 2026-07-25 执行:验证命令结果说明npm test通过生产构建成功;130 项测试全部通过,0 失败npm run lint通过ESLint 无错误、无警告输出npx tsc --noEmit通过TypeScript 类型检查通过视觉验收通过3 张成果图均为 1600×900自动化测试验证最终有序结果,并覆盖以下内容:45 种算法的事件回放、元素守恒和完成事件;随机种子的确定性及 9 种数据分布;支持范围内的重复值和负数;45 份教学内容、预设、检查点和 5 条课程路径;珠排序三阶段、真实二叉搜索树和锦标赛路径重赛;服务端渲染的标题、产品壳层和关键控件。四、释放资源4.1 停止本地开发服务器案例操作完成后,在运行 npm run dev 的终端中按 Ctrl+C 停止开发服务器。4.2 资源释放说明本案例未创建 ECS、CCE、EIP、云数据库等计费云资源,无需执行额外的云资源删除操作。项目源码、测试结果和截图均保存在本地工作区,可按需要继续保留。五、扩展资料说明5.1 案例总结本案例通过 CodeArts 智能体完成了需求分析、系统设计、代码实现、测试和浏览器验收。项目建立了以下扩展机制:统一事件协议让异构算法共享播放器、统计、回放和测试;Generator 与 Reducer 把算法执行过程转化为可观察状态;Canvas 和专用辅助结构把抽象数据变化映射为一致视觉语义;同源双算法实验避免以动画速度代替复杂度判断;教学内容配置层让每种算法都拥有目标、不变量、练习和课程归属;Worker、历史上限和极慢算法保护让高吞吐与教学可读性可以共存;自动化测试统一检查算法正确性、事件合法性、教学完整性和 SSR 页面内容。整个项目按照可验证阶段组织 Agent 协作。我负责定义目标、指出风险和判断结果,Agent 负责理解代码、提出实现方案、完成跨文件修改并补齐测试。每轮需要生成可运行的中间成果,后续任务建立在已经验证的实现基础上。5.2 与 Agent 协作的经验先让 Agent 读懂项目,再让它修改。 第一轮只分析入口、依赖、数据流和风险,能够减少对现有结构的误判。提示词同时写目标、约束和验收方式。 双算法对比任务需要明确同源数据、同步口径、独立统计和回放要求。关键产品取舍由人确认。 事件协议、同步语义、教学内容结构等决定长期维护成本,不能仅因为首个方案能运行就直接接受。把失败结果作为下一轮上下文。 向 Agent 提供测试输出、边界输入和浏览器现象,便于定位并修正问题。每轮留下可验证的中间成果。 先稳定事件协议,再扩展算法;先完成播放器,再做对比和教学,避免多个问题互相遮蔽。代码验收和视觉验收分开进行。 自动化测试负责正确性与契约,浏览器验收负责动画语义、布局和教学可读性,两者缺一不可。5.3 当前限制与后续计划学习记录当前保存在浏览器本地,尚未实现账号体系和跨设备同步;视觉回归主要依靠人工验收,可继续加入截图差异测试;复杂度实验侧重趋势观察,尚未建立跨设备性能基准;可继续增加课堂任务模板、实验结果导出和教师端班级数据;可将统一事件协议开放为插件接口,让学习者自行接入新算法。5.4 参考案例AssetMgmt 固定资产管理系统(一):码道搭台,设计筑基AssetMgmt 固定资产管理系统(二):码道领航,落地生根
  • [热门活动] TravelMap 编排—游记一体化平台:基于 CodeArts Agent 的全流程构建实践
    TravelMap 编排—游记一体化平台:基于 CodeArts Agent 的全流程构建实践案例部署链接:https://travelmap.linykweb.top/本案例说明如何在本地环境中使用华为云码道完成需求规格、系统设计、编码、测试和运行,再将通过本地验收的应用部署到华为云。项目内容案例名称TravelMap 编排—游记一体化平台:基于 CodeArts Agent 的全流程构建实践核心工具华为云码道(CodeArts)代码智能体开发模式Idea 探索 + SDD 规范驱动 + TDD 测试驱动 + 人机协同迭代技术栈Next.js 15、React 18、TypeScript strict、tRPC、Prisma、PostgreSQL、Redis、TipTap、高德地图云上资源华为云 ECS、EVS、EIP、OBS、SWR,Docker Compose + Caddy当前演示地址https://travelmap.linykweb.top/项目源码GitCode:qq_26761683/travelmap案例适用人群独立开发者、产品经理、前端/全栈工程师、使用 AI 完成全栈开发、测试和部署的团队一、概述1.1 案例介绍1.1.1 旅行编排与协作问题旅行计划通常散落在许多工具里:灵感收藏在内容社区;地点保存在地图收藏夹;日期和时间写在表格;交通方案散落在聊天记录;同行人通过群聊反复确认;旅行结束后,又需要重新整理素材写游记。这些工具分别提供内容检索、地图查看和笔记记录能力,规划、执行和记录数据之间仍然缺少统一关联。一份多人行程会持续发生时间调整、地点替换、提醒补充和交通变化,群聊和表格无法提供稳定的版本、权限和冲突处理。我提出以下初始目标:构建支持时间编排、地图路线、多人协作和游记复用的旅行计划系统。这个目标仍需补充时间冲突、并发编辑、路线可信度、离线访问、AI 修改权限和数据恢复等要求。我先让华为云码道分析问题和边界,再形成可实现、可验收的产品定义。编码工作在需求、非目标和验收条件明确后开始。1.1.2 最终形成的产品定位经过多轮澄清和迭代,TravelMap 的产品范围确定为编排、协作和游记一体化平台:编排:以日期横轴、时间纵轴组织活动、交通、用餐、休息与组合模块。协作:支持 owner、editor、commenter、viewer 四级角色,以及邀请、在线状态、评注和事件级软锁。执行:提供旅行模式、路线信息、执行状态、离线恢复和版本历史。记录:帖子承载轻量图片内容,游记承载富文本叙事,编排可作为结构化组件插入游记。智能辅助:AI 读取偏好、查询资料、核验地点和路线,但只提交可审查的 Proposal;用户确认后才会应用到正式编排。工程交付:具备测试、迁移、构建、健康检查、对象存储、监控、备份、升级与回滚材料。1.1.3 Agent 开发方法人负责目标、价值和取舍;Agent 负责检索上下文、发现缺口、提出结构化方案。一个真实可交付的项目还必须可解释、可测试、可部署、可回滚,并能让后来者理解为什么这样设计。每个重要阶段都要留下四类证据:发现了什么问题、做出了什么决定、修改了哪些内容、怎样证明修改有效。项目规则、SDD 文档、TDD、类型检查、测试、构建和发布门禁用于确保每次变更可追溯、可验证和可复现。1.2 案例时间本案例从空目录开始,在本地完成应用构建和验证,最后部署单机 Demo。预计总时长约 12–16 小时,可分 2–3 天完成:阶段预计时间本地工具安装与空项目初始化45–60 分钟使用码道形成 Spec、Design 和 Tasks60–90 分钟内容、游记和基础用户功能2–3 小时编排、保存、离线和多人协作3–4 小时AI 规划、导入和可靠性处理2–3 小时本地测试、构建和页面验收1–2 小时华为云部署、验收和维护配置1.5–2 小时实际耗时受网络、依赖下载、数据库配置、第三方服务凭据和读者经验影响。本文提供完整流程和关键实现,读者可以按阶段执行并保存每个质量门结果。1.3 案例流程flowchart LR A["1. 提出旅行协作 Idea"] --> B["2. 码道完善需求与 SDD 规格"] B --> C["3. TDD 实现与反复迭代"] C --> D["4. 规范化代码、文档与质量门"] D --> E["5. 构建镜像并部署华为云"] E --> F["6. 健康检查、业务验收与资源释放"] 说明:提出编排、协作和游记一体化的产品目标;码道在探索阶段分析角色、边界和风险,并形成规格、设计和任务;每个重要实现遵循失败测试、最小完整修改、目标验证和全量验证的顺序;将反复对话中形成的产品决策沉淀为架构、协议、测试、运行手册和证据矩阵;使用 Docker、SWR、ECS、EVS、OBS、EIP 和 Caddy 完成可恢复的单机 Demo 部署;通过健康接口和真实页面展示效果,体验完成后备份数据并释放计费资源。1.4 资源总览资源名称使用阶段本案例用途推荐规格或版本华为云码道(CodeArts)代码智能体本地开发需求分析、代码库理解、编码、测试和部署材料生成本地 IDE/CLI,账号可用版本Git本地开发版本管理2.xNode.js 与 npm本地开发Web、脚本和测试运行时Node.js 20 LTSPostgreSQL本地开发权威业务数据16Redis本地协作验证Pub/Sub、Presence 和软锁7.4Docker Desktop 或 Docker Engine本地验证数据服务和生产镜像验证24+,Compose v2华为云 ECS、EVS、EIP部署与维护运行应用和持久化数据x86,4 vCPU / 8 GiB,100 GiB 数据盘华为云 OBS部署与维护公开媒体与私有数据库备份两个独立桶华为云 SWR部署与维护保存不可变应用镜像账号所在区域私有组织域名与 DNS部署与维护HTTPS 访问已备案域名或合规测试域名二、环境和资源准备本章全部在本地计算机完成。华为云资源从 3.9 节的部署阶段开始使用。2.1 安装本地开发工具从码道下载页安装 CodeArts IDE 或 CLI;在本地码道中登录账号,新建空工作区并选择本机目录;安装 Git 2.x、Node.js 20 LTS、npm、Docker Desktop 或 Docker Engine 24+;PostgreSQL 使用 16 版,Redis 使用 7.4 版;确认本地终端可以执行以下检查:git --version node --version npm --version docker --version docker compose version本案例使用本地文件系统、本地终端和本地浏览器完成开发。数据库可以直接安装在本机,也可以通过 Docker 启动。2.2 创建空项目先创建空目录和 Git 仓库:mkdir travel-map cd travel-map git init npm init -y 在码道中打开该目录,建立初始会话。第一项任务要求 Agent 只创建基础工程,不加入业务功能:请在当前空目录创建 Next.js 15、React 18 和 TypeScript strict 项目。 使用 App Router,配置 Tailwind CSS、Vitest、Testing Library 和 Playwright。 先生成 package.json、tsconfig.json、基础页面和测试配置。 完成后运行基础测试、类型检查和生产构建,并说明每个文件的职责。核心运行时依赖使用明确版本:npm install next@15.5.21 react@18.3.1 react-dom@18.3.1 npm install -D typescript@5.7.2 @types/node@22.10.0 \ @types/react@18.3.14 @types/react-dom@18.3.2随后由 Agent 按功能阶段补充 tRPC、Prisma、TipTap、Redis、图片处理和测试依赖,并生成 package-lock.json。从锁文件已经生成的阶段开始,统一使用:npm ci2.3 配置本地数据库和环境变量使用 Docker 启动本地 PostgreSQL:docker run --name travel-map-postgres \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=travel_map \ -p 5432:5432 \ -d postgres:16-alpine基础开发阶段使用应用内置的单进程 WebSocket Gateway。实现 Redis 协作阶段后,再启动本地 Redis:docker run --name travel-map-redis \ -p 6379:6379 \ -d redis:7.4-alpine复制环境变量模板:cp .env.example .env openssl rand -base64 32 将生成值写入本地 .env 的 NEXTAUTH_SECRET,并保留以下本地数据库地址:DATABASE_URL="postgresql://postgres:postgres@localhost:5432/travel_map?schema=public" NEXTAUTH_URL="http://localhost:3000" OBJECT_STORAGE_PROVIDER="local" 地图、LLM 和联网搜索配置在对应功能阶段再填写。凭据只能存放在本地 .env,不能写入 Prompt、截图、日志快照或版本库。初始化数据库客户端和迁移:npm run db:generate npm run db:deploy2.4 配置本地码道工作流码道支持项目级代码生成、代码库理解、研发知识问答、测试生成、文件搜索与修改,以及在授权范围内运行 Git、npm、测试和构建命令。TravelMap 涉及前端、服务端、数据库、Worker 和部署文件,项目级索引可以让 Agent 在修改前读取相关实现。开发过程分为两个阶段:探索阶段:分析用户、场景、边界、风险和可选方案;规范阶段:将确认结果写入规格、设计、任务和验收清单。本案例使用以下 SDD 命令:/sdd-new → 生成需求规格 spec.md /sdd-design → 生成技术设计 design.md /sdd-tasks → 生成任务规划 tasks.md /sdd-apply → 按任务实施并更新状态本地开发流程如下:产品目标 → spec.md → design.md → tasks.md → 失败测试 → 最小完整实现 → 本地目标测试 → 本地全量测试和构建 → 浏览器验收 → 更新文档复杂任务按只读审查、测试定位、文档校验和主实施四类职责拆分。主 Agent 汇总证据后再修改文件,减少跨模块任务遗漏。三、构建 TravelMap 应用3.1 建立基础应用和本地质量门空项目完成依赖安装后,先建立稳定的目录和命令:src/app/ # 页面和 Route Handler src/components/ # 通用组件 src/features/ # 前端领域功能 src/server/ # Router、Service 和基础设施 prisma/ # Schema 和 migrations scripts/ # Worker 与校验脚本 __tests__/ # Vitest 测试 e2e/ # Playwright 测试 docs/ # 规格、设计和运行文档基础 package.json 至少提供以下命令:{ "scripts": { "dev": "tsx scripts/dev.ts", "build": "next build", "typecheck": "tsc --noEmit", "test:run": "vitest run", "test:e2e": "playwright test", "db:generate": "prisma generate", "db:deploy": "prisma migrate deploy" } } 先建立首页冒烟测试,再运行基础质量门:npm run test:run npm run typecheck npm run build npm run dev浏览器访问 http://localhost:3000。此时页面只需要显示项目名称和基础导航,后续功能按 tasks.md 分阶段加入。每个阶段开始前,Agent 读取 spec.md、design.md、tasks.md 和当前测试。3.2 从 Idea 到产品规格3.2.1 第一条 Prompt:先理解问题,不要立即写代码我给码道的起始任务可以概括为:我希望构建一个编排—游记一体化旅行平台。 核心功能包括高效率时间编排、地图路线、多人协作, 以及将结构化行程插入游记。 请先不要编码。先分析用户角色、核心旅程、关键对象、边界条件、 风险与分阶段实现方式;对不清楚的地方提出问题。这一步先确定领域和验收条件,随后再创建业务页面。Agent 将需求拆分为帖子、游记、编排、导入、用户、互动和管理后台等领域,并识别时间调度、地图数据、富文本、权限和内容审核等风险。阶段成果沉淀在:output/prd-travel-journey-platform.mddocs/arrangement-architecture.mddocs/case-report/sdd/spec.mddocs/case-report/sdd/design.mddocs/case-report/sdd/tasks.md3.2.2 统一行程列表与时间板编排模型第一版产品概念仍包含传统行程:每天若干地点,地点之间附路线。但在真实使用中,我发现它难以表达这些情况:一个活动有固定预约时间,但前后需要排队和缓冲;住宿、租车、通票跨越多天,却不应挤占普通时间格;同一天存在多个地点组合和容器;某些活动时间可以移动,某些只能在窗口内移动;拖动一个事件后,交通时间可能不再足够;旅行中只需要执行视图,不需要复杂编辑器。我让 Agent 重新分析领域模型,并停止继续扩展旧结构:请比较传统每日行程列表和日期 × 时间模块化编排两种模型。 重点分析固定事件、柔性事件、交通、缓冲、跨日背景周期、 组合容器、时间冲突、拖动与旅行执行模式。 给出统一模型,并说明哪些字段应由程序确定,哪些可以让用户或 AI 输入。最终,Arrangement 成为唯一的结构化旅行计划模型;旧 Itinerary 被迁移下线。活动、交通、用餐、休息和容器统一为事件,调度器以确定性规则处理时间与冲突。这也是一次重要的产品判断:当新模型已经覆盖旧模型时,继续维护两套近似能力只会放大复杂度。3.2.3 根据实现证据调整产品范围编排工作区完成基础验证后,多人协作和离线编辑被纳入产品范围,依据如下:编排天然由多人共同讨论;旅行现场的网络条件不可控;只有服务端版本、历史和恢复机制完整,协作才可信。因此我允许范围改变,但要求每次改变都回答三个问题:它是否强化核心价值,并避免无关功能堆积?现有架构是否能可靠承载?新增复杂度怎样通过测试和运维材料被控制?3.3 从规格到第一版可用产品3.3.1 安装基础技术并建立领域顺序层次技术选择理由Web 框架Next.js 15 + React 18同一工程承载页面、Server API 与 SSR类型系统TypeScript strict让跨前后端数据变更尽早暴露APItRPC + React Query端到端类型、缓存和请求状态管理数据库PostgreSQL + Prisma事务、关系模型、迁移与并发控制样式Tailwind CSS快速建立一致的响应式设计富文本TipTap结构化富文本与自定义编排节点地图高德地图地理编码、POI、路线、天气实时协作WebSocket + Redis + PostgreSQL Outbox低延迟广播与可靠事件记录结合图片处理Sharp + 本地存储/S3 适配服务端校验、重编码与存储接口测试Vitest + Testing Library + Playwright覆盖纯逻辑、组件、服务与浏览器流程基础架构完成后,按以下顺序实施:用户与认证 → 帖子和互动 → TipTap 游记 → Arrangement 编排 → 保存、历史和离线 → 多人协作 → AI 规划 → 智能导入 → 管理后台每个阶段先更新 Prisma Schema 和 API 契约,再实现页面与组件,最后运行目标测试、类型检查和构建。当前仓库包含约 383 个 TypeScript/TSX 文件、61,000 余行受版本管理的 TS/TSX/Prisma/SQL、47 个 Prisma 模型、27 条数据库迁移、133 个 Vitest 测试文件和 7 个浏览器 E2E 规格文件。这些统计用于说明当前工程规模和验证范围。3.3.2 实现编排工作区实施顺序:在 prisma/schema.prisma 中建立 Arrangement、ArrangementEvent、ArrangementRevision 和素材模型;建立 Zod 领域 Schema、五分钟吸附、半开区间冲突和容器边界测试;实现确定性调度器,通过纯逻辑测试后再接入页面;实现时间板几何、拖动、边缘缩放和素材命中;接入 tRPC 读写、自动保存、版本和 Revision;使用本地浏览器检查跨日拖动、短事件、固定事件、交通下限和公开视图。编排工作区需要提供清晰展示、即时拖放和显式冲突反馈:日期横轴、时间纵轴;五分钟吸附;跨日拖动和上下边缘缩放;活动、交通、用餐、休息和组合容器;全天栏与背景周期;交通时长下限和路线状态;评注轨道;素材库与公开素材复制;自动保存、撤销、重做、历史版本;时间板视图与旅行视图。点击事件可以查看地点、时间、备注与只读详情。公开视图会过滤私人备注、Checklist 等敏感执行信息。3.3.3 实现游记与编排动态关联实施顺序:建立 Journal 数据模型和 TipTap JSON 文档格式;实现标题、列表、引用、链接和图片节点;创建 arrangementBlock 扩展,节点只保存 arrangementId;服务端从文档中提取并校验 embeddedArrangementIds;阅读页通过公开投影读取最新编排;添加不存在、未公开、已删除和脏事件数据测试。游记编辑器直接提供标题、列表、引用、链接和多种图片布局。普通用户无需编写 Markdown。编排通过自定义富文本节点插入游记。这个设计解决了两个问题:作者不需要复制一份很快过时的静态表格;源编排更新后,阅读页可以读取最新的公开投影。3.3.4 内容社区与智能导入内容功能按帖子、互动、用户主页和导入任务的顺序实施。导入功能使用持久任务和 Worker,生成结果先保存为草稿,再由用户审核。平台同时保留帖子与游记:帖子适合轻量图片内容;游记适合图文长叙事;编排适合可执行计划;用户主页、评论、点赞、关注、收藏夹和通知把内容连接起来。智能导入支持 URL 与粘贴文本,能够把非结构化内容转为帖子或游记草稿;当游记中识别到可执行行程时,系统会同步生成编排并建立引用。受登录、反爬和来源平台规则影响时,产品提示用户改用手动粘贴。外部账号只能在合法授权下使用。本地验收包括:创建帖子并上传图片 → 创建富文本游记 → 创建编排 → 在游记中插入编排 → 发布并使用未登录窗口查看公开投影 → 提交文本导入任务 → Worker 生成草稿 → 用户审核后发布3.4 使用 Agent 进行问题定位和迭代3.4.1 我使用的固定迭代模板每次出现问题时,我向 Agent 提供完整的问题结构:现象:用户看到什么? 期望:完成任务时应该怎样? 证据:页面、日志、数据或测试说明了什么? 边界:哪些数据不能破坏,哪些能力不能退化? 方法:先写失败测试,再做最小完整修复。 验收:目标测试、全量测试、类型检查、构建和真实页面操作。例如:模块无法直接从素材库拖进容器,会被碰撞检测拒绝。 请先写能够稳定复现这个行为的测试,定位素材拖入与已存在模块拖动 是否错误地共用了碰撞规则。修复后验证容器落点、跨日拖动、撤销和自动保存。3.4.2 调整编排卡片的视觉层级编排早期版本能显示事件,但短事件、组合容器和多种时间层级挤在一起时,信息层级不清楚。Agent 先根据代码和页面生成修改方案,我再通过真实页面发现:短事件标题容易被截断;胶囊状态与主要标题争夺空间;交通、用餐和休息缺少快速区分;AI 浮层会遮挡设置或详情操作。我们逐项修改卡片几何、标记、颜色、悬停详情和浮层避让,并保留每轮修改前后的截图。3.4.3 约束 AI 规划的权限和执行流程早期 AI 规划采取自由工具循环:模型一次生成很大的完整对象,Schema 失败后又整份重写。真实运行中出现过:深层 JSON 截断或嵌套字符串;时间格式和持续时间关系矛盾;运行数小时仍在重复工具调用;Worker 中断后从头执行;搜索、地点和路线预算边界不一致;前端已经显示正文,但终态没有正确解锁。这些问题说明,继续扩大模型上下文或工具轮数无法解决结构校验和执行恢复问题。我和 Agent 共同将架构改为受控分阶段流程:理解需求 → 生成候选行程结构 → 联网研究 → POI 解析 → 路线与天气核验 → 确定性排程 → 事实与约束校验 → 有限局部修复 → 生成攻略 → 形成 Proposal → 用户确认后应用最终原则是:模型负责意图、候选和解释;程序负责 ID、时间、路线、派生字段和硬约束;失败只重试当前阶段;已验证阶段可以从检查点恢复;AI 不能直接覆盖正式编排;地点歧义、证据冲突或路线无法核验时必须阻断。3.4.4 增加可靠自动保存和版本控制自动保存早期只在修改后发送一次请求。加入离线和协作功能后,保存协议增加以下要求:客户端操作 ID 保证重试幂等;服务端版本号防止静默覆盖;冲突时进行可解释合并或要求用户处理;离线信封保存元数据和事件;网络恢复后按版本合并;Revision 记录操作者和变更;历史恢复生成新版本,并保留原有历史。保存流程覆盖客户端状态、网络重试、数据库版本和历史记录。3.4.5 构建可扩展协作链路Agent 先设计角色、邀请、评注与事件锁,再通过代码审查发现搜索越权、游标竞态和撤权后锁未释放等问题。最终协作链路包括:owner / editor / commenter / viewer;邮箱或链接邀请、有效期、次数和撤销;在线 Presence;事件级 15 秒软锁与 fencing token;PostgreSQL 有序事件和 Transactional Outbox;Redis Pub/Sub;独立 Collaboration Gateway 与 Relay;WebSocket → SSE → 持久游标轮询的降级链;序列缺口恢复与权威快照回退;Prometheus 指标和告警规则。协作交互的最终方案为:本地立即预览拖动;后台并行获取软锁;获得锁后才提交;锁冲突时自动撤销;拖动期间关闭位置过渡;WebSocket 误路由时快速失败并进入降级通道。自动化测试通过后仍需执行本地双会话浏览器验收,覆盖权限变化、锁冲突、断线和恢复。3.5 规范化项目上下文、测试与追踪3.5.1 项目上下文Agent 每次工作前需要读取当前项目文件,避免依赖旧会话中的过期信息。项目使用以下材料维持共同上下文:文档作用output/prd-travel-journey-platform.md完整产品需求和初始范围CODEARTS_CONTEXT.md历史决策、已实现能力、风险和凭据安全提醒docs/case-report/sdd/spec.md当前产品需求、非目标和可验收场景docs/case-report/sdd/design.md当前架构、数据流、可靠性、安全和部署设计docs/case-report/sdd/tasks.mdSDD 任务状态、代码证据、测试证据和发布待办docs/arrangement-architecture.md编排领域模型和前后端边界docs/arrangement-save-reliability.md自动保存、版本、冲突与恢复协议docs/arrangement-ai-planner-architecture.mdAI 分阶段规划与可信边界docs/arrangement-collaboration-scale-plan.md多实例协作架构docs/operations/arrangement-collaboration-runbook.md故障诊断与运维步骤3.5.2 TDD:先证明问题存在项目实现阶段遵循固定顺序:建立失败测试 → 运行并确认失败原因正确 → 实现最小完整改动 → 运行目标测试 → 运行全量验证 → 浏览器或生产环境验收 → 更新文档测试覆盖:调度器、五分钟吸附、跨日、DST 和时间几何;自动保存、离线恢复、版本冲突和 Proposal;协作协议、Outbox、Redis、WebSocket、软锁和权限;AI Worker、租约、心跳、账本、证据、路线、天气和 SSE;内容权限、URL 与图片安全、密钥信封;导入队列、LLM 结构校验和失败恢复;组件交互、无障碍和浏览器 E2E;Prisma migration 与 PostgreSQL 并发可靠性;健康接口和本地进程启动。最近一次完整实现会话记录了:133 个测试文件 727 项测试通过 TypeScript strict 检查通过 Next.js 生产构建通过本案例报告编写时没有把旧记录当成新的执行结果。当前仓库仍保留测试、Playwright 报告、截图和构建材料;发布新版本前应重新执行完整门禁。2026-07-26 编写本报告时又进行了独立复核:npm run typecheck:通过;npm run build:通过,保留既有 <img> 图片优化警告;npm run test:run:沙箱内 133 个文件中的 132 个通过,720/727 项通过;唯一未通过文件的 7 项均因沙箱禁止监听 127.0.0.1,错误为 listen EPERM,业务断言未失败;在批准本地监听的受控环境重跑该 WebSocket 文件,9/9 项通过。本次证据结论为:应用类型检查和生产构建通过;需要监听本地端口的 WebSocket 测试在允许本地监听的环境中通过。报告同时保留第一次运行时的环境限制和错误信息。3.5.3 需求—设计—实现—验证追踪一个需求只有同时具备以下证据,才算完成:需求设计/决策实现验证时间板拖放确定性调度 + 五分钟吸附scheduler.ts、时间板组件调度器、组件、E2E多人共同编辑权威版本 + 软锁 + 有序事件collaboration services协议、路由、Gateway、容量 HarnessAI 不直接改正式计划Proposal + 差异确认Proposal 服务与抽屉幂等、重基、应用测试断网可恢复离线信封 + 版本合并offline store / recovery离线浏览器用例3.6 完整项目结构与源码交付TravelMap 采用前后端一体的 Next.js 工程,核心目录如下:travel-map/ ├── src/app/ # 页面、路由与健康接口 ├── src/features/arrangements/ # 编排模型、调度器、自动保存与 UI ├── src/components/editor/extensions # TipTap 编排嵌入节点 ├── src/server/services/arrangements # Proposal、协作、公开投影等服务 ├── prisma/ # 数据模型、迁移和种子数据 ├── scripts/ # Worker、Gateway、Relay 与发布校验 ├── __tests__/ # 单元、组件、服务和部署资产测试 ├── e2e/ # Playwright 浏览器用例 └── docs/ # 架构、协议、部署与运行手册项目依赖由 package-lock.json 锁定,必须使用 npm ci,不能在案例步骤中把关键依赖写成无版本约束的临时安装命令。项目源码仓库地址为 GitCode:qq_26761683/travelmap。仓库完成推送后,读者可以通过 SSH 获取与本文对应的完整工程:git clone git@gitcode.com:qq_26761683/travelmap.git travel-map cd travel-map npm ci没有配置 GitCode SSH 公钥时,可以在仓库页面复制 HTTPS 地址。完成依赖安装后,继续执行 2.3 节的本地环境配置和 3.8 节的验证命令。3.7 关键代码讲解与释义本节解释六处核心实现。每一处均来自当前仓库,并关联相应测试或运行证据。3.7.1 确定性调度与可行性校验解决的问题:拖动事件或生成 AI 行程后,系统必须统一判断时间重叠、容器边界、时间窗口和不确定时长风险,不能由页面或模型各自给出结论。核心函数位于 src/features/arrangements/scheduler.ts:export function analyzeArrangement(events: readonly ArrangementEventInput[]) { const conflicts: ArrangementIssue[] = []; conflicts.push(...relativeTimeConflicts(events)); const expectedPairs = pairs(events, false); const expectedKeys = new Set( expectedPairs.map(([left, right]) => `${left.id}:${right.id}`), ); for (const [left, right] of expectedPairs) { conflicts.push({ kind: 'overlap', eventIds: [left.id, right.id], message: `${left.title}与${right.title}发生重叠`, }); } const risks: ArrangementIssue[] = pairs(events, true).flatMap( ([left, right]) => expectedKeys.has(`${left.id}:${right.id}`) ? [] : [{ kind: 'worst_case' as const, eventIds: [left.id, right.id], message: `${left.title}按最长时长可能影响${right.title}`, }], ); return { conflicts, risks, feasibleExpected: conflicts.length === 0, feasibleWorstCase: conflicts.length === 0 && risks.length === 0, }; } 逐项释义:relativeTimeConflicts 先处理相对另一个事件开始或结束的约束;pairs(events, false) 计算期望时长下的真实冲突,冲突会阻止保存或应用;pairs(events, true) 使用最长时长再次计算,只在新增组合中形成 worst_case 风险;feasibleExpected 与 feasibleWorstCase 分别表示预计时长和保守时长下的可行性,界面据此显示风险;同一函数还检查三级容器限制、子事件是否落在父容器内、窗口和最早/最晚时间,省略部分未在代码块中重复展示。这样设计的原因是:自然语言适合提出候选与解释,硬约束必须由确定性程序执行。对应测试为 __tests__/features/arrangement-scheduler.test.ts,界面效果对应 3.3.2 的公开编排与详情截图。3.7.2 可靠自动保存:防止断网和并发导致静默覆盖解决的问题:频繁拖动会产生连续快照;网络错误需要重试;多人同时编辑时,系统必须防止旧版本静默覆盖新版本。核心队列位于 src/features/arrangements/autosave.ts:constructor(options: ReliableAutosaveQueueOptions<TSnapshot>) { this.options = { debounceMs: 900, retryBaseMs: 750, retryMaxMs: 30_000, ...options, }; this.version = options.initialVersion; this.baseSnapshot = options.initialSnapshot; this.acknowledgedSerial = options.initialAcknowledgedSerial ?? 0; } const result = await this.options.save({ serial: active.serial, snapshot: active.snapshot, reason: active.reason, kind: active.kind, expectedVersion: this.version, clientMutationId: active.clientMutationId, }); this.version = result.version; this.acknowledgedSerial = Math.max( this.acknowledgedSerial, active.serial, ); 重试部分:const newest = this.pending as PendingEntry<TSnapshot> | null; const retry = newest && newest.serial > active.serial ? newest : { ...active, attempt: active.attempt + 1 }; this.pending = retry; const exponent = Math.min(retry.attempt, 8); const delay = Math.min( this.options.retryMaxMs, this.options.retryBaseMs * 2 ** exponent, ); this.publish({ status: 'retrying', nextRetryMs: delay, attempt: retry.attempt }); this.schedule(delay); 逐项释义:900ms 防抖减少拖动过程中的无效请求;expectedVersion 让服务端发现版本冲突,并拒绝最后写入者静默覆盖;clientMutationId 让同一次保存的网络重试具备幂等身份;serial 区分快照新旧,重试时优先保留更新的本地快照;网络型错误按指数退避,最多等待 30 秒;权限、参数等不可恢复错误进入 blocked,不做无限重试;冲突恢复会基于 baseSnapshot 合并,并把恢复后的权威版本回传界面。对应测试为 __tests__/features/arrangement-autosave.test.ts。3.4.4 节记录了该实现的产品背景和迭代过程。3.7.3 AI Proposal:AI 只提交建议,用户确认后才落盘解决的问题:AI 可能使用旧版本、生成无效时间或删除用户内容,因此不能直接覆盖正式编排。核心应用逻辑位于 src/server/services/arrangements/proposals.ts:const proposal = persistedProposal( await tx.arrangementProposal.findFirst({ where: { id: input.proposalId, ownerId: input.ownerId }, }), ); if (!proposal) notFound(); const replay = await tx.arrangementRevision.findUnique({ where: { proposalId: proposal.id }, select: { version: true }, }); if (replay || proposal.status === 'applied') { const version = replay?.version ?? proposal.appliedVersion; if (!version) { throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: '已应用提案缺少版本审计记录', }); } return { status: 'applied', proposalId: proposal.id, version, replayed: true }; } if (proposal.expiresAt && proposal.expiresAt.getTime() <= Date.now()) { await tx.arrangementProposal.update({ where: { id: proposal.id }, data: { status: 'expired' }, }); return { status: 'expired', proposalId: proposal.id }; } 应用前的版本与删除确认:if (arrangement.currentVersion !== input.expectedVersion) { throw new TRPCError({ code: 'CONFLICT', message: '编排版本已变化,请先重新预览提案', }); } const rebased = rebaseArrangementProposal(base, desired, current); if (rebased.status === 'conflict') { await tx.arrangementProposal.update({ where: { id: proposal.id }, data: { status: 'stale', validationReport: json({ valid: false, rebaseConflicts: rebased.conflicts, }), }, }); return { status: 'stale', proposalId: proposal.id, conflicts: rebased.conflicts }; } if (missingDeletionIds.length > 0 || unexpectedDeletionIds.length > 0) { return { status: 'confirmation_required', proposalId: proposal.id, missingDeletionIds, unexpectedDeletionIds, }; } 逐项释义:查询同时带 proposalId 和 ownerId,权限校验发生在服务端;ArrangementRevision.proposalId 是幂等屏障,同一 Proposal 重放不会生成两个版本;过期或验证不通过的 Proposal 会被拒绝;正式编排已变化时先重基;无法安全合并则返回 stale,要求重新预览;删除事件需要逐项确认,AI 提出的删除操作不得直接写入数据;最终修改和 Revision 审计记录在 Serializable 事务中一并完成。对应测试为 __tests__/features/arrangement-proposal.test.ts、__tests__/server/arrangement-proposal-service.test.ts 和 __tests__/prisma/arrangement-proposals.test.ts,界面效果对应 3.4.3 的 AI 编排助手截图。3.7.4 Transactional Outbox:数据库成功后,协作消息也能恢复解决的问题:如果数据库已提交但 Redis 广播失败,其他协作者会遗漏更新;如果多个 Relay 并行工作,还要避免乱序和重复认领。事件和 Outbox 在同一数据库事务中写入,代码位于 src/server/services/arrangements/collaboration.ts:const rows = await db.$queryRaw< Array<{ collaborationStreamSequence: bigint }> >(Prisma.sql` UPDATE "arrangements" SET "collaboration_stream_sequence" = "collaboration_stream_sequence" + 1 WHERE "id" = ${input.arrangementId} RETURNING "collaboration_stream_sequence" AS "collaborationStreamSequence"`); const event = await db.arrangementCollaborationEvent.create({ data: { arrangementId: input.arrangementId, streamSequence, actorId: input.actorId ?? null, type: input.type, version: input.version ?? null, payload, }, }); await db.arrangementCollaborationOutbox.create({ data: { eventSequence: event.sequence, arrangementId: input.arrangementId, channel: arrangementRedisKeys(input.arrangementId).channel, }, }); Relay 的认领条件位于 src/server/services/arrangements/collaboration-outbox-relay.ts:WHERE outbox."published_at" IS NULL AND outbox."available_at" <= CURRENT_TIMESTAMP AND NOT EXISTS ( SELECT 1 FROM "arrangement_collaboration_outbox" AS predecessor_outbox JOIN "arrangement_collaboration_events" AS predecessor_event ON predecessor_event."sequence" = predecessor_outbox."event_sequence" WHERE predecessor_outbox."arrangement_id" = outbox."arrangement_id" AND predecessor_outbox."published_at" IS NULL AND predecessor_event."stream_sequence" < event."stream_sequence" ) ORDER BY outbox."id" FOR UPDATE SKIP LOCKED逐项释义:每个编排原子递增 collaboration_stream_sequence,得到权威事件顺序;业务事件与 Outbox 同事务写入,避免数据库已经更新、消息仍未记录;NOT EXISTS 阻止同一编排的后续事件越过尚未发布的前序事件;FOR UPDATE SKIP LOCKED 允许多个 Relay 安全并行认领;Redis 发布失败时保留 Outbox,增加尝试次数并延后 availableAt,后续可以恢复;客户端仍通过 WebSocket、SSE 或持久游标轮询处理断线与序列缺口。对应测试为 __tests__/server/collaboration-outbox-relay.test.ts 和 __tests__/server/collaboration-outbox-listener.test.ts。3.7.5 游记嵌入与公开投影:保持最新,也不泄露私人执行信息解决的问题:游记需要展示编排,但复制一份完整 JSON 会很快过期;直接公开源数据又可能泄露 Checklist、私人备注和执行状态。编辑器节点只保存 arrangementId,阅读时获取最新数据,代码位于 src/components/editor/extensions/ArrangementBlockView.tsx:const { data: arrangement, isLoading } = trpc.arrangements.getById.useQuery( { id: arrangementId }, { enabled: Boolean(arrangementId), retry: false }, ); const events = (arrangement?.events ?? []).flatMap((event) => { const parsed = arrangementEventSchema.safeParse(event); return parsed.success ? [parsed.data] : []; }); {!isLoading && !arrangement && ( <div className="p-5 text-center text-xs text-slate-500"> 编排不存在、未公开或已被删除 </div> )} {arrangement && ( <ArrangementJourneyView title={arrangement.title} days={days} events={events} backgroundPeriods={periods.success ? periods.data : []} timeZone={arrangement.timeZone} compact /> )} 服务端公开投影位于 src/server/services/arrangements/public-projection.ts:return { ...arrangement, mode: 'planning', backgroundPeriods: [], events: arrangement.events.map((event) => { const constraints = event.constraints && typeof event.constraints === 'object' ? Object.fromEntries( Object.entries(event.constraints) .filter(([key]) => key !== 'manualLock'), ) : {}; return { ...event, executionStatus: 'pending', checklist: [], notes: null, constraints, }; }), } as T; 逐项释义:富文本节点只保存稳定引用,避免游记内产生一份无法同步的行程副本;读取时用 Zod safeParse 隔离脏事件,单条异常不会让整篇游记崩溃;不存在、未公开或删除时显示安全降级文案;服务端强制切回 planning,清空背景周期、Checklist、私人备注和实时执行状态;manualLock 属于私人编辑约束,不进入公开页面。对应测试为 __tests__/features/arrangement-public-projection.test.ts,效果对应 3.3.3 的游记阅读页截图。3.7.6 本地健康检查:区分进程存活与依赖就绪解决的问题:本地进程能够监听端口时,数据库或 Redis 仍可能不可用。存活检查和就绪检查需要分别返回状态。src/app/api/health/live/route.ts 提供进程存活检查:export async function GET() { return NextResponse.json({ status: 'ok', service: 'travel-map-web', timestamp: new Date().toISOString(), }, { status: 200, headers: { 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', }, }); } src/app/api/health/ready/route.ts 检查本地依赖:await prisma.$queryRaw`SELECT 1 AS ready`; const redis = await collaborationRedisConnections().catch(() => null); const redisStatus = process.env.COLLABORATION_TRANSPORT === 'redis' ? await redis?.health() ?? 'unavailable' : undefined; return NextResponse.json({ status: redisStatus === 'unavailable' ? 'degraded' : 'ready', checks: { database: 'ok', ...(redisStatus ? { redis: redisStatus } : {}), }, }); 逐项释义:/api/health/live 只确认 Web 进程能够响应;/api/health/ready 执行 SELECT 1,确认 PostgreSQL 可访问;启用 Redis 协作模式时,就绪接口继续检查 Redis;Redis 不可用时返回 degraded,数据库异常时返回 HTTP 503;两个接口均禁用缓存,避免旧健康结果影响判断。对应实现为两个健康 Route Handler。本地启动后使用 curl 验证,部署阶段继续复用相同接口。这六组代码对应调度、保存、AI 权限、协作事件、公开数据和运行状态六项核心质量要求。3.8 本地运行、测试和效果验收3.8.1 初始化并启动全部本地进程完成各功能任务后,执行:npm ci npm run db:generate npm run db:deploy npm run devnpm run dev 启动 Web、Import Worker 和 Planner Worker。设置 COLLABORATION_TRANSPORT=redis 后,还会启动 Collaboration Gateway 和 Relay。本地健康检查:curl -fsS http://localhost:3000/api/health/live curl -fsS http://localhost:3000/api/health/ready3.8.2 执行自动化质量门npm run test:run npm run typecheck npm run build本报告采集证据时,类型检查和生产构建通过。普通测试在受限沙箱中通过 720/727 项;7 项 WebSocket 测试因本地监听权限产生 listen EPERM。在允许本地监听的环境中重跑对应文件,9/9 项通过。报告保留两次执行条件和结果。3.8.3 执行本地浏览器验收按顺序检查:注册、登录和用户主页;帖子、游记和编排的创建与公开页面;时间板拖放、缩放、冲突提示、撤销和自动保存;游记插入编排和公开投影;邀请、成员角色、评注、事件锁和双会话同步;WebSocket 断开后的 SSE 与轮询降级;AI Planning Run、Proposal 预览和用户确认;URL/文本导入、草稿审核和失败重试;断网读取、网络恢复和版本合并。3.9 部署项目代码到华为云3.9.1 选择部署方案部署任务同样在码道会话中完成:Agent 先读取运行依赖、Worker、对象存储与长连接需求,再对照华为云官方文档比较 FunctionGraph、CCE、CAE、Flexus 与 ECS。最终选择按需 ECS 单机 Demo,原因是:项目包含 Web、导入 Worker、规划 Worker和协作常驻进程;需要 PostgreSQL、Redis、WebSocket、SSE 和图片处理;当前 Demo 采用 Compose,以控制部署时间和资源费用;所有资源都要能按需释放;同时保留未来迁移到 RDS 和多应用节点的路径。3.9.2 当前 Demo 架构公网用户 │ ▼ 域名 / HTTPS / EIP │ ▼ Caddy :80/:443 ├── Next.js Web :3000(仅容器内网) └── Collaboration Gateway :3001(仅容器内网) 单台按需 ECS + EVS ├── Web ├── Import Worker ├── Planner Worker ├── Collaboration Gateway ├── Collaboration Relay ├── PostgreSQL 16 ├── Redis 7.4 ├── Prometheus └── Redis/PostgreSQL Exporter ECS ──► OBS 公共读媒体桶 ECS ──► OBS 私有数据库备份桶 ECS ◄── SWR 不可变应用镜像3.9.3 第一步:准备 VPC、安全组、ECS、EVS 和 EIPDemo 推荐基线:资源建议ECS按需、x86、4 vCPU / 8 GiB系统盘80 GiB 通用型 SSDEVS 数据盘100 GiB,承载 PostgreSQL、Redis 和短期备份EIP按流量、5–10 Mbit/s 峰值安全组公网入方向80、443;22 仅允许固定管理 IP明确不开放:3000:只能由 Caddy 访问;3001:只能由 Caddy 转发 WebSocket;5432:PostgreSQL 只允许容器内网;6379:Redis 只允许容器内网;Docker API、Portainer 等管理端口。EVS 初始化时必须先用 lsblk、blkid、findmnt 确认目标盘,再格式化新盘。/etc/fstab 使用 UUID,不能假设重启后设备名不变。3.9.4 第二步:创建两个 OBS 桶媒体和备份不能共用一个桶:桶权限内容媒体桶公共读,禁止公共写用户上传并经过服务端重编码的公开图片数据库备份桶私有,建议服务端加密PostgreSQL 逻辑备份与校验和使用专用 IAM 用户和最小权限,不使用主账号 AK/SK。部署前运行:npm run deploy:check-storage该脚本依次执行上传测试对象、通过公网读取和删除对象,可以提前发现 Endpoint、Region、AK/SK、桶策略或公开 URL 配置错误。3.9.5 第三步:构建并推送 SWR 镜像在可信构建机执行:npm ci npm audit --omit=dev npm run test:run npm run typecheck npm run buildApple Silicon 为 x86 ECS 构建时:docker buildx build \ --platform linux/amd64 \ --build-arg NEXT_PUBLIC_AMAP_KEY=浏览器高德Key \ --build-arg NEXT_PUBLIC_AMAP_SECURITY_CODE=高德安全密钥 \ -t swr.实际区域.myhuaweicloud.com/组织名/travel-map:不可变版本号 \ --push \ . 这里有三条不可越过的边界:使用不可变标签,不长期依赖 latest;NEXT_PUBLIC_* 会进入浏览器 bundle,修改后必须重新构建;数据库、OBS、LLM 和加密密钥不能作为 build arg。3.9.6 第四步:生产配置与迁移生产配置分为 Compose 环境和应用环境:deploy/huawei-cloud/.env:镜像地址、域名、PostgreSQL/Redis 密码和数据目录;deploy/huawei-cloud/app.env:NextAuth、高德、LLM、OBS、健康检查和加密密钥。两个文件都不能提交到仓库,权限应设为 600。上线前先检查:npm run deploy:check-env docker compose --env-file .env config --quiet 再进行备份、拉取和迁移:./backup-postgres-to-obs.sh docker compose --env-file .env pull docker compose --env-file .env up -d postgres redis docker compose --env-file .env --profile tools run --rm migrate云上结构只允许:prisma migrate deploy不能使用 prisma db push 掩盖 migration drift。3.9.7 第五步:启动服务与 HTTPSdocker compose --env-file .env up -d --no-build docker compose --env-file .env ps Caddy 负责:80/443;自动 HTTPS;安全响应头;WebSocket 路由;SSE 关闭代理缓冲。更新 Caddyfile 后必须验证运行中的 Caddy 已加载新配置。项目曾出现文件已经上传、容器仍使用旧路由的问题,因此部署文档要求强制重建容器或明确重新加载 Caddy 配置。部署验收还发现拖动开始前等待远程 Redis 软锁会增加约一秒延迟。3.4.5 节记录的处理方式包括本地立即预览、后台获取软锁、获得锁后提交,以及锁冲突时撤销预览。该问题需要在真实网络环境中验证,本地回环网络无法提供同等延迟条件。3.9.8 第六步:健康与业务验收基础健康接口:curl -fsS https://travelmap.linykweb.top/api/health/live curl -fsS https://travelmap.linykweb.top/api/health/ready2026-07-26 本案例采集报告素材时,真实响应为:{"status":"ok","service":"travel-map-web"} {"status":"ready","checks":{"database":"ok","redis":"ok"}} 业务验收至少包括:注册、登录和管理员权限;帖子、游记和编排公开页;图片上传、读取和删除;导入任务由 Worker 领取;AI 规划 SSE 与轮询恢复;WebSocket 协作、撤权和重连;浏览器刷新、断网与离线恢复;OBS 备份对象可下载;pg_restore --list 能读取备份。3.9.9 更新、回滚和备份推荐发布顺序:备份数据库 → 推送新不可变镜像 → 拉取镜像 → 执行向前兼容 migration → 启动/重建服务 → 健康检查 → 业务冒烟 → 观察协作、Worker 和数据库指标应用回滚可以把 TRAVEL_MAP_IMAGE 改回上一个标签。数据库迁移不能简单假设可逆;涉及数据结构删除时,必须提前设计 expand/contract 迁移或通过备份恢复。每天备份默认意味着最长约 24 小时 RPO。报告不能把它写成高可用生产架构。3.9.10 单机 Demo 的边界当前架构是单故障域:ECS 故障会同时影响 Web、Worker、Redis 和 PostgreSQL;没有数据库主备自动切换;4C8G 下多个服务存在资源竞争风险;本地容量 Harness 的 1k/5k/10k 连接结果不等于云上已经承载相同并发;恢复时间取决于镜像、备份和人工操作。如果平台进入正式长期运营,演进方向是:PostgreSQL → RDS 主备 Redis → DCS 媒体 → 私有桶 + CDN/签名访问策略 Web/Gateway/Worker → 多节点 Caddy → ELB/Ingress 监控 → AOM/LTS 或完整可观测平台3.10 阶段成果3.10.1 产品成果帖子、游记和编排的一体化内容平台;可拖放、可缩放、可跨日的时间板;地点、路线、交通、缓冲、固定事件和背景周期;游记富文本与编排嵌入;收藏、评论、点赞、关注、通知和公开收藏夹;URL/文本智能导入与审核;用户、内容、举报、账号池和 AI 配置后台。3.10.2 协作成果四级成员角色;邀请、Presence、评注与事件锁;WebSocket/SSE/轮询降级;PostgreSQL Outbox + Redis;版本、审计、冲突与权限回收;Prometheus 指标和运行手册。3.10.3 AI 与可靠性成果持久 PlanningRun、租约、心跳、重试与 fencing token;分阶段、可恢复的规划流程;来源、证据、事实冲突和成本账本;地点、路线与天气 fail-closed;Proposal 差异审查和用户确认;失败任务按最新配置原地重试。3.10.4 工程成果TypeScript strict;Prisma migrations;单元、组件、服务、数据库和浏览器测试;Docker 多阶段镜像;华为云 Compose、Caddy、OBS、SWR 与备份材料;存活、就绪和规划健康接口;部署、协作、备份和故障排查文档。3.11 人与 Agent 的职责边界阶段我负责码道负责Idea提出真实问题、目标与价值判断展开角色、场景、边界与风险需求决定优先级和非目标生成规格、查漏补缺、保持一致性设计判断产品取舍分析架构、数据模型和实施路径实现审批范围和重要变更读取代码、生成测试、修改文件、执行命令迭代提供真实现象和体验判断定位根因、补回归测试、完成修复验收判断结果是否符合使用预期运行测试、类型检查、构建和浏览器操作部署掌握账号、凭据和生产授权生成部署材料、核对官方文档、执行受控命令运维决定故障处置和风险接受分析日志、健康状态和回滚路径以下三个决定由人负责:这个功能是否值得做;这个风险是否可以接受;什么证据足以说明已经完成。3.12 案例总结本案例形成了一套从空项目到完整应用的 AI 辅助开发流程:创建本地空项目 → Agent 完善需求 → 编写 Spec、Design 和 Tasks → 建立失败测试 → 实现最小完整改动 → 执行本地测试、构建和浏览器验收 → 部署华为云 → 配置健康检查、备份和回滚 → 更新架构、测试和运行文档项目完成了以下主要调整:使用模块化编排统一旧行程列表;增加多人协作、权限、软锁和有序事件;使用受控 Proposal 管理 AI 规划结果;为保存、恢复、规划和协作增加审计与验证证据;完成本地运行、华为云部署、监控、备份和回滚材料。每项重要变更均关联需求、设计、代码和验证结果。人负责产品取舍、风险接受和发布授权,Agent 负责上下文分析、任务实施和证据整理。四、释放资源删除云资源可能不可恢复。先确认体验已经结束,并把需要保留的数据下载到受控位置。4.1 备份并验证数据在 ECS 的 deploy/huawei-cloud 目录执行最后一次 backup-postgres-to-obs.sh;进入 对象存储服务 OBS > 桶列表 > 私有备份桶,下载最新数据库备份及校验和;使用 pg_restore --list <备份文件> 验证备份可读;导出需要保留的公开媒体对象;记录最后一个可用的 SWR 镜像不可变标签。如果没有完成以上步骤,后续删除 ECS、EVS 或 OBS 对象可能导致数据永久丢失。4.2 删除 ECS、EVS 和 EIP进入 弹性云服务器 ECS > 弹性云服务器,选择本案例 ECS,单击 更多 > 删除;在确认对话框中核对是否需要同时删除系统盘、数据盘并释放绑定的 EIP;进入 云硬盘 EVS > 云硬盘,检查是否仍有未随 ECS 删除的按需数据盘,确认无保留需求后删除;进入 虚拟私有云 VPC > 弹性公网 IP 和带宽,释放仍处于计费状态的 EIP 和带宽;最后检查 安全组 和 VPC,仅在没有其他业务资源依赖时删除。ECS 关机后,EVS、EIP 等资源仍可能计费。需要按资源清单逐项释放。4.3 清理 OBS、SWR 与访问凭据进入 容器镜像服务 SWR > 我的镜像 > 镜像版本,删除不再需要的 TravelMap 镜像版本;若整个组织还承载其他项目,不要删除组织;进入 对象存储服务 OBS > 桶列表,分别检查媒体桶和备份桶;下载需保留对象后清空版本、碎片和对象,再删除不再使用的桶;保留对象仍会计费;进入 统一身份认证服务 IAM > 用户 > 安全设置 > 访问密钥,停用或删除本案例专用 AK/SK;在域名服务商或 云解析服务 DNS > 公网域名 中删除不再使用的 TravelMap 解析记录;在费用中心检查 ECS、EVS、EIP、OBS 和其他按需资源是否仍有计费项。五、扩展资料说明5.1 可复用 Prompt5.1.1 Idea 完善请先不要编码。阅读项目上下文,围绕目标用户、核心旅程、关键对象、 边界条件、失败场景、非目标和阶段计划完善这个 Idea。 请区分事实、假设和需要我决定的产品取舍。5.1.2 架构审查请只读审查当前实现。先给出数据流、权限边界、并发模型和故障恢复链路, 再列出按严重度排序的问题。每个问题必须包含代码证据、影响范围、 建议方案和需要增加的测试。本轮不要修改文件。5.1.3 TDD 修复现象: 期望: 现有证据: 不可破坏的行为: 请先增加一个能稳定复现问题的失败测试,确认它因目标原因失败; 然后实现最小完整修复,运行目标测试、全量测试、类型检查和生产构建。 如果真实页面行为无法由测试证明,再执行浏览器验收。5.1.4 部署设计请先盘点项目的 Web、数据库、Worker、长连接、对象存储和凭据需求, 再对照华为云官方文档比较候选部署方案。 输出推荐架构、资源规格、网络边界、费用与故障域, 并列出上线前阻断项。本轮先分析,不生成虚假的成功结论。5.1.5 发布验收请按发布门禁检查:依赖安装、测试、类型、构建、Prisma migration、 Docker/Compose 配置、健康接口、对象存储、Worker、WebSocket、SSE、 备份和回滚。每项给出实际命令、结果和证据位置。 未执行的项目必须明确标记为未执行。5.3 官方与项目资料5.3.1 华为云码道华为云码道官方产品页产品介绍码道 IDE/CLI 下载CLI SDD 规范驱动开发CLI SkillsCLI 自定义命令CLI MCPCLI 子智能体CLI 权限管理CLI 沙箱机制代码库索引5.3.2 华为云部署弹性云服务器 ECS对象存储服务 OBSSWR 上传镜像CodeArts BuildCodeArts DeployCodeArts Deploy 快速入门
  • [高校训练营] 基于华为云码道(CodeArts)代码智能体的校园二手书交易平台全栈开发实践
    基于华为云码道的校园二手书交易平台全栈开发实践1. 案例概述在高校学习生活中,教材和课外书具有明显的阶段性。课程结束、考试完成或学生毕业后,大量书籍进入闲置状态;与此同时,新生和低年级学生仍然需要以较低成本获取教材。传统微信群、朋友圈和线下摆摊存在信息分散、搜索效率低、书籍状态不透明、交易过程无法追踪等问题。本案例围绕“让校园闲置书低成本、安全、可追踪地流转”这一目标,构建一套前后端分离的校园二手书交易平台。项目提供用户注册登录、书籍发布与审核、关键词检索、购物车、站内余额、管理员充值、订单履约、收藏、评价、站内通知和后台统计等功能,最终形成“发布—审核—选购—支付—发货—收货—评价”的完整业务闭环。项目在开发过程中使用华为云码道辅助完成需求梳理、系统设计、任务拆解、功能开发和代码检查,并在仓库中沉淀了 spec.md、design.md 和 tasks.md 等规格文档。当前交付版本采用 Vue 3、Node.js、Express 和 sql.js,能够在本地快速运行,也便于进一步部署到华为云。2. 案例目标本案例不是只完成静态页面,而是以“可正常运行、可完整演示、可自动验证”为交付目标。主要目标如下:实现普通用户、买家、卖家和管理员之间清晰的权限边界。实现真实书籍封面、分类筛选、关键词搜索和书籍详情。实现管理员为用户充值、用户使用站内余额购买书籍的资金闭环。实现购物车、跨卖家拆单、订单支付、卖家发货、买家收货和评价。为充值、支付和订单状态变化保留资金流水与站内通知。使用自动化测试验证权限、状态、余额和异常场景。保留可部署到华为云 ECS、RDS 和 OBS 的演进空间。3. 开发环境与技术选型3.1 前端Vue 3:构建组件化单页应用。Vue Router:管理用户端和管理端路由。Pinia:保存登录用户、余额和公共业务状态。Element Plus:提供表单、表格、弹窗、分页和消息反馈组件。Axios:统一访问后端 REST API。Vite:提供开发服务器和生产构建。3.2 后端Node.js + Express:提供 REST API。JWT:完成身份认证和接口鉴权。bcryptjs:保护用户密码。Multer:处理书籍封面上传。sql.js:使用 SQLite 数据文件完成轻量持久化。Supertest + Node.js Test Runner:实现接口级集成测试。3.3 工程结构项目当前主要由以下部分组成:used-book-web/ Vue 3 前端 used-book-api/ Express 后端与 SQLite 数据 used-book-server/ 早期 Spring Boot 实现(保留版本) .codeartsdoer/ 需求、设计和任务规格文档 README.md 项目启动、账号和功能说明早期系统设计采用 Spring Boot/MySQL。为了降低实习项目演示和单机部署的复杂度,当前默认运行主线演进为 Express/sql.js。案例以实际运行版本为准,同时保留早期版本作为架构演进记录。4. 使用华为云码道完成项目构建4.1 需求分析首先向华为云码道提供校园二手书的业务背景、目标角色和初步功能设想,请其帮助拆分功能域、明确验收条件和识别边界场景。需求阶段重点确认了以下规则:新发布书籍默认进入待审核状态。普通用户不能访问管理接口。用户不能购买自己发布的书籍。订单详情只能由相关买家、卖家或管理员访问。余额不足时必须阻止支付。同一购物车存在多个卖家时,需要按卖家拆分订单。只有卖家可以发货,只有买家可以确认收货和评价。需求分析结果沉淀在 .codeartsdoer/specs/used_book/spec.md 中,为后续设计和测试提供统一依据。4.2 系统设计在需求规格基础上,继续让华为云码道从数据模型、接口、权限、业务状态和异常处理几个方面给出设计建议。系统最终包含 13 张业务表:数据表作用user用户、角色、余额与账户状态category书籍分类book书籍基础信息、卖家、价格与状态book_image书籍图片price_change_log价格变更记录cart购物车order买家、卖家和订单状态order_item订单中的书籍价格快照review交易完成后的评价wallet_transaction充值、购买支出和卖家收入流水notification支付、发货、收货等站内通知favorite用户收藏book_view_log书籍浏览记录系统设计文档和任务拆解分别记录在 design.md 和 tasks.md 中。设计阶段最大的价值是提前发现“余额支付不是单表状态更新”“多卖家订单不能由一个卖家统一履约”等问题。4.3 任务拆解项目按照依赖关系分阶段实施:初始化前后端项目与数据库。完成用户注册、登录和 JWT 鉴权。完成分类、书籍发布、封面上传和审核。完成首页检索、详情、收藏和浏览记录。完成购物车和订单创建。完成管理员充值、余额支付和资金流水。完成发货、收货、评价和通知。完成管理后台和统计。增加集成测试并执行完整代码审查。通过规格驱动的任务拆解,可以减少前后端并行开发时的接口歧义,也方便在每个阶段运行测试。4.4 关键功能迭代在初版完成后,又使用华为云码道对照 README 检查实际代码和需求,重点完成了两轮增强。第一轮增强解决了交易资金来源问题。系统原本具有买卖功能,但缺少适合演示的充值入口。优化后只有管理员可以为指定用户增加站内余额,同时记录充值金额、备注、操作时间和变动后余额。普通用户不能直接修改余额。第二轮增强提升了书籍展示真实性。项目演示数据补充了真实书籍封面,并为图片加载失败保留默认占位图,避免外部图片失效导致页面结构异常。最后对完整仓库进行功能和代码检查,运行后端集成测试和前端生产构建,修复字段命名、参数校验、权限边界、待审核书籍可见性等问题。5. 系统总体方案5.1 逻辑架构浏览器 │ ▼ Vue 3 单页应用 │ Axios / JSON / JWT ▼ Express REST API ├─ 用户与鉴权 ├─ 书籍与审核 ├─ 购物车与订单 ├─ 钱包与资金流水 ├─ 收藏、评价与通知 └─ 管理后台与统计 │ ├─ SQLite 数据文件 └─ 封面上传目录前端通过 Axios 访问 API,后端使用 JWT 判断用户身份和角色。所有关键权限均在服务端再次校验,不能只依赖前端隐藏按钮。业务数据持久化到 SQLite 文件,封面图片由上传目录提供静态访问。5.2 用户与权限普通用户可以浏览和搜索书籍、发布书籍、加入购物车、购买、发货或确认收货,但具体操作受到资源归属限制。管理员拥有用户管理、书籍审核、充值和数据统计权限。管理接口统一检查 admin 角色,普通用户直接调用接口也会被拒绝。5.3 书籍发布与审核用户发布书籍时需要填写书名、作者、分类、价格、成色、出版信息和描述,并可上传封面。前端提供表单提示,后端再次校验日期、价格和分类合法性。新书默认处于待审核状态。只有审核通过并处于在售状态的书籍才进入公共列表,避免违规或错误内容直接公开。首页支持分类、关键词、价格与排序筛选。搜索同时匹配书名和作者,详情页展示封面、价格、成色、卖家和描述。5.4 购物车与跨卖家拆单用户可将不同卖家的书籍加入同一个购物车。创建订单时,后端重新读取每本书的最新状态和价格,并按 sellerId 分组,为每个卖家生成独立订单。这种设计使每个卖家只能处理自己的订单,发货和结算互不影响,也避免某个卖家延迟发货导致其他订单无法推进。5.5 站内余额与管理员充值系统买卖书籍使用平台站内余额。管理员可在用户管理页面选择用户,输入充值金额和备注。服务端校验管理员身份、金额范围和目标用户,并同时更新用户余额和钱包流水。钱包流水包含:管理员充值;购买支出;卖家收入;交易说明;变动后余额;操作时间。这样既能方便训练营项目演示,也能让每次资金变化可追踪。5.6 订单状态订单的主要状态流转如下:待支付 pending_payment │ 买家余额支付 ▼ 已支付 paid │ 卖家发货 ▼ 已发货 shipped │ 买家确认收货 ▼ 已完成 completed │ ▼ 评价 review创建订单后,买家可以支付或取消。支付时后端重新检查订单状态和余额,余额不足则拒绝。卖家只能对已支付订单发货,买家只能对已发货订单确认收货。关键状态变化会生成站内通知。6. 关键技术难点与解决方案6.1 资金与业务状态一致性支付涉及买家余额、钱包流水、订单状态、书籍状态和通知等多项变化。如果只在前端扣减显示余额,刷新页面后数据会失真,也可能产生越权支付。本项目将后端作为唯一可信来源。支付请求到达后,后端依次验证:当前用户是否为订单买家;订单是否处于待支付状态;订单中的书籍是否仍可交易;买家余额是否足够;所有校验通过后再执行扣款、状态更新和流水记录。前端只负责展示后端返回的新余额和订单状态。6.2 多卖家订单拆分购物车是买家维度的数据,但订单履约是卖家维度的数据。如果不同卖家的书籍进入同一订单,一个卖家可能看到或操作另一个卖家的书籍。系统在创建订单时先按卖家分组,再分别创建订单和订单条目。自动化测试专门覆盖跨卖家拆单,验证订单数量、卖家归属和金额计算。6.3 状态机与越权防护订单详情、评价删除、书籍审核和后台统计都存在越权风险。系统在每个接口中校验用户角色、资源归属和前置状态。例如普通用户不能删除他人评价,未参与订单的用户不能查看订单详情,待审核书籍不会出现在公共列表。6.4 真实封面与降级展示真实封面可以显著改善项目效果,但外部图片可能失效、加载缓慢或出现跨域问题。当前演示数据使用可访问封面,同时保留默认封面作为降级展示。部署到生产环境后,建议将确认合规的图片迁移到 OBS 或项目静态资源。6.5 架构演进的一致性项目早期规格采用 Spring Boot/MySQL,当前交付主线采用 Express/sql.js。为了避免评审时出现文档与实现不一致,本案例明确区分“早期设计版本”和“当前可运行版本”,并建议后续以实际云端部署技术栈更新设计文档。7. 测试与验证项目执行统一检查命令:npm run check检查结果如下:后端集成测试:14/14 通过,0 失败;前端生产构建:成功;Vite 版本:8.1.5;前端转换模块:1799 个;本地 Web 服务:http://localhost:5173;本地 API 服务:http://localhost:8088。14 个集成场景覆盖:公共书籍接口字段采用 camelCase;搜索同时匹配书名和作者;非法出版日期和分类被拒绝;不存在的接口返回统一 JSON 404;订单详情权限隔离;普通用户不能删除评价;购物车返回卖家信息;待审核书籍的可见性;管理后台统计;管理员充值、余额购买和卖家入账;跨卖家购物车拆分订单;支付、发货、收货、评价和通知完整闭环;收藏功能;余额不足时拒绝支付,并允许取消订单。除自动化测试外,还进行了真实页面操作。本次演示由管理员为用户充值 100 元,用户以 20 元购买《三体》,支付后余额为 80 元,同时生成购买支出流水和支付成功通知。8. 运行与演示8.1 启动方式在项目根目录执行:npm run dev该命令同时启动前端和 API。执行 npm run check 可以运行后端测试并构建前端生产包。8.2 推荐演示路径在首页搜索并查看《三体》详情。管理员进入后台,为测试用户充值 100 元。买家将书籍加入购物车并创建订单。使用站内余额支付,查看余额由 100 元变为 80 元。查看资金流水和支付成功通知。卖家发货,买家确认收货并评价。回到管理后台查看订单和书籍状态统计。8.3 运行效果截图以下截图展示首页、书籍详情、管理员充值、购物车、订单支付、钱包流水、站内通知和后台统计等核心效果。9. 华为云实际部署项目已部署到华为云 ECS,在线演示地址为 http://124.70.103.252/,健康检查地址为 http://124.70.103.252/health(受论坛外链白名单限制,复制地址时请将中文全角冒号 : 改为英文半角冒号 :)。完整案例 PDF 下载地址:http://124.70.103.252/used-book-case-report.pdf(复制时同样将 : 改为 :)。使用 Nginx 部署 used-book-web/dist,并反向代理 /api、/uploads 和 /health;使用 systemd 运行 Node.js API,支持开机启动和异常重启;SQLite 与 uploads 位于独立持久化目录;API 仅监听 127.0.0.1:8088,8088 不直接对公网开放;部署时生成随机 JWT 密钥,并使种子账号默认密码失效;演示实例计划于 2026 年 7 月 31 日 02:49(GMT+08:00)自动释放。当前部署适合训练营阶段验收。如果后续需要长期运行,可增加域名和 HTTPS,将 SQLite 迁移到华为云 RDS,将上传图片迁移到 OBS,并增加集中日志、监控和备份。10. 案例总结本项目完成了从需求、设计、编码、测试到交付材料整理的完整过程。与简单的增删改查项目相比,校园二手书平台重点解决了多角色权限、订单状态、跨卖家拆单、资金流水和业务一致性问题。华为云码道在项目中的主要价值体现在:将模糊的项目想法转化为可执行的规格和验收条件;从需求中识别状态、权限和异常场景;形成设计文档和任务拆解;辅助实现管理员充值、真实封面和完整交易闭环;对照 README 和代码进行仓库级检查;通过自动化测试验证功能并发现边界问题;将代码、截图、测试结果和指导书要求整理为可交付案例。
  • [训练营] 居民小区物业管理系统设计
    一、概述1.1 案例介绍本案例基于华为云码道(CodeArts)代码智能体,演示如何使用规范驱动开发(SDD)全流程高效构建并落地一套智慧社区 PMS 小区物业管理系统。通过自然语言指令与业务技能联动,快速完成从“总体设计”、“MySQL DDL建表”、“RESTful API接口定义”到“策略模式后端核心计费代码(Spring Boot)”及“Vue 3前端界面”的全栈开发与部署调优,帮助开发者掌握AI辅助现代软件工程的最佳实践。1.2 适用对象个人开发者高校学生1.3 案例时间本案例总时长预计4小时。1.4 案例流程说明:AI IDE 华为云码道(CodeArts)代码智能体安装部署;下载物业管理项目所需skills;借助码道进行数据清洗、归因分析并完成现状诊断;CodeArts IDE集成CodeArts Pipeline插件实现本地调用华为云云上流水线任务;线上查看运行结果;得到物业管理课题分析与系统设计结果。1.5 资源总览资源名称规格单价(元)华为云码道(CodeArts)代码智能体专业版代金券购买二、环境和资源准备2.1 AI IDE华为云码道安装部署完成 Windows 版 AI IDE 华为云码道(CodeArts)代码智能体安装部署。2.2 配置相关环境和skills在本地安装Python / Node.js,为后续系统设计做好准备。在华为云码道AI IDE中配置物业管理相关的Skills,包括官方的code-reviewer、database-design、java-ut-generator等技能。三、物业管理系统设计3.1 项目背景与需求本项目为一家中小型住宅小区物业服务公司开发轻量级、模块化的物业管理系统,解决以下核心痛点:痛点描述房产与业主数据混乱一户多车、租户变动频发,台账更新不及时,"人-房-车"绑定关系难以精准掌握计费与收缴效率低下房屋物业费人工核对成本高;停车位收费标准不一,容易漏收、错收账单缺乏透明度与追溯业主对计费规则存疑,收缴记录难以快速检索,催缴阻力大车位权属不清晰售出/租用车位混杂管理,租期到期缺乏预警,易引发纠纷编写项目背景与需求.md文档,并根据文档内容对智能体提出要求:#项目背景与需求.md 请帮我完成以下工作: 第一阶段:需求分析与总体设计 1. 加载`/database-design` 和`/property-business-rules`技能。 2. 使用`spec-story-generator`方法论进行需求细化与场景拆解。 3. 识别系统的关键决策点与潜在风险。 4. 设计系统整体分层架构与核心模块划分。 5. 定义数据模型设计思路。 6. 规划核心RESTful API接口清单。 7. 制定后续代码与组件的实施步骤计划。 第二阶段:文档输出要求 1. 输出一份完整的总体设计思路与架构方案文档(Markdown格式,使用中文编写)。 2. 避免使用复杂数学符号,排版清晰,结构严谨,便于后续直接指导开发。后续对话码道进行优化调整:加载/database-design、/property-business-rules 和 spec-story-generator skill的规则逻辑,分析一下总体设计文档,是否有需要优化或调整的地方,若有请进行最优调整3.2 系统架构3.2.1 整体架构采用前后端分离架构:后端:Spring Boot 2.7.18 + Java 8 单体应用,RESTful API前端:Vue 3 SPA,Vite 构建,Element Plus UI数据库:MySQL 8.0,utf8mb4 字符集认证:JWT 无状态认证3.2.2 后端技术栈技术版本用途Spring Boot2.7.18应用框架Java1.8运行时MyBatis-Plus3.5.7ORM 框架Spring Security5.7.x认证授权JJWT0.11.5JWT 令牌BCrypt-密码加密下图为后端项目整体构建过程。项目从pom.xml骨架与分层包结构起步,率先搭建了统一响应体、全局异常处理和审计字段基类等公共组件,为后续业务模块提供了规范化的基础支撑。在此基础上,依照领域模块的依赖关系,依次实现了认证授权(auth)、小区与房产管理(community)、人员管理(resident)、车位管理(parking),再通过策略模式落地计费与账单生成(billing),进而完成收缴管理(payment)和统计看板(dashboard)。开发全程依托Spring Boot 2.7.18 + MyBatis-Plus + Spring Security + JWT,严格遵循RESTful规范与数据库设计。3.2.3 前端技术栈技术版本用途Vue3.5.x前端框架Vite8.1.x构建工具Element Plus2.14.xUI 组件库Pinia4.0.x状态管理Vue Router4.6.x路由管理ECharts6.1.x图表可视化Axios1.18.xHTTP 客户端dayjs1.11.x日期处理3.2.4 模块架构后端按业务领域划分为8个模块:模块职责auth用户认证、JWT 令牌、账号管理community小区、楼栋、单元、房间、费率配置resident人员管理、房间绑定关系parking车位管理、车位分配/释放billing账单生成、计费策略(策略模式)payment缴费录入、逾期查询dashboard数据看板、收缴统计common通用响应、配置、工具类3.3 核心业务规则3.3.1 物业费计算应缴物业费 = 房屋建筑面积 × 单位物业费单价 × 计费月数建筑面积必须 > 0单价通过费率配置表按生效日期查询3.3.2 车位计费策略车位类型计费周期计算规则售出车位 (type=1)按年年管理费 = 费率配置单价 × 1租用车位 (type=2)按月月租金 = 费率配置单价;不足整月按比例折算:月租金 / 当月天数 × 实际使用天数策略模式实现类:BillingStrategyFactory — 策略工厂,按 feeType 路由SoldParkingStrategy — 售出车位按年计费策略RentedParkingStrategy — 租用车位按月计费策略PropertyFeeService — 物业费计费服务// 售出车位:按年计费 @Component public class SoldParkingStrategy implements BillingStrategy { @Override public List<Bill> calculate(ParkingFeeContext ctx) { BigDecimal amount = ctx.getUnitPrice().setScale(2, RoundingMode.HALF_UP); Bill bill = new Bill(); bill.setAmount(amount); bill.setSourceType(ctx.getSourceType()); bill.setSourceId(ctx.getParkingSpotId()); bill.setBillingPeriodStart(ctx.getBillingPeriodStart()); bill.setBillingPeriodEnd(ctx.getBillingPeriodEnd()); bill.setPaidAmount(BigDecimal.ZERO); bill.setStatus(0); return Collections.singletonList(bill); } } // 租用车位:按月计费,不足月按天折算 @Component public class RentedParkingStrategy implements BillingStrategy { @Override public List<Bill> calculate(ParkingFeeContext ctx) { LocalDate rentStart = ctx.getRentStartDate(); LocalDate rentEnd = ctx.getRentEndDate(); if (rentStart == null || rentEnd == null) { throw new IllegalArgumentException("租用车位必须填写租赁起止日期"); } // 取租期与计费周期的交集 LocalDate effectiveStart = rentStart.isAfter(ctx.getBillingPeriodStart()) ? rentStart : ctx.getBillingPeriodStart(); LocalDate effectiveEnd = rentEnd.isBefore(ctx.getBillingPeriodEnd()) ? rentEnd : ctx.getBillingPeriodEnd(); // 逐月生成账单 List<Bill> bills = new ArrayList<>(); YearMonth sm = YearMonth.from(effectiveStart); YearMonth em = YearMonth.from(effectiveEnd); for (YearMonth ym = sm; !ym.isAfter(em); ym = ym.plusMonths(1)) { // 整月 → 单价;不足月 → 单价/当月天数×实际天数 boolean isFullMonth = billStart.equals(ym.atDay(1)) && billEnd.equals(ym.atEndOfMonth()); BigDecimal amount = isFullMonth ? ctx.getUnitPrice() : ctx.getUnitPrice().multiply(BigDecimal.valueOf(actualDays)) .divide(BigDecimal.valueOf(daysInMonth), 2, RoundingMode.HALF_UP); // 组装 Bill 对象... bills.add(bill); } return bills; } } 3.3.3 关键业务约束售出车位不可直接转为租用状态,须校验所有权状态及未清账单费率配置基于 effective_date 版本化查询,不使用 is_current 标志 - 账单状态:0=待缴、1=已缴、2=部分缴;逾期为业务逻辑判断(当前日期 > 截止日期且状态≠已缴)枚举字段(fee_type、parking_spot.type、resident.type)均为 tinyint 整数3.4. 数据库设计请结合总体设计文档.md 和 /database-design、/property-business-rules 技能,为‘居民小区物业管理系统’生成完整的 MySQL 初始化 DDL 建表脚本。 要求包含:房屋表(含面积)、业主/租户表、车位表(区分售出与租用)、物业费账单表、车位费账单表。每张表必须包含主键、索引、字段注释以及审计字段(created_at, updated_at, deleted_at)。3.4.1 设计规范符合第三范式(3NF)所有表包含审计字段:created_at、updated_at、deleted_at(软删除)关联表外键添加 ON DELETE CASCADE字符集:utf8mb4 + utf8mb4_unicode_ci主键:BIGINT AUTO_INCREMENT3.4.2 数据表清单序号表名说明1t_community小区表2t_fee_config费率配置表(版本化)3t_building楼栋表4t_unit单元表5t_room房间表(冗余community_id反范式)6t_resident人员表(业主/租户)7t_user系统用户表8t_room_resident_binding房间-人员绑定表9t_parking_spot车位表10t_parking_assignment车位分配表11t_bill账单表(多态引用 source_type+source_id)12t_payment缴费记录表13t_fee_type费用类型字典表14t_pay_method缴费方式字典表15t_bill_status账单状态字典表16t_bind_type绑定类型字典表3.4.3 关键设计决策决策方案原因账单关联source_type + source_id 多态引用避免可空外键,支持物业费/车位费统一账单表费率查询纯基于 effective_date 查询避免布尔标志的并发更新风险房间冗余t_room 冗余 community_id减少账单生成时的多表 JOIN,提升查询性能软删除deleted_at 字段保留数据审计追溯能力3.5 API 接口设计请结合项目背景与需求.md和刚才的数据库结构,加载 api-spec-designer 和 /property-business-rules 技能,帮我设计 RESTful API 接口规范。 需包含以下核心模块的接口: 房屋与业主模块:房屋 CRUD、业主绑定与解绑接口。 停车位模块:车位状态变更、售出/租用信息登记接口。 费用与账单模块:生成房屋物业费账单、生成车位费账单(区分售出按年/租用按月)、账单支付与催缴查询接口。 请输出 Swagger/OpenAPI YAML 格式或清晰的 Markdown 接口规范文档,明确每个接口的请求路径、HTTP 方法、请求参数(Header/Query/Body)及响应示例。3.5.1 接口规范遵循 RESTful 规范,OpenAPI 3.0 YAML 文档(v2.2.0)统一 /v1 前缀 - 统一响应格式:{ code, message, data }JWT Bearer Token 认证@RestController @RequestMapping("/v1") @RequiredArgsConstructor public class BillingController { // 分页查询账单 @GetMapping("/bills") public ApiResponse<Page<BillDTO>> listBills( @RequestParam int page, @RequestParam int size, @RequestParam(required = false) Long communityId, @RequestParam(required = false) Integer status) { return ApiResponse.ok(billingService.listBills(page, size, communityId, status)); } // 生成物业费账单 @PostMapping("/bills/generate-property-fee") public ApiResponse<List<BillDTO>> generatePropertyFee( @Valid @RequestBody GeneratePropertyFeeRequest request) { return ApiResponse.ok(billingService.generatePropertyFee(request)); } // 删除账单 @DeleteMapping("/bills/{id}") public ApiResponse<Void> deleteBill(@PathVariable Long id) { billMapper.deleteById(id); return ApiResponse.ok(); } } 3.5.2 接口清单模块接口数主要操作认证 (auth)4登录、登出、用户 CRUD小区 (community)12小区/楼栋/单元/房间/费率 CRUD人员 (resident)5人员 CRUD、房间绑定车位 (parking)5车位 CRUD、分配/释放、到期预警账单 (billing)6生成物业费/车位费、账单查询缴费 (payment)5缴费录入、按账单查询、逾期查询看板 (dashboard)3费用汇总、收缴率、逾期统计3.5.3 删除接口接口方法路径删除小区DELETE/v1/communities/{id}删除楼栋DELETE/v1/buildings/{id}删除单元DELETE/v1/units/{id}删除房间DELETE/v1/rooms/{id}删除人员DELETE/v1/residents/{id}删除车位DELETE/v1/parking-spots/{id}删除账单DELETE/v1/bills/{id}删除缴费DELETE/v1/payments/{id}3.6 前端页面3.6.1 页面清单页面路径功能登录/loginJWT 认证登录仪表盘/dashboard费用汇总、收缴率、ECharts 图表小区管理/communities小区列表、新增、删除楼栋管理/buildings楼栋列表、新增、删除房间管理/rooms房间列表、新增、编辑、删除人员管理/residents人员列表、新增、编辑、删除车位管理/parking车位列表、新增、删除、分配/释放、到期预警费率配置/fee-configs费率列表、新增费用账单/bills账单列表、生成物业费/车位费、删除缴费管理/payments缴费列表、录入缴费、逾期查询、删除用户管理/users系统用户列表、新增、删除3.6.2 交互特性Element Plus 组件库统一 UI 风格el-popconfirm 二次确认删除操作el-pagination 分页查询el-tag 状态标签(已缴/待缴/逾期等)ECharts 数据可视化看板Axios 请求拦截器自动注入 JWT Token<el-popconfirm title="确定删除该楼栋?" @confirm="handleDelete(row.id)"> <template #reference> <el-button link type="danger">删除</el-button> </template> </el-popconfirm> <script setup> async function handleDelete(id) { await deleteBuilding(id); ElMessage.success('删除成功'); loadData(); } </script> 删除操作使用Element Plus的el-popconfirm组件包裹,用户点击删除按钮后弹出确认气泡,防止误操作。确认后调用后端删除接口,成功刷新列表并给出反馈,保证交互一致性与数据安全。// 请求拦截:自动注入 JWT Token request.interceptors.request.use(config => { const auth = useAuthStore(); if (auth.token) config.headers.Authorization = `Bearer ${auth.token}`; return config; }); // 响应拦截:统一错误处理,401 自动登出 request.interceptors.response.use( response => { const res = response.data; if (res.code !== 200 && res.code !== 0) { ElMessage.error(res.message || '请求失败'); if (res.code === 401) { auth.logout(); router.push('/login'); } return Promise.reject(new Error(res.message)); } return res; }, error => { if (error.response?.status === 401) { auth.logout(); router.push('/login'); } ElMessage.error(error.message || '网络错误'); return Promise.reject(error); } ); 请求拦截器从Pinia状态中取出JWT Token,自动注入Authorization头,实现无感认证。响应拦截器统一处理业务错误与HTTP异常,401时自动清除登录态并跳转登录页,避免用户停留在无权限页面。四、总结与反思4.1 遇到的问题及解决方法JDK 版本降级。 项目初始选型Spring Boot 3.3 + Java 21,开发环境实际仅有 JDK 8,降级至Spring Boot 2.7.18 + Java 8,所有jakarta.* 包需全量替换为 javax.*,耗费无效Tokens。技术选型前必须先确认目标环境的软件版本。DELETE 请求跨域被拒。前后端分离架构下,浏览器发起DELETE请求时因后端未配置CORS,接口返回405错误。排查后发现Spring Boot脚手架中遗漏了 CorsFilter 配置。解决方案:将CORS配置纳入项目初始化模板,与JWT、统一响应等组件同步搭建。Windows下文件编码损坏。使用PowerShell的Set-Content命令生成SQL文件时,中文注释变为乱码,影响DDL脚本可读性。原因是Set-Content默认编码为ASCII。解决方案:Windows环境下统一使用文本编辑器或专用工具保存UTF-8文件,避免依赖shell重定向。前后端枚举值不一致。前端传递费用类型时使用字符串(如"PROPERTY"、“SOLD”),后端接口期望tinyint整数(1、2、3),导致JSON反序列化失败,接口联调频繁报错。解决:在OpenAPI 3.0规范文档中显式约定所有枚举字段的整数值映射,前端据此同步定义常量,彻底消除类型歧义。4.2 总结与收获架构设计方面:策略模式在车位计费场景的成功落地,验证了开闭原则在物业系统中的适用性,新增车位类型只需扩展策略类,现有逻辑零修改。账单表采用source_type + source_id多态引用,降低了关联查询复杂度,为多源数据统一管理提供了可复用的设计模式。开发流程方面:“环境先行”与“契约驱动”是最深刻的教训。JDK版本降级的返工成本表明,技术选型须以生产环境约束为前提。OpenAPI规范先行、枚举值显式约定等措施,显著降低了前后端联调摩擦。初始数据脚本化、CORS 等横切关注点的模板化,指明了项目脚手架标准化的改进方向。质量保障方面:核心计费策略已通过单元测试覆盖整月、不足月、租期交集及异常防御等全部分支,未来需补充 Controller 层集成测试与前端E2E测试,形成更完整的验证闭环。仓库地址:居民小区物业管理系统
总条数:697 到第
上滑加载中