Beego本身不提供召回能力,召回逻辑必须自行实现并置于独立service层,Controller仅作薄包装;参数需严格校验与降级,ID须转字符串防JS溢出。

Beego 本身不提供推荐系统召回能力,所谓“召回接口”必须由你自行实现算法逻辑,框架只负责 HTTP 路由、参数解析、响应封装等胶水工作。别指望 beego.Router 自动帮你算相似用户或向量近邻。
召回接口该放在 Controller 还是独立 service 层?
必须放在独立 service 层,Controller 只做薄包装。常见错误是把特征提取、相似度计算、缓存穿透防护全塞进 Get() 方法里,导致:
- 单元测试无法覆盖核心逻辑(Controller 依赖
this.Ctx,难 mock) - 无法复用召回结果给其他通道(如消息队列触发的离线重排)
- goroutine 泄漏风险:若在 Controller 内启 goroutine 调用耗时召回,HTTP 请求结束但 goroutine 未退出,
msg.Ack()会失败(见 RabbitMQ 踩坑记录)
正确结构:
// services/recall.go
func GetUserRecall(uid int64, limit int) ([]int64, error) {
// 1. 从 Redis 查 u2i 热门池 fallback
// 2. 调用内部 gRPC 召回服务(如 faiss-server)
// 3. 合并去重,按 score 截断
}
<p>// controllers/recommend.go
func (c *RecController) Get() {
uid, <em> := c.GetInt64("uid")
limit, </em> := c.GetInt("limit", 20)
items, err := services.GetUserRecall(uid, limit)
if err != nil {
c.Abort("500")
return
}
c.Data["json"] = map[string]interface{}{"items": items}
c.ServeJSON()
}
召回参数校验和降级策略怎么写才不翻车?
召回接口对参数敏感,uid 缺失或为 0、limit 超过 200,都应立即拒绝,而不是传给下游引发雪崩。Beego 的 c.GetInt64() 默认返回 0,不能直接信任。
- 用
c.GetString("uid")先判空,再strconv.ParseInt();0 值 uid 视为非法 -
limit必须做硬限制:if limit 200 { limit = 20 } - 降级兜底必须同步完成:Redis 查询超时,立刻 fallback 到本地内存缓存的热门榜(
sync.Map),而非启动新 goroutine 异步加载 - 禁止在降级路径里调用任何外部服务(包括日志上报),否则降级本身变成故障点
为什么召回结果要转成字符串 ID 再返回?
Go 的 int64 直接 JSON 序列化给前端 JS 会溢出(JS 安全整数上限是 2^53 - 1),后端 ID 经常超过此值。Beego 的 c.ServeJSON() 不自动处理,必须手动转。
- 错误写法:
"item_id": item.ID→ 前端拿到的是截断数字 - 正确写法:
"item_id": fmt.Sprintf("%d", item.ID) - 若用 struct 返回,字段类型必须是
string,且加 JSON tag:ItemID string `json:"item_id"` - 别信
json.Marshal的 float64 自动转换——它不解决精度问题
召回不是魔法,它是特征、模型、缓存、降级四层堆出来的工程结果。Beego 只管把 /api/v1/recall 这个路径接到你的函数上,剩下的每一步,比如向量索引是否预热、Redis pipeline 是否拆包、gRPC 超时设多少,都得你亲手拧紧螺丝。


















