> 本文记录使用华为云码道 CodeArts 代码智能体,从 0 到 1 开发一款 MBTI 趣味人格测试微信小程序的全过程。原生微信小程序 + 本地存储,50 道题 / 16 型人格 / canvas 海报 / 零后端开箱即用。
[一键开通华为云码道 CodeArts 代码智能体](https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwatomgit1&sourcead=dmzntgwatomgithd)
一、项目背景
MBTI 人格测试这两年在社交平台上火得一塌糊涂,朋友圈、小红书到处都是"我是 INFP""你居然是 ENTJ"的讨论。作为一个经常被朋友拉去做各种测试的开发者,我一直在想:能不能做一个属于自己的 MBTI 测试微信小程序?要求不高——题要够多、结果要好看、能生成海报分享、不用服务器也能跑。
于是「趣测人格」诞生了。更让我意外的是,这次开发全程使用华为云码道 CodeArts 代码智能体完成,从需求确认、方案设计到代码实现、问题修复,AI 智能体全程辅助,让我对"AI 辅助开发"有了非常具体的认识。
核心理念
做完 50 道题,得出你的 4 字母人格类型,配上详细&解读和海报,一键分享到朋友圈。
听起来简单,背后是一套完整的设计:
- 4 个独立维度(;E/I、S/N、T/F、J/P,每维度 12-13 题,互不干扰
- 16 型人格详细解读:标签、优势、短板、适合职业、恋爱相处、成长建议
- 中途退出自动保存草稿,下次进入继续作答
- canvas 绘制分享海报,保存到相册
- 本地存储历史记录,换设备前都不会丢
- 人格百科免答题直接浏览 16 型
技术栈
- 原生微信小程序:WXML + WXSS + JS(零构建依赖,开发者工具直接打开)
- 本地存储:wx.getStorageSync / setStorageSync,零后端
- canvas 海报:wx.createCanvasContext 绘制 + canvasToTempFilePath
- 16 人格人物形象:emoji 默认 + 图片位自动回退
- 个人主体小程序,已注册正式号并上传体验版
二、从需求到上线:开发全流程
第一阶段:需求确认
我提供了一份完整的功能方案文档给智能体,它没有直接开始写代码,5先确认了几个关键决策:
1. 前端技术栈:原生微信小程序 vs uni-app → 选原生(零构建依赖,开箱即用)
2. 后端交付方式:本地存储版 vs 全栈 vs 云开发 → 选本地存储版(先跑起来)
3. 题库规模:20 题精简版 vs 60 题完整版 → 先做 20 题,后扩到 50 题
这个阶段让我意识到:方案文档越完整,AI 的产出质量越高。我把用户端、后台、数据统计、合规避坑都写进去了,智能体据此给出了非常贴合的实现。
第二阶段:项目搭建
智能体规划了完整的项目结构:
mbti-miniprogram/ ├── app.js / app.json / app.wxss ├── project.config.json ├── config/ │ ├── site.js # 站点文案、免责声明、功能开关 │ └── defaults.js # 计分规则、维度定义、平分默认倾向 ├── data/ │ ├── questions.js # 50 题题库(EI 13 + SN 13 + TF 12 + JP 12) │ └── types.js # 16 型人格数据(解读、占比、配色、形象) ├── utils/ │ ├── scorer.js # 计分核心逻辑 │ ├── storage.js # 本地存储封装 │ └── util.js # 日期格式化、ID 生成 ├── images/ │ ├── avatar.png # 小程序头像 │ └── avatars/ # 16 人格人物形象图位 └── pages/ ├── index/ # 首页 ├── test/ # 答题页 ├── result/ # 结果页(海报生成) ├── history/ # 历史记录 ├── encyclopedia/ # 人格百科列表 ├── type-detail/ # 人格详情 └── privacy/ # 隐私说明第三阶段:代码编写
智能体按"骨架 → 数据 → 工具层 → 页面"的顺序逐步实现,每一步都有明确验收标准:
1. 全局配置(app.json / app.js / app.wxss / project.config.json)
2. 20 题题库 + 16 型人格数据
3. 计分核心逻辑 + 本地存储封装
4. 首页(免责声明弹窗、草稿恢复)
5. 答题页(进度条、自动跳题、上一题、草稿保存)
6. 结果页(分数可视化、解读、canvas 海报、保存相册)
7. 历史记录页、人格百科、人格详情、隐私页
8. README + 完整性验证
代码质量亮点:
1. 计分逻辑纯函数化:createEmptyScores / applyAnswer / computeType,易于验证
2. 存储层统一封装:所有 wx.getStorageSync 调用收敛到 utils/storage.js
3. 配置与代码分离:文案、开关、计分规则都在 config/ 里,改文案不用动逻辑
4. 16 人格数据完整:每型都有标签、优势、短板、职业、恋爱、成长建议
第四阶段:测试验证
由于环境没有 Node,我用 Python 写了校验脚本验证:
- 全部 10 个 JSON 文件合法性 ✓
- 50 题 ID 连续、维度分布正确(EI 13 / SN 13 / TF 12 / JP 12)✓
- 16 型人格数据完整(每型 7 个必填字段、3+ 条优势短板)✓
- 所有 JS 文件括号平衡 ✓
- 每题选项 scoreTo 与维度配置匹配 ✓
验证结果:全部通过,零错误。
第五阶段:内测上线
代码写完后,走微信小程序内测流程:
1. 微信公众平台注册正式小程序账号(个人主体,免费)
2. 完善基础信息和小程序类目
3. 微信开发者工具填入正式 AppID → 点「上传」
4. 后台「版本管理」设为“体验版”
5. 添加体验成员 → 扫码即可使用
⚠️关键:体验版不需要备案、不需要认证、不需要审核,当天就能扫码内测。备案和认证只有正式发布上架时才卡。
三、实现功能
| 页面 | 说明 |
|---|---|
| 首页 | 品牌介绍、免责声明弹窗(首次进入测试前必须阅读)、开始测试、继续上次测试、历史记录、人格百科入口 |
| 答题页 | 顶部进度条、维度标签、选择后自动跳下一题、上一题返回修改、中途退出自动保存草稿、禁止跳过 |
| 结果页 | 4 字母人格 + 四个维度分数可视化进度条 + 标签/优势/短板/职业/恋爱/成长解读 + canvas 海报生成 + 保存相册 |
| 历史记录 | 本地记录列表、点击重看报告、单条删除、清空全部 |
| 人格百科 | 16 型人格按四大类分组浏览,免登录免答题直接查看 |
| 人格详情 | 任一类型的完整解读 + 分享 |
| 隐私说明 | 隐私协议与免责声明全文 |
首页
- 品牌标题 + 简短介绍
- 免责声明弹窗(首次进入测试前必须阅读,同意后存本地不再弹)
- 开始测试按钮 + 测试信息(共 50 道题 · 预计 8 分钟 · 免费)
- 继续上次测试(检测到草稿时显示,可接着作答)
- 历史记录、人格百科、隐私说明入口
答题页
- 顶部进度条:当前第几题 / 总题数,实时更新
- 维度标签:显示该题测哪个维度(如"外向 E · 内向 I")
- 单选选择,选中后自动跳转下一题(300ms 延迟,有过渡动画)
- 上一题:允许5许返回修改答案
-@中途退出自动保存草稿,下次进入继续
- 禁止跳过:必须选择才能继续
结果报告页
- 4 字母人格类型大字展示 + 中文名 + 英文名
- 四个维度分数可视化:左右对比进度条,高分项高亮
- 人格标签 chips
- 详细解读 tabs:优势 / 短板 / 适合职业 / 恋爱相处 / 成长建议
- 人群占比(如"该类型约占人群 4.4%")
- canvas 海报生成 + 保存到相册
- 分享好友(onShareAppMessage 配置分享卡片)
- 重新测试、查看历史、人格百科详情入口
历史记录页
- 列出历次测试结果:类型色块 + 中文名 + 时间
- 点击重新查看完整报告
- 单条删除(带确认弹窗)
- 清空全部记录
人格百科
- 16 型人格按四大类分组:分析家 / 外交家 / 守护者 / 探险家
- 每型显示人物形象头像(emoji 默认,放图片后自动切换)
- 免答题直接浏览,点击进入详情页
海报生成
- canvas 绘制:主题色渐变背景 + 类型大字 + 中文名 + 一句话 + 模拟二维码 + 免责声明
- wx.canvasToTempFilePath 导出临时图片
- wx.saveImageToPhotosAlbum 保存到相册(含授权处理)
四、项目运行效果
> 以下为小程序各页面运行截图
首页
紫色渐变背景,品牌名居中,开始测试按钮带阴影,下方功能入口卡片式排列。
答题页
进度条实时更新,选项选中后高亮带勾,自动跳转下一题。
结果页
hero 区主题色渐变,人物头像圆形展示,维度分数左右对比进度条,解读 tabs 切换。
也可保存到系统相册中。
五、项目架构
计分核心逻辑
四个独立维度,每维度二选一,互不干扰:
function createEmptyScores() { return { E: 0, I: 0, S: 0, N: 0, T: 0, F: 0, J: 0, P: 0 } } function applyAnswer(scores, question, choiceLabel) { const opt = question.options.find(o => o.label === choiceLabel) if (opt) { scores[opt.scoreTo] = (scores[opt.scoreTo] || 0) + 1 } } function computeType(scores) { const tie = scoringConfig.tiePreference return ( (scores.E > scores.I || (scores.E === scores.I && tie.EI === 'E') ? 'E' : 'I') + (scores.S > scores.N || (scores.S === scores.N && tie.SN === 'S') ? 'S' : 'N') + (scores.T > scores.F || (scores.T === scores.F && tie.TF === 'T') ? 'T' : 'F') + (scores.J > scores.P || (scores.J === scores.P && tie.JP === 'J') ? 'J' : 'P') ) }边界处理:分数相等时按 `config/defaults.js` 的 `tiePreference` 取默认倾向(E/S/T/J 优先,可配置)。
本地存储封装
所有 storage 操作收敛到 utils/storage.js,结构清晰:
const KEYS = { DRAFT: 'mbti_draft', // 答题草稿 HISTORY: 'mbti_history', // 历史记录 USER: 'mbti_userinfo', // 用户信息 DISCLAIMER_AGREED: 'mbti_disclaimer_agreed' // 免责声明确认 } function addHistory(record) { const list = getHistory() list.unshift(record) if (list.length > HISTORY_LIMIT) list.length = HISTORY_LIMIT // 最多 30 条 set(KEYS.HISTORY, list) return list }草稿结构:{ answers: {1: 'A', 2: 'B', ...}, currentIndex, updatedAt },每次选择都保存,中途退出不丢。
canvas 海报绘制
drawPoster(callback) { const ctx = wx.createCanvasContext('posterCanvas', this) // 渐变背景 const grad = ctx.createLinearGradient(0, 0, 0, 600) grad.addColorStop(0, typeInfo.color) grad.addColorStop(1, '#1F1B4D') ctx.setFillStyle(grad) ctx.fillRect(0, 0, 375, 600) // 类型大字 ctx.setFillStyle('#FFFFFF') ctx.setFontSize(46) ctx.fillText(type, 187, 128) // ... 中文名、一句话、模拟二维码、免责声明 ctx.draw(false, () => callback()) }canvas 放在屏幕外(`left: -3000rpx`),绘制完成后 `canvasToTempFilePath` 导出,再 `saveImageToPhotosAlbum` 保存,含授权拒绝后引导去设置页的处理。
16 人格数据结构
每型人格包含完整解读:
INFP: { code: 'INFP', name: '调停者', en: 'Mediator', group: 'diplomats', percent: '4.4%', color: '#E84393', emoji: '🌷', avatar: '/images/avatars/INFP.png', tagline: '内心柔软的理想主义者,守护着心中美好的乌托邦。', tags: ['温柔', '理想主义', '创造力', '真诚'], overview: '...', strengths: ['想象力和创造力丰富', '温柔真诚,容易让人信任', ...], weaknesses: ['过于敏感,容易内耗', '回避冲突', ...], careers: ['作家', '插画师', '心理咨询师', ...], love: '...', growth: ['把创意付诸行动', '练习直接表达需求', ...] }六、开发过程中踩的坑
坑一:首页中间说明文字遮挡布局
症状:首页 hero 区有一行半透明说明文字(毛玻璃效果),视觉上挡住了品牌区,用户反馈"中间有行字挡住了"。
根因:`hero-intro` 用了 `rgba(255,255,255,0.16)` 背景 + padding,在紫色背景上形成一个横条,挤占了视觉空间。
解决:删除 `hero-intro` 块,把测试信息(共 50 道题 · 预计 8 分钟)改成开始按钮下方的小字提示条,背景用 `rgba(255,255,255,0.7)`,清晰不遮挡。
教训:移动端 hero 区要克制,信息层级要分明,装饰性元素容易喧宾夺主。
坑二:canvas 海报绘制清晰度
症状:旧版 canvas API(wx.createCanvasContext)绘制的海报在部分机型上模糊。
根因:旧版 canvas 按逻辑像素绘制,未适配 devicePixelRatio。
解决:canvas 尺寸设为 375×600(与设计稿一致),绘制时按固定像素坐标,导出时用 `canvasToTempFilePath` 的默认参数。清晰度可接受,如需更高清可改用 `canvas type="2d"` 新 API。
教训:小程序 canvas 有新旧两套 API,旧版兼容性好但清晰度一般,新版清晰度高但代码复杂,按需选择。
坑三:16 人格人物图片加载失败
症状:用户没放人物图片时,`<image src="/images/avatars/INTJ.png">` 显示空白破图。
根因:图片文件不存在,image 组件默认显示空白。
解决:用 `binderror` 事件回退到 emoji:
<image wx:if="{{t.showAvatar}}" src="{{t.avatar}}" binderror="onAvatarError">onAvatarError(e) { const code = e.currentTarget.dataset.code // 遍历 groups 找到对应 type,设 showAvatar = false }教训:图片资源要始终有兜底方案,binderror 是小程序里处理图片加载失败的标准做法。
坑四:草稿恢复时题目索引错位
症状:用户中途退出再进入,恢复到的题目位置不对。
根因:草稿里 `currentIndex` 和 `answers` 的数量可能不一致(用户返回修改过前面的题)。
解决:恢复时以 `draft.currentIndex` 为准(而不是 answers 的数量),重新从 answers 重建 scores,渲染当前题并高亮已选项。每次选择都更新 `currentIndex = index + 1`。
教训:草稿恢复要明确"当前位置"和"已答数量"是两个概念,前者决定显示哪题,后者决定进度。
七、性能优化实践
优化一:本地存储代替后端请求
所有数据存 wx.storage,零网络请求,秒开秒存:
// 每次选择都存草稿,不丢进度 this.draft.answers[q.id] = label this.draft.currentIndex = index + 1 store.saveDraft(this.draft)做完题目一次性提交答案生成记录,减少请求(虽然本地版根本没请求)。
优化二:自动跳题用 setTimeout 而非立即跳转
onChoose(e) { this.setData({ selected: label }) setTimeout(() => { this.renderQuestion(next, ...) }, 300) }300ms 延迟让用户看到选中高亮动画,体验更顺滑,避免瞬间切换的突兀感。
优化三:canvas 离屏绘制
.poster-canvas { position: fixed; left: -3000rpx; /* 移到屏幕外 */ top: 0; width: 750rpx; height: 1200rpx; }canvas 始终存在于页面但不占视觉空间,点击"保存海报"时直接绘制,无需动态创建。
优化四:历史记录限制条数
const HISTORY_LIMIT = 30 function addHistory(record) { const list = getHistory() list.unshift(record) if (list.length > HISTORY_LIMIT) list.length = HISTORY_LIMIT set(KEYS.HISTORY, list) }本地存储空间有限,历史记录最多保留 30 条,超出自动裁剪最早的。
八、个人心得体会
借助华为云码道 CodeArts 完成「趣测人格」小程序开发,我体会到 AI 辅助开发的几个要点:
1. 方案文档是关键:我提供了一份完整的功能方案(用户端、后台、合规避坑全写清楚),智能体据此给出的实现非常贴合,几乎不用大改。模糊的需求只会得到模糊的产出。
2. AI 生成代码仍需校验:智能体写的代码整体质量不错,但仍有细节要人工把关——比如首页说明文字遮挡、图片加载失败回退,都是实际运行后才发现的问题。AI 省的是编码时间,不是思考成本。
3. 小步验证很重要:每写完一批文件就用 Python 脚本校验 JSON 合法性、数据结构、括号平衡,早发现问题早修复,避免到最后积压一堆 bug。
4. 内测优先于发布:小程序不需要一上来就走备案+认证+审核的完整发布流程,先上传体验版扫码内测,快速迭代,等稳定了再正式发布。这次项目当天就跑起了内测版本。
当前状态:项目已形成内测版本,代码已上传微信小程序体验版,添加体验成员后扫码即可使用。
后续计划:补齐 16 人格人物形象图、走备案流程正式发布上线。
如果你也想体验这种 AI 辅助开发的效率,欢迎一键开通华为云码道 CodeArts 代码智能体!
总结
通过华为云码道 CodeArts 代码智能体,我从一份方案文档出发,快速完成了这款 MBTI 趣味人格测试小程序的开发。50 道题、16 型人格、canvas 海报、本地存储、零后端,当天形成内测版本扫码可玩。从需求到内测上线,智能体提供了全方位辅助,大大提升了开发效率。
本项目已托管到 AtomGit 公开仓库:mbti:原生微信小程序实现的 MBTI 趣味人格测试,共 50 道题 / 16 型人格,数据本地存储,零后端开箱即用。 - AtomGit
关键词:#华为云码道 #CodeArts #代码智能体 #AI辅助开发 #微信小程序 #MBTI #人格测试 #前端开发
标签:#华为云 #微信小程序 #MBTI #前端开发 #AI编程