Files
platforms/backend/api/internal/logic/client/user/recommendation.go

91 lines
3.9 KiB
Go
Raw Normal View History

// 功能描述按购物车或收藏关联分类推荐真实在售商品版本1.0.0。
package user
import (
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
common "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// Recommendations 排除本人已入购物车商品,收藏场景另排除已收藏;相同分类优先,其余按最近更新排序。
func Recommendations(ctx *gin.Context) {
account, ok := common.UserAccount(ctx)
if !ok {
return
}
var request struct {
Source string `form:"source,default=cart" binding:"oneof=cart favorites"`
Page int `form:"page,default=1" binding:"gte=1,lte=10000"`
Size int `form:"page_size,default=2" binding:"gte=1,lte=20"`
}
if ctx.ShouldBindQuery(&request) != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
query := impl.DBService.Where("status = ? AND stock_quantity > 0", common.StatusEnable).
Where("NOT EXISTS (SELECT 1 FROM ec_cart c WHERE c.ec_product_id = ec_product.id AND c.user_account_id = ? AND c.status = ? AND c.deleted_at IS NULL)", account.ID, common.StatusEnable)
table := "ec_cart"
if request.Source == "favorites" {
table = "ec_favorite"
query = query.Where("NOT EXISTS (SELECT 1 FROM ec_favorite f WHERE f.ec_product_id = ec_product.id AND f.user_account_id = ? AND f.status = ? AND f.deleted_at IS NULL)", account.ID, common.StatusEnable)
}
// 表名来自固定枚举用户输入不拼接进SQL归属只使用JWT对应的内部账户键。
rank := "CASE WHEN EXISTS (SELECT 1 FROM " + table + " r JOIN ec_product p ON p.id = r.ec_product_id WHERE r.user_account_id = ? AND r.status = ? AND r.deleted_at IS NULL AND p.ec_category_id = ec_product.ec_category_id) THEN 0 ELSE 1 END"
var products []models.EcProduct
if err := query.Clauses(clause.OrderBy{Expression: gorm.Expr(rank+", ec_product.updated_at desc, ec_product.identity", account.ID, common.StatusEnable)}).Offset((request.Page - 1) * request.Size).Limit(request.Size + 1).Find(&products).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
values, err := recommendationValues(impl.DBService, products)
if err != nil {
infra.Response.Error(ctx, err)
return
}
respondClientPage(ctx, clientPage{Number: request.Page, Size: request.Size}, values)
}
// recommendationValues 批量读取主图和分类,公开结果只包含展示和下单所需字段。
func recommendationValues(db *gorm.DB, products []models.EcProduct) ([]gin.H, error) {
result := make([]gin.H, 0, len(products))
if len(products) == 0 {
return result, nil
}
ids := make([]uint64, 0, len(products))
categoryIDs := []uint64{}
seen := map[uint64]bool{}
for _, p := range products {
ids = append(ids, p.ID)
if !seen[p.EcCategoryID] {
seen[p.EcCategoryID] = true
categoryIDs = append(categoryIDs, p.EcCategoryID)
}
}
var images []models.EcProductImage
if err := db.Where("ec_product_id IN ? AND status = ?", ids, common.StatusEnable).Order("is_cover desc, sort_no, identity").Find(&images).Error; err != nil {
return nil, err
}
imageMap := map[uint64]string{}
for _, i := range images {
if _, ok := imageMap[i.EcProductID]; !ok {
imageMap[i.EcProductID] = i.ImageURI
}
}
var categories []models.EcCategory
if err := db.Where("id IN ? AND status = ?", categoryIDs, common.StatusEnable).Find(&categories).Error; err != nil {
return nil, err
}
categoryMap := map[uint64]string{}
for _, c := range categories {
categoryMap[c.ID] = c.Name
}
for _, p := range products {
result = append(result, gin.H{"identity": p.Identity, "name": p.Name, "price_amount": p.PriceAmount, "stock_quantity": p.StockQuantity, "image_url": imageMap[p.ID], "category_name": categoryMap[p.EcCategoryID]})
}
return result, nil
}