1. 项目背景与核心需求
新沂市作为快速发展的县级市,本地娱乐消费需求日益增长。传统的信息获取方式存在更新不及时、推荐不精准等问题。我们团队基于ThinkPHP和Laravel框架,开发了一套微信小程序娱乐项目推荐系统,旨在解决以下痛点:
- 信息碎片化:娱乐场所信息分散在各个平台
- 推荐不智能:缺乏基于用户偏好的个性化推荐
- 体验不流畅:多数本地服务小程序交互体验差
这个项目最核心的价值在于:通过混合推荐算法(协同过滤+内容推荐+LBS),为新沂市用户提供精准的周边娱乐场所推荐,同时为商家带来精准客流。
2. 技术选型与框架对比
2.1 ThinkPHP vs Laravel 深度对比
在项目启动阶段,我们针对两个主流PHP框架进行了详细评估:
| 评估维度 | ThinkPHP6 | Laravel9 |
|---|---|---|
| 学习曲线 | 简单,中文文档完善 | 中等,需要理解核心概念 |
| 性能表现 | 轻量,基准测试QPS 1200 | 功能丰富,QPS 900 |
| 扩展生态 | 官方扩展有限 | 海量社区包(Packagist) |
| ORM能力 | 基础CRUD支持 | Eloquent关系模型强大 |
| 适合场景 | 快速开发中小型项目 | 复杂业务系统 |
实际选择建议:如果团队PHP经验较少或项目周期紧张,推荐ThinkPHP;如果需要长期维护和功能扩展,Laravel更合适。
2.2 微信小程序技术栈搭配
前端我们采用微信原生框架+TypeScript,主要考虑:
- 更好的类型检查和代码提示
- 与后端API的强类型对接
- 社区生态完善(如TDesign组件库)
后端接口遵循RESTful规范,关键设计原则:
- 资源化设计(如
/api/businesses) - 标准化状态码(200成功,401未授权等)
- 统一响应格式:
json复制{
"code": 200,
"data": {},
"message": "success"
}
3. 核心数据库设计
3.1 主要表结构设计
商家表(businesses)核心字段:
php复制Schema::create('businesses', function (Blueprint $table) {
$table->id()->comment('主键');
$table->string('name', 100)->comment('商家名称');
$table->point('location')->spatialIndex()->comment('经纬度坐标');
$table->decimal('score', 2, 1)->default(0)->comment('评分');
$table->json('tags')->comment('标签数组如["KTV","酒吧"]');
$table->string('cover_url')->comment('封面图URL');
$table->unsignedInteger('view_count')->default(0)->comment('浏览量');
// 其他字段...
});
用户行为表(user_behaviors)设计要点:
- 采用事件表设计模式(Event Sourcing)
- 记录用户ID、行为类型(浏览/收藏/消费等)、目标商家、时间戳
- 使用复合索引提高查询效率:
php复制$table->index(['user_id', 'created_at']);
3.2 地理空间数据处理
针对LBS推荐需求,我们采用MySQL的空间扩展:
sql复制-- 查找5公里范围内的商家
SELECT id, name,
ST_Distance_Sphere(location, POINT(118.123, 34.456)) AS distance
FROM businesses
WHERE ST_Distance_Sphere(location, POINT(118.123, 34.456)) <= 5000
ORDER BY distance;
性能优化技巧:对于大规模数据,建议使用Redis GEO或Elasticsearch的geo_point类型。
4. 推荐算法实现
4.1 混合推荐策略架构

我们的推荐系统采用三层架构:
-
召回层:基于规则快速筛选候选集
- 地理围栏过滤(5公里内)
- 热门商家Top100
- 用户历史行为匹配
-
排序层:机器学习模型精排
- 特征工程:商家评分、距离、价格、用户偏好等
- 使用LightGBM模型进行CTR预估
-
业务规则:人工调控
- 新商家加权
- 合作商家优先展示
4.2 协同过滤实现示例
基于用户的协同过滤核心代码(Laravel实现):
php复制public function recommend($userId) {
// 获取目标用户行为
$targetUser = UserBehavior::where('user_id', $userId)
->pluck('business_id');
// 计算相似用户(余弦相似度)
$similarUsers = UserBehavior::selectRaw('user_id, COUNT(*) as score')
->whereIn('business_id', $targetUser)
->groupBy('user_id')
->orderByDesc('score')
->limit(20)
->get();
// 获取相似用户喜欢的商家
return Business::whereIn('id', function($query) use ($similarUsers) {
$query->select('business_id')
->from('user_behaviors')
->whereIn('user_id', $similarUsers->pluck('user_id'));
})
->whereNotIn('id', $targetUser) // 排除已访问
->orderByDesc('score')
->limit(10)
->get();
}
4.3 冷启动解决方案
针对新用户和新商家,我们采用以下策略:
- 地域热门推荐:展示该区域浏览量最高的商家
- 标签匹配:用户注册时选择兴趣标签
- 探索机制:10%流量强制展示新商家
5. 接口开发与安全
5.1 RESTful API设计规范
我们遵循严格的API设计规范:
- 版本控制:
/api/v1/recommend - 资源嵌套不超过两级:
/api/businesses/{id}/comments - 过滤参数使用查询字符串:
/api/businesses?category=KTV&sort=distance
5.2 JWT鉴权实现
Laravel中使用jwt-auth包实现:
php复制// 登录接口
public function login(Request $request) {
$credentials = $request->only('code'); // 微信code
if (!$token = auth('wechat')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
// 受保护路由
Route::middleware('auth:wechat')->group(function() {
Route::get('/user', 'UserController@profile');
});
安全要点:设置合理的token过期时间(建议2小时),并实现refresh token机制。
6. 性能优化实战
6.1 缓存策略
采用多级缓存架构:
- CDN静态资源缓存:商家图片、前端资源
- Redis热点数据:
php复制// 获取热门商家 $businesses = Cache::remember('hot_businesses', 3600, function() { return Business::where('score', '>', 4) ->orderByDesc('view_count') ->limit(20) ->get(); }); - 数据库查询缓存:对复杂推荐结果缓存15分钟
6.2 数据库优化
- 读写分离:1主2从架构
- 慢查询监控:
sql复制-- 在my.cnf中配置 slow_query_log = 1 long_query_time = 1 - 索引优化:使用EXPLAIN分析所有查询
6.3 前端性能技巧
小程序端关键优化:
- 分页加载:实现上拉触底加载更多
- 图片懒加载:使用
lazy-load属性 - 数据预取:在onShow时预加载可能需要的资源
7. 部署与监控
7.1 Docker化部署
Laravel项目Dockerfile示例:
dockerfile复制FROM php:8.1-fpm
# 安装扩展
RUN docker-php-ext-install pdo_mysql opcache \
&& pecl install redis \
&& docker-php-ext-enable redis
# 安装composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# 复制代码
COPY . /var/www/html
WORKDIR /var/www/html
# 安装依赖
RUN composer install --optimize-autoloader --no-dev \
&& php artisan config:cache \
&& php artisan route:cache
7.2 监控方案
我们采用Prometheus+Grafana监控体系:
- 业务指标:推荐点击率、接口响应时间
- 系统指标:CPU/内存使用率、数据库连接数
- 告警规则:当500错误率>1%时触发企业微信告警
关键Prometheus配置:
yaml复制scrape_configs:
- job_name: 'laravel'
metrics_path: '/metrics'
static_configs:
- targets: ['app:9100']
8. 踩坑与经验总结
8.1 微信登录的坑
问题现象:iOS设备偶尔登录失败
原因排查:微信unionId获取流程不一致
解决方案:
php复制// 正确处理微信用户信息
public function getWechatUser($code) {
$app = app('wechat.mini_program');
$auth = $app->auth->session($code);
// 关键处理:检查unionid是否存在
if (empty($auth['unionid'])) {
throw new Exception('需要用户授权手机号获取unionid');
}
return User::firstOrCreate(
['unionid' => $auth['unionid']],
['openid' => $auth['openid']]
);
}
8.2 推荐算法效果提升
通过AB测试我们发现:
- 单纯协同过滤的点击率:12%
- 加入LBS权重后:18%
- 最终混合算法:23%
关键调整参数:
php复制// 距离权重计算
function calculateDistanceWeight($distance) {
return max(0, 1 - ($distance / 5000)); // 5公里内线性衰减
}
9. 扩展功能实现
9.1 智能预警系统
基于Laravel任务调度实现库存预警:
php复制// 在App\Console\Kernel中
protected function schedule(Schedule $schedule) {
$schedule->call(function() {
$lowStock = Business::where('inventory', '<', 5)->get();
foreach ($lowStock as $business) {
Alert::create([
'business_id' => $business->id,
'message' => "库存不足:当前仅剩{$business->inventory}",
'level' => 'urgent'
]);
}
})->hourly();
}
9.2 视频弹幕实现
WebSocket方案选型对比:
- Swoole:高性能但需要额外扩展
- Laravel Echo:开发简单但依赖Node.js
- 最终选择:使用Pusher服务(免运维)
弹幕存储设计:
php复制Schema::create('danmus', function (Blueprint $table) {
$table->id();
$table->foreignId('video_id')->constrained();
$table->foreignId('user_id')->constrained();
$table->string('content');
$table->integer('time')->comment('弹幕出现时间(秒)');
$table->string('color')->default('#fff');
$table->timestamps();
});
10. 项目演进方向
当前系统已稳定运行3个月,日活用户约5000人。下一步计划:
- 推荐算法升级:试验图神经网络捕捉用户-商家复杂关系
- 实时计算:引入Flink处理用户实时行为数据
- 多模态搜索:支持"找附近有包间的KTV"等自然语言查询
技术栈扩展评估:
- 推荐系统:从Python迁移到Java(更好的线程模型)
- 地理搜索:Elasticsearch替代MySQL空间查询
- 消息队列:Kafka替代Redis Streams(更高吞吐量)