已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
140
backend/api/internal/logic/client/user/favorite.go
Normal file
140
backend/api/internal/logic/client/user/favorite.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// 功能描述:用户商品收藏、分类列表与条件状态更新;版本:1.0.0。
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var errFavoriteChanged = errcode.NewError(2404, "收藏状态已变化,请刷新后重试")
|
||||
|
||||
// favoriteValue 只返回商品公开标识和收藏状态,版本绑定收藏关系以防旧请求复活。
|
||||
func favoriteValue(item models.EcFavorite, productIdentity string) gin.H {
|
||||
revision := ""
|
||||
if item.ID != 0 {
|
||||
revision = fmt.Sprintf("%s:%d", item.Identity, item.Revision)
|
||||
}
|
||||
return gin.H{"product_identity": productIdentity, "favorite": item.ID != 0 && item.Status == common.StatusEnable, "revision": revision}
|
||||
}
|
||||
|
||||
// FavoriteState 读取当前账户对指定商品的状态;商品下架后仍能取消收藏。
|
||||
func FavoriteState(ctx *gin.Context) {
|
||||
account, ok := common.UserAccount(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var product models.EcProduct
|
||||
if err := impl.DBService.Unscoped().Where("identity = ?", ctx.Param("identity")).Take(&product).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = errcode.ErrRecordNotFound
|
||||
}
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
var item models.EcFavorite
|
||||
if err := impl.DBService.Where("user_account_id = ? AND ec_product_id = ?", account.ID, product.ID).Find(&item).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
infra.Response.Success(ctx, favoriteValue(item, product.Identity))
|
||||
}
|
||||
|
||||
// SetFavorite 使用绝对收藏状态和原版本;相同目标重试无写入,不支持物理删除。
|
||||
func SetFavorite(ctx *gin.Context) {
|
||||
account, ok := common.UserAccount(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var request struct {
|
||||
Favorite *bool `json:"favorite" binding:"required"`
|
||||
Revision string `json:"revision" binding:"max=128"`
|
||||
}
|
||||
if ctx.ShouldBindJSON(&request) != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
var item models.EcFavorite
|
||||
err := impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
if err := lockAddressOwner(tx, account.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
var product models.EcProduct
|
||||
if err := tx.Unscoped().Where("identity = ?", ctx.Param("identity")).Take(&product).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errcode.ErrRecordNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := tx.Where("user_account_id = ? AND ec_product_id = ?", account.ID, product.ID).Find(&item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
state := favoriteValue(item, product.Identity)
|
||||
if state["favorite"] == *request.Favorite {
|
||||
return nil
|
||||
}
|
||||
if state["revision"] != request.Revision {
|
||||
return errFavoriteChanged
|
||||
}
|
||||
if *request.Favorite && (product.Status != common.StatusEnable || product.DeletedAt.Valid) {
|
||||
return errcode.ErrRecordNotFound
|
||||
}
|
||||
if *request.Favorite {
|
||||
var count int64
|
||||
if err := tx.Model(&models.EcFavorite{}).Where("user_account_id = ? AND status = ?", account.ID, common.StatusEnable).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count >= 1000 {
|
||||
return errcode.ErrOutOfRange
|
||||
}
|
||||
}
|
||||
if item.ID == 0 {
|
||||
item = models.EcFavorite{Entity: common.NewEntity(common.StatusEnable), UserAccountID: account.ID, EcProductID: product.ID, Revision: 1}
|
||||
return tx.Create(&item).Error
|
||||
}
|
||||
if item.Revision >= math.MaxInt64 {
|
||||
return errcode.ErrOutOfRange
|
||||
}
|
||||
status := common.StatusArchived
|
||||
if *request.Favorite {
|
||||
status = common.StatusEnable
|
||||
}
|
||||
return tx.Model(&item).Updates(map[string]any{"status": status, "revision": item.Revision + 1}).Error
|
||||
})
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
infra.Response.Success(ctx, favoriteValue(item, ctx.Param("identity")))
|
||||
}
|
||||
|
||||
// ListFavorites 按最后状态更新时间分页;下架/软删除商品仍展示历史条目并禁止加购。
|
||||
func ListFavorites(ctx *gin.Context) {
|
||||
account, ok := common.UserAccount(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
query, page, ok := paginateClientList(ctx, impl.DBService.Where("user_account_id = ? AND status = ?", account.ID, common.StatusEnable))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var items []models.EcFavorite
|
||||
if err := query.Order("updated_at desc, identity").Find(&items).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
result, err := favoriteRows(impl.DBService, items)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
respondClientPage(ctx, page, result)
|
||||
}
|
||||
Reference in New Issue
Block a user