Node.js调用春联生成模型实战构建节日祝福微信小程序马上又要过年了你是不是还在为写不出有新意的春联而发愁或者作为开发者想给自己的小程序加点节日气氛却苦于没有合适的创意内容来源今天我就来分享一个特别应景的实战项目用Node.js搭建一个后端服务调用AI春联生成模型然后把它塞进一个微信小程序里。用户只需要输入几个关键词比如“健康”、“发财”就能立刻得到一副为他量身定制的、文采斐然的春联。整个过程从后端到前端我会手把手带你走一遍即使你之前没怎么接触过Node.js或者小程序开发跟着做也能跑起来。这个项目特别适合想学习全栈开发或者想给现有应用增加AI能力的同学。做完之后你不仅收获一个能实际用起来的小程序还能掌握一套标准的服务集成流程。1. 项目整体思路与准备工作在开始敲代码之前我们先理清整个项目是怎么跑的。想象一下用户的操作路径他在小程序里输入关键词点击生成小程序把这个请求发给我们的Node.js服务器Node.js服务器去调用那个专业的春联生成模型拿到生成的春联文本最后服务器把春联文本返回给小程序小程序再漂亮地展示出来。所以我们的核心任务就两块搭建Node.js后端服务负责接收请求、调用AI模型API、返回结果。开发微信小程序前端负责提供输入界面、发送请求、展示结果。为了让这个流程跑通我们需要先准备好几样“工具”。1.1 环境与工具清单首先确保你的电脑上已经安装了Node.js。如果没有去Node.js官网下载最新的长期支持版本安装就行安装过程很简单一路点“下一步”即可。安装好后打开命令行工具输入node -v和npm -v如果能显示出版本号说明安装成功。接下来我们需要一个代码编辑器VS Code就非常合适。然后去微信公众平台注册一个小程序账号拿到小程序的AppID这是后续开发的“通行证”。最关键的一环是找到能提供春联生成能力的AI模型服务。现在很多AI开放平台都有类似的功能你需要根据它们的文档去申请API密钥。通常你需要关注两个参数一个是API Key用来证明你的身份另一个是API Base URL就是你要调用的那个服务的具体地址。把这两个信息记下来我们后面会用到。1.2 初始化项目结构我们先来创建后端项目。在你的工作目录下新建一个文件夹比如叫couplet-server。然后打开命令行进入这个文件夹执行下面的命令来初始化一个Node.js项目npm init -y这个命令会生成一个package.json文件它就像是项目的“说明书”。接下来我们要安装项目运行所依赖的几个核心库npm install express axios cors dotenv我来简单解释一下这几个库是干嘛的express一个非常流行的Node.js web框架用来快速搭建我们的服务器。axios一个用来发送HTTP请求的库比Node.js自带的http模块更好用。我们用它来调用AI模型的API。cors一个处理跨域资源共享的中间件。因为我们的前端小程序和后端Node.js服务可能不在同一个域名下需要这个来允许跨域请求。dotenv用来管理环境变量。我们可以把API密钥这些敏感信息放在一个单独的.env文件里避免硬编码在代码中。安装完成后你的package.json里的dependencies部分应该能看到它们。2. 构建Node.js后端服务后端是我们的“大脑”所有复杂的逻辑和外部调用都在这里发生。我们一步步来搭建。2.1 创建基础服务器在项目根目录下创建一个名为app.js的文件这是我们的主服务器文件。把下面的代码贴进去// app.js const express require(express); const cors require(cors); require(dotenv).config(); // 加载环境变量 const app express(); const port process.env.PORT || 3000; // 可以从环境变量读取端口默认3000 // 使用中间件 app.use(cors()); // 启用CORS允许跨域请求 app.use(express.json()); // 解析JSON格式的请求体 // 一个简单的测试接口确认服务已启动 app.get(/, (req, res) { res.json({ message: 春联生成后端服务正在运行 }); }); // 春联生成接口 - 我们先留个空后面实现 app.post(/api/generate, (req, res) { res.json({ message: 生成接口待实现 }); }); // 启动服务器 app.listen(port, () { console.log(后端服务已启动运行在 http://localhost:${port}); });现在在命令行里运行node app.js如果看到“后端服务已启动”的提示并且在浏览器里访问http://localhost:3000能看到欢迎信息那么恭喜你最基础的服务器已经跑起来了2.2 封装AI模型调用这是最核心的一步。我们在项目根目录下创建一个新的文件夹services然后在里面创建一个coupletService.js文件。这个文件专门负责和AI模型“对话”。首先我们需要配置环境变量。在项目根目录创建一个名为.env的文件注意前面有个点然后把你的API密钥和地址填进去# .env 文件 API_KEY你的_AI_模型_API_密钥 API_BASE_URL你的_AI_模型_API_地址 MODEL_NAME春联生成模型的名称 # 例如chinese-couplet-base重要提示.env文件里包含你的密钥千万不要把它上传到GitHub等公开代码仓库你应该把.env添加到你的.gitignore文件中。接下来我们编写服务逻辑// services/coupletService.js const axios require(axios); // 从环境变量读取配置 const API_KEY process.env.API_KEY; const API_BASE_URL process.env.API_BASE_URL; const MODEL_NAME process.env.MODEL_NAME; class CoupletService { /** * 根据用户输入的关键词生成春联 * param {string} keywords - 用户输入的关键词如“健康 发财” * returns {PromiseObject} - 返回生成的春联数据 */ static async generateCouplet(keywords) { // 1. 准备请求数据 const requestData { model: MODEL_NAME, messages: [ { role: user, // 这里的提示词prompt很关键告诉模型我们要什么 content: 请根据以下关键词创作一副春节对联要求对仗工整寓意吉祥富有文采。关键词${keywords}。请直接返回上下联和横批格式为上联... 下联... 横批... } ], // 以下参数可以根据模型的具体要求调整用于控制生成效果 max_tokens: 150, // 生成文本的最大长度 temperature: 0.8, // 控制创意程度值越高越有创意 }; // 2. 设置请求头通常需要携带API Key进行认证 const headers { Content-Type: application/json, Authorization: Bearer ${API_KEY} // 常见的认证方式 }; try { console.log(正在请求模型生成春联关键词${keywords}); // 3. 发送POST请求到AI模型API const response await axios.post(API_BASE_URL, requestData, { headers }); // 4. 解析响应数据 // 注意不同AI平台的响应结构可能不同这里需要根据实际情况调整 const aiResponse response.data; let generatedText ; // 假设响应结构是 { choices: [{ message: { content: ... } }] } if (aiResponse.choices aiResponse.choices[0] aiResponse.choices[0].message) { generatedText aiResponse.choices[0].message.content; } else { // 如果结构不一样尝试其他常见字段 generatedText aiResponse.result || aiResponse.text || aiResponse.content || JSON.stringify(aiResponse); } console.log(模型返回原始内容${generatedText}); // 5. 从返回的文本中提取上联、下联和横批 const coupletData this._parseCoupletText(generatedText); return { success: true, keywords: keywords, ...coupletData }; } catch (error) { console.error(调用春联生成API失败, error.message); // 返回一个友好的错误信息而不是把技术细节暴露给前端 return { success: false, message: 生成春联时出了点小问题请稍后再试或换个关键词试试。, error: error.message }; } } /** * 辅助方法从模型返回的文本中解析出上联、下联和横批 * param {string} text - 模型生成的原始文本 * returns {Object} - 包含上联、下联、横批的对象 */ static _parseCoupletText(text) { // 初始化默认值 let upperLine ; let lowerLine ; let horizontalScroll ; // 尝试用正则表达式匹配常见的格式 // 例如上联XXXX 下联XXXX 横批XXXX const upperMatch text.match(/上联[:]\s*([^下联横批])/); const lowerMatch text.match(/下联[:]\s*([^上联横批])/); const scrollMatch text.match(/横批[:]\s*([^上联下批])/); if (upperMatch) upperLine upperMatch[1].trim(); if (lowerMatch) lowerLine lowerMatch[1].trim(); if (scrollMatch) horizontalScroll scrollMatch[1].trim(); // 如果没匹配到尝试其他常见分隔符 if (!upperLine || !lowerLine) { const lines text.split(/[\n\r]/).filter(line line.trim()); if (lines.length 3) { upperLine lines[0].replace(/^上联[:]\s*/, ).trim(); lowerLine lines[1].replace(/^下联[:]\s*/, ).trim(); horizontalScroll lines[2].replace(/^横批[:]\s*/, ).trim(); } } // 如果还是没提取到就把整个文本作为上联兜底策略 if (!upperLine) { upperLine text.trim(); } return { upperLine: upperLine || 福星高照全家福, lowerLine: lowerLine || 春光耀辉满堂春, horizontalScroll: horizontalScroll || 吉祥如意 }; } } module.exports CoupletService;这段代码做了几件重要的事它构建了一个符合AI模型要求的请求发送出去拿到返回的文本然后像“拆礼物”一样从文本里把上联、下联和横批找出来。我们还做了错误处理万一网络不好或者API出问题会给用户一个友好的提示而不是一堆看不懂的错误代码。2.3 完善后端API接口现在我们回到app.js把之前留空的/api/generate接口实现出来。// app.js (续写/修改部分) const CoupletService require(./services/coupletService); // ... 前面的express、cors等初始化代码不变 ... // 春联生成接口 app.post(/api/generate, async (req, res) { try { const { keywords } req.body; // 1. 校验用户输入 if (!keywords || keywords.trim() ) { return res.status(400).json({ success: false, message: 请输入关键词例如健康、发财、平安 }); } // 2. 调用服务生成春联 const result await CoupletService.generateCouplet(keywords.trim()); // 3. 返回结果给前端 if (result.success) { res.json({ success: true, data: { keywords: result.keywords, upperLine: result.upperLine, lowerLine: result.lowerLine, horizontalScroll: result.horizontalScroll, // 可以加个时间戳方便前端缓存或显示 generatedAt: new Date().toISOString() } }); } else { // 如果服务层返回失败 res.status(500).json({ success: false, message: result.message || 生成失败请重试 }); } } catch (error) { console.error(处理生成请求时出错, error); res.status(500).json({ success: false, message: 服务器内部错误请稍后再试 }); } }); // ... 后面的app.listen代码不变 ...这个接口现在就是一个完整的“处理器”了它先检查用户有没有输入关键词然后调用我们刚写好的CoupletService去生成春联最后把生成好的结果或者友好的错误信息打包成JSON格式返回给小程序。为了让代码更健壮我们还可以加一个全局的错误处理中间件放在所有路由定义之后app.listen之前// 全局错误处理中间件放在所有路由之后 app.use((err, req, res, next) { console.error(全局错误捕获, err.stack); res.status(500).json({ success: false, message: 服务器开小差了请稍后再试 }); });好了后端部分大功告成现在你的Node.js服务已经具备了接收请求和生成春联的能力。记得用node app.js重启一下服务。3. 开发微信小程序前端后端准备好了现在我们来打造用户直接能看到和操作的小程序界面。打开微信开发者工具创建一个新的小程序项目填入你申请到的AppID。3.1 小程序页面布局我们主要工作在一个页面上完成比如就叫index页面。先来设计界面修改index.wxml文件!-- index.wxml -- view classcontainer !-- 顶部标题 -- view classheader text classtitleAI春联送祝福/text text classsubtitle输入关键词定制你的专属春联/text /view !-- 输入区域 -- view classinput-section view classinput-label请输入祝福关键词/view input classinput-box placeholder例如健康、发财、学业有成 placeholder-classplaceholder value{{keywords}} bindinputonInputChange maxlength20 / text classinput-tip多个关键词用空格隔开最多20个字/text /view !-- 生成按钮 -- button classgenerate-btn bindtapgenerateCouplet loading{{loading}} {{loading ? 生成中... : 一键生成春联}} /button !-- 结果展示区域 -- view classresult-section wx:if{{showResult}} view classsection-title为您生成的春联/view !-- 春联展示 - 模拟传统样式 -- view classcouplet-display !-- 左侧上联 -- view classcouplet-line upper-line text classcouplet-text{{upperLine}}/text /view !-- 中间横批 -- view classhorizontal-scroll text classscroll-text{{horizontalScroll}}/text /view !-- 右侧下联 -- view classcouplet-line lower-line text classcouplet-text{{lowerLine}}/text /view /view !-- 操作按钮 -- view classaction-buttons button classsecondary-btn bindtapcopyToClipboard复制春联/button button classprimary-btn bindtapgenerateAgain再生成一副/button /view /view !-- 加载状态 -- view classloading wx:if{{loading}} image src/images/loading.gif classloading-icon/image text classloading-textAI正在构思您的专属春联.../text /view !-- 空状态提示 -- view classempty-tip wx:if{{!showResult !loading}} image src/images/couplet-icon.png classempty-icon/image text classempty-text输入关键词生成您的第一副AI春联吧/text /view /view为了让页面好看我们需要一些样式。修改index.wxss/* index.wxss */ .container { padding: 40rpx; min-height: 100vh; background: linear-gradient(135deg, #fdf6e3 0%, #f5e8c7 100%); } /* 头部样式 */ .header { text-align: center; margin-bottom: 60rpx; } .title { display: block; font-size: 48rpx; font-weight: bold; color: #d63031; margin-bottom: 16rpx; } .subtitle { font-size: 28rpx; color: #636e72; } /* 输入区域 */ .input-section { background: white; border-radius: 20rpx; padding: 40rpx; margin-bottom: 40rpx; box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08); } .input-label { font-size: 32rpx; font-weight: 500; color: #2d3436; margin-bottom: 24rpx; } .input-box { height: 88rpx; border: 2rpx solid #dfe6e9; border-radius: 12rpx; padding: 0 24rpx; font-size: 32rpx; margin-bottom: 16rpx; } .input-box:focus { border-color: #0984e3; } .placeholder { color: #b2bec3; } .input-tip { font-size: 24rpx; color: #636e72; } /* 生成按钮 */ .generate-btn { background: linear-gradient(90deg, #d63031, #e17055); color: white; border-radius: 50rpx; height: 96rpx; font-size: 34rpx; font-weight: 500; margin: 40rpx 0; } .generate-btn:active { opacity: 0.9; } /* 结果区域 */ .result-section { background: white; border-radius: 20rpx; padding: 40rpx; margin-top: 40rpx; box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08); animation: fadeIn 0.5s ease; } keyframes fadeIn { from { opacity: 0; transform: translateY(20rpx); } to { opacity: 1; transform: translateY(0); } } .section-title { font-size: 36rpx; font-weight: bold; color: #2d3436; text-align: center; margin-bottom: 50rpx; position: relative; } .section-title::after { content: ; position: absolute; bottom: -12rpx; left: 50%; transform: translateX(-50%); width: 80rpx; height: 4rpx; background: #d63031; border-radius: 2rpx; } /* 春联展示样式 */ .couplet-display { display: flex; flex-direction: column; align-items: center; position: relative; margin: 40rpx 0; } .couplet-line { width: 100%; min-height: 200rpx; background: linear-gradient(90deg, #d63031, #c0392b); border-radius: 12rpx; display: flex; align-items: center; justify-content: center; padding: 30rpx; margin: 20rpx 0; position: relative; box-shadow: 0 6rpx 20rpx rgba(214, 48, 49, 0.3); } .upper-line::before, .lower-line::after { content: ; position: absolute; width: 24rpx; height: 24rpx; background: gold; border-radius: 50%; } .upper-line::before { top: 20rpx; left: 20rpx; } .lower-line::after { bottom: 20rpx; right: 20rpx; } .couplet-text { color: gold; font-size: 40rpx; font-weight: bold; text-align: center; line-height: 1.6; text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.3); } .horizontal-scroll { background: gold; padding: 20rpx 60rpx; border-radius: 10rpx; margin: 30rpx 0; position: relative; z-index: 2; box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.2); } .scroll-text { color: #d63031; font-size: 36rpx; font-weight: bold; } /* 操作按钮 */ .action-buttons { display: flex; justify-content: space-between; margin-top: 50rpx; } .primary-btn, .secondary-btn { flex: 1; height: 80rpx; border-radius: 40rpx; font-size: 30rpx; font-weight: 500; } .primary-btn { background: #0984e3; color: white; margin-left: 20rpx; } .secondary-btn { background: white; color: #0984e3; border: 2rpx solid #0984e3; margin-right: 20rpx; } /* 加载状态 */ .loading { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; } .loading-icon { width: 120rpx; height: 120rpx; margin-bottom: 30rpx; } .loading-text { font-size: 28rpx; color: #636e72; } /* 空状态提示 */ .empty-tip { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 100rpx 0; text-align: center; } .empty-icon { width: 200rpx; height: 200rpx; opacity: 0.6; margin-bottom: 30rpx; } .empty-text { font-size: 28rpx; color: #636e72; line-height: 1.5; }3.2 小程序逻辑实现界面有了现在让它们动起来。修改index.js文件处理用户的输入、点击并调用我们的后端API。// index.js Page({ data: { keywords: , // 用户输入的关键词 loading: false, // 加载状态 showResult: false, // 是否显示结果 upperLine: , // 上联 lowerLine: , // 下联 horizontalScroll: // 横批 }, // 输入框内容变化 onInputChange(e) { this.setData({ keywords: e.detail.value }); }, // 生成春联 async generateCouplet() { const { keywords } this.data; // 1. 输入验证 if (!keywords || keywords.trim() ) { wx.showToast({ title: 请输入关键词, icon: none }); return; } // 2. 显示加载状态 this.setData({ loading: true, showResult: false }); // 3. 准备请求数据 const requestData { keywords: keywords.trim() }; try { // 注意这里需要替换成你实际部署的后端服务地址 // 本地开发时可能是 http://localhost:3000 // 上线后需要换成你的服务器地址 const backendUrl https://your-backend-domain.com; // 请替换为你的后端地址 // 4. 调用后端API const res await wx.request({ url: ${backendUrl}/api/generate, // 你的后端生成接口 method: POST, data: requestData, header: { content-type: application/json } }); // 5. 处理响应 if (res.statusCode 200 res.data.success) { const { upperLine, lowerLine, horizontalScroll } res.data.data; // 更新页面数据展示结果 this.setData({ upperLine, lowerLine, horizontalScroll, showResult: true, loading: false }); // 提示成功 wx.showToast({ title: 生成成功, icon: success, duration: 2000 }); } else { // 处理业务逻辑错误 throw new Error(res.data.message || 生成失败); } } catch (error) { console.error(请求失败, error); // 6. 错误处理 this.setData({ loading: false }); wx.showToast({ title: error.message || 网络错误请重试, icon: none, duration: 3000 }); // 这里可以提供一个默认的春联作为降级方案 this.setData({ upperLine: 福星高照全家福, lowerLine: 春光耀辉满堂春, horizontalScroll: 吉祥如意, showResult: true }); } }, // 复制春联到剪贴板 copyToClipboard() { const { upperLine, lowerLine, horizontalScroll } this.data; const textToCopy 上联${upperLine}\n下联${lowerLine}\n横批${horizontalScroll}; wx.setClipboardData({ data: textToCopy, success: () { wx.showToast({ title: 已复制到剪贴板, icon: success }); } }); }, // 再次生成 generateAgain() { this.setData({ showResult: false, keywords: }); }, // 页面加载时的一些初始化 onLoad() { // 可以在这里检查网络状态或者加载一些默认配置 console.log(春联小程序页面加载完成); } });3.3 配置网络请求域名微信小程序对网络请求有安全限制只能访问事先配置好的域名。所以在你测试和上线前需要在小程序管理后台进行设置。登录微信公众平台。进入你的小程序管理后台。在左侧菜单找到开发-开发管理-开发设置。找到服务器域名配置项。在request合法域名中添加你后端服务部署的域名例如https://your-backend-domain.com。如果你在本地开发还需要在微信开发者工具中点击右上角详情-本地设置勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。注意这仅用于本地开发调试正式上线前必须配置合法域名。4. 项目部署与优化建议代码都写完了本地也能跑通了接下来就是把它放到真正的服务器上让所有人都能访问。4.1 后端服务部署你可以选择任何支持Node.js的云服务平台比如腾讯云、阿里云、或者一些专门的PaaS平台。部署流程大致如下准备生产环境配置在服务器上安装Node.js环境。创建一个生产环境的.env文件填入正确的API密钥和地址。上传代码将你的couplet-server项目代码上传到服务器。安装依赖在服务器上进入项目目录运行npm install --production安装依赖。使用进程管理工具为了让服务稳定运行推荐使用pm2这样的进程管理工具。# 全局安装pm2 npm install -g pm2 # 使用pm2启动你的应用 pm2 start app.js --name couplet-server # 设置开机自启 pm2 startup pm2 save配置域名和SSL为你的服务器绑定一个域名并申请SSL证书比如用Lets Encrypt免费获取启用HTTPS。这是微信小程序要求的。4.2 前端小程序发布后端服务部署好并配置了合法域名后就可以发布小程序了在微信开发者工具中点击上传按钮将小程序代码提交审核。登录小程序管理后台在版本管理中提交审核。审核通过后即可发布上线。4.3 一些优化思路一个能跑通的项目是基础但要让体验更好还可以考虑下面这些优化点加入缓存用户短时间内重复输入相同关键词可以直接返回缓存结果减少API调用次数和等待时间。增加历史记录在小程序端用本地存储保存用户生成过的春联方便查看。丰富生成选项除了关键词可以让用户选择春联的“风格”比如“传统典雅”、“幽默风趣”、“现代简约”通过修改提示词来实现。美化分享功能将生成的春联配上好看的背景图生成一张祝福卡片方便用户分享给好友。后端增加限流防止恶意请求保护你的API调用额度。监控与日志记录API调用成功率、响应时间方便出了问题快速定位。5. 写在最后跟着上面的步骤走下来你应该已经拥有了一个完整的、能生成春联的微信小程序。这个项目虽然不大但涵盖了从后端API封装、服务搭建到前端界面交互、网络请求的全栈开发流程是一个很好的练手项目。实际开发中你可能会遇到各种小问题比如AI模型返回的格式和预期不一样或者网络环境导致请求超时。这时候关键就是看日志、调试、以及准备一个友好的降级方案比如我们代码里提供的默认春联。AI应用开发就是这样充满了不确定性但也正是这种不确定性让结果往往能带来惊喜。希望这个实战案例能给你一些启发。你可以基于这个框架把它改造成生成节日祝福语、创意文案、甚至诗词歌赋的小工具。技术的乐趣就在于用代码把想法变成现实。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。