关于 AI 取名大师
借助豆包、通义千问、DeepSeek 等 AI 大模型,为您的宝宝、宠物、店铺、网名、笔名、项目、产品、服务、文章等取一个专业、有意义的名字😄。
开源地址:👉GitCode(国内友好)👈、👉GitHub👈 技术组合:Bun.js、Elysia.js、uni-app 体验地址:AI取名大师(H5版)
特别注明:本系列文章仅为实战经验分享,并记录开发过程中碰到的问题😄,如有不足之处欢迎随时留言提出。
📣 需求简介
想给取名大师加一个免费试用功能,零成本试用核心功能,降低使用门槛。另外希望通过试用,提升新用户活跃度,为后续付费转化铺垫,同时通过唯一标识控制成本😄。
核心需求
- 可配置化发放:支持通过配置文件自定义试用积分(默认 5 积分),无需修改代码即可调整福利力度;
- 资格精准判断:基于客户端唯一标识(指纹),确保 “同一设备 / 客户端仅能领取一次”,避免羊毛党重复领取;
- 场景化交互:用户访问首页后,若未持有积分券且符合试用资格,自动触发弹窗提示,引导领取;
- 权益绑定闭环:用户点击领取后,系统自动创建积分券并与当前客户端绑定,支持后续服务使用抵扣。
🤔 实施思路
- 首先我们设计一个数据表(trial)用于存放试用情况,记录客户端ID(指纹)、IP地址、录入时间等;
- 用户进入首页,若已有积分券则终止;
- 前端使用FingerprintJS (动态加载)计算客户端指纹,调用后端接口判断是否具备试用资格;
- 情况一:未启用试用(配置文件中
trialSize未设置大于0) - 情况二:该客户端已经试用过(
trial表中已有记录) - 情况三:当日试用名额已用完(配置文件中
trialPreDay)
- 情况一:未启用试用(配置文件中
- 若后端返回正整数,则表示可以试用该值的积分,此时弹窗提示用户;
- 用户点击
立即领取后自动创建积分券并绑定。
👨💻 代码实现
数据字典
表名: trial
| 字段名 | 中文名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | 指纹 | String | 是 | 客户端唯一值 | |
| cid | 积分券 | String | 是 | ||
| ip | IP地址 | String | |||
| region | 地区 | String | |||
| platform | 平台 | String | |||
| sys | 系统信息 | String | |||
| addOn | 录入时间 | Long | 是 | 13 位时间戳 |
后端服务
/**
* 判断是否具备试用资格
* @param {String} uuid
* @returns {Number} 试用积分数,-1=未开启,0=无可用,-2=免费额度已用完
*/
const checkTril = uuid=>{
let { trialSize, trialPreDay } = config.app
if(trialSize<=0)
return -1
// 如果已经存在过,则不能再试用
if(count(TRIAL, "id=?", uuid)>0)
return 0
// 判断是否超过试用每日限额
if(trialPreDay > 0){
let now = new Date()
now.setHours(0, 0, 0, 0)
if(count(TRIAL, "addOn>=?", now.getTime())>=trialPreDay)
throw -2
}
return trialSize
}
/**
* @param {Elysia} app
*/
export default app=>{
app.post("/trial/check", ({ body:{ uuid }})=> ok(checkTril(uuid)) )
app.post("/trial/:uuid", async ({ server, request, headers, params:{ uuid } })=>{
const trialSign = checkTril(uuid)
if(trialSign == -1) throw `未开放试用`
if(trialSign == -2) throw `今日试用名额已用完`
if(trialSign <= 0) throw `客户端已试用`
let ip = server?.requestIP(request).address
let platform = headers['platform']
let sys = headers['system']
logger.info(`客户端 ${ip}(${platform}/${sys}) 尝试生产试用积分券 coupon=${trialSign}`)
let cid = createUUID(config.app.couponLen)
//创建积分券
createCoupon({ id: cid, quota: trialSign })
logger.info(`试用积分券 ${cid} 已生成...`)
let region = await ipToRegion(ip)
insertNew(TRIAL, Trial.parse({ id: uuid, cid, region, platform, sys, ip, addOn: Date.now() }))
logger.info(`保存试用信息 ${uuid} (region=${region} platform=${platform}) sys=${sys}`)
return ok(cid)
})
}
组件实现
<!-- 免费试用 -->
<template>
<wd-transition :show="showTrial" name="fade-down" :duration="600">
<view class="card card-success">
<view class="pt-1">现在能免费领 {{ trial }} 积分试用啦!不用注册,手机号、邮箱都不用填,到手就能用🎉</view>
<view class="text-right mt-3">
<wd-button size="small" type="info" style="margin-right: 10px;" @click="showTrial=false">暂不需要</wd-button>
<wd-button size="small" type="success" @click="getTrial">立即领取</wd-button>
</view>
</view>
</wd-transition>
<wd-toast />
</template>
<script setup>
import { RESULT } from '@U'
import { useDataStore } from '@/store'
const dataStore = useDataStore()
const toast = useToast()
let uuid = ""
const trial = ref(5)
const showTrial = ref(false)
const tryTrial = ()=>{
//如果已经有积分券,则跳过
if(dataStore.coupon)
return
// Initialize the agent at application startup.
const fpPromise = import('https://openfpcdn.io/fingerprintjs/v5')
.then(FingerprintJS => FingerprintJS.load())
// Get the visitor identifier when you need it.
fpPromise
.then(fp => fp.get())
.then(result => {
uuid = result.visitorId
console.debug(`获取到客户端指纹`, uuid)
RESULT("/trial/check", { uuid }, d=>{
if(!isNaN(d.data) && d.data>0){
trial.value = d.data
showTrial.value = true
}
})
})
}
const getTrial = ()=> {
if(!uuid) return toast.warning(`无效指纹数据`)
RESULT(`/trial/${uuid}`, {}, d=>{
dataStore.setCoupon(d.data)
toast.success(`免费积分券已领取`)
showTrial.value = false
})
}
const check = () => {
nextTick( tryTrial )
// setTimeout(()=> showTrial.value = true, 2000)
}
defineExpose({ check })
</script>