已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
90
backend/api/internal/logic/client/user/recommendation.go
Normal file
90
backend/api/internal/logic/client/user/recommendation.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// 功能描述:按购物车或收藏关联分类推荐真实在售商品;版本: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
|
||||
}
|
||||
Reference in New Issue
Block a user