实现已发放优惠券的管理接口
This commit is contained in:
@@ -53,6 +53,11 @@ const (
|
||||
ScopeCouponWrite = string("coupon:write") // 写入优惠券
|
||||
ScopeCouponWriteAssign = string("coupon:write:assign") // 发放优惠券
|
||||
|
||||
ScopeCouponUser = string("coupon_user") // 用户优惠券
|
||||
ScopeCouponUserRead = string("coupon_user:read") // 读取用户优惠券列表
|
||||
ScopeCouponUserReadOfUser = string("coupon_user:read:of_user") // 读取指定用户的用户优惠券列表
|
||||
ScopeCouponUserWrite = string("coupon_user:write") // 写入用户优惠券
|
||||
|
||||
ScopeBatch = string("batch") // 批次
|
||||
ScopeBatchRead = string("batch:read") // 读取批次列表
|
||||
ScopeBatchReadOfUser = string("batch:read:of_user") // 读取指定用户的批次列表
|
||||
|
||||
330
web/handlers/coupon_user.go
Normal file
330
web/handlers/coupon_user.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/pkg/u"
|
||||
"platform/web/auth"
|
||||
"platform/web/core"
|
||||
g "platform/web/globals"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
s "platform/web/services"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// PageCouponUser 分页查询当前用户已发放优惠券
|
||||
func PageCouponUser(c *fiber.Ctx) error {
|
||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req PageCouponUserReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||
conds = append(conds, q.CouponUser.UserID.Eq(authCtx.User.ID))
|
||||
|
||||
list, total, err := q.CouponUser.
|
||||
Joins(q.CouponUser.Coupon).
|
||||
Select(couponUserSelect(false)...).
|
||||
Where(conds...).
|
||||
Order(q.CouponUser.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取数据失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
List: list,
|
||||
Total: int(total),
|
||||
Page: req.GetPage(),
|
||||
Size: req.GetSize(),
|
||||
})
|
||||
}
|
||||
|
||||
type PageCouponUserReq struct {
|
||||
core.PageReq
|
||||
CouponUserPageFilter
|
||||
}
|
||||
|
||||
// GetCouponUser 获取当前用户已发放优惠券详情
|
||||
func GetCouponUser(c *fiber.Ctx) error {
|
||||
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req core.IdReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item, err := q.CouponUser.
|
||||
Joins(q.CouponUser.Coupon).
|
||||
Select(couponUserSelect(false)...).
|
||||
Where(
|
||||
q.CouponUser.ID.Eq(req.Id),
|
||||
q.CouponUser.UserID.Eq(authCtx.User.ID),
|
||||
).
|
||||
Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("已发放优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取数据失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(item)
|
||||
}
|
||||
|
||||
// PageCouponUserByAdmin 分页查询全部已发放优惠券
|
||||
func PageCouponUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req PageCouponUserByAdminReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||
if req.UserID != nil {
|
||||
conds = append(conds, q.CouponUser.UserID.Eq(*req.UserID))
|
||||
}
|
||||
if req.UserPhone != nil {
|
||||
conds = append(conds, q.User.As("User").Phone.Eq(*req.UserPhone))
|
||||
}
|
||||
|
||||
list, total, err := q.CouponUser.
|
||||
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||
Select(couponUserSelect(true)...).
|
||||
Where(conds...).
|
||||
Order(q.CouponUser.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取数据失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
List: list,
|
||||
Total: int(total),
|
||||
Page: req.GetPage(),
|
||||
Size: req.GetSize(),
|
||||
})
|
||||
}
|
||||
|
||||
type PageCouponUserByAdminReq struct {
|
||||
core.PageReq
|
||||
CouponUserPageFilter
|
||||
UserID *int32 `json:"user_id,omitempty"`
|
||||
UserPhone *string `json:"user_phone,omitempty"`
|
||||
}
|
||||
|
||||
// PageCouponUserOfUserByAdmin 分页查询指定用户已发放优惠券
|
||||
func PageCouponUserOfUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserReadOfUser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req PageCouponUserOfUserByAdminReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||
conds = append(conds, q.CouponUser.UserID.Eq(req.UserID))
|
||||
|
||||
list, total, err := q.CouponUser.
|
||||
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||
Select(couponUserSelect(true)...).
|
||||
Where(conds...).
|
||||
Order(q.CouponUser.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取数据失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
List: list,
|
||||
Total: int(total),
|
||||
Page: req.GetPage(),
|
||||
Size: req.GetSize(),
|
||||
})
|
||||
}
|
||||
|
||||
type PageCouponUserOfUserByAdminReq struct {
|
||||
core.PageReq
|
||||
CouponUserPageFilter
|
||||
UserID int32 `json:"user_id" validate:"required"`
|
||||
}
|
||||
|
||||
// GetCouponUserByAdmin 获取已发放优惠券详情
|
||||
func GetCouponUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req core.IdReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
item, err := q.CouponUser.
|
||||
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||
Select(couponUserSelect(true)...).
|
||||
Where(q.CouponUser.ID.Eq(req.Id)).
|
||||
Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("已发放优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取数据失败", err)
|
||||
}
|
||||
|
||||
return c.JSON(item)
|
||||
}
|
||||
|
||||
func CreateCouponUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req s.CreateCouponUserData
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.CouponUser.Create(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateCouponUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req s.UpdateCouponUserData
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.CouponUser.Update(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteCouponUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var req core.IdReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.CouponUser.Delete(req.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type CouponUserPageFilter struct {
|
||||
CouponID *int32 `json:"coupon_id,omitempty"`
|
||||
CouponName *string `json:"coupon_name,omitempty"`
|
||||
Status *m.CouponUserStatus `json:"status,omitempty"`
|
||||
Expired *bool `json:"expired,omitempty"`
|
||||
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||
ExpireAtStart *time.Time `json:"expire_at_start,omitempty"`
|
||||
ExpireAtEnd *time.Time `json:"expire_at_end,omitempty"`
|
||||
UsedAtStart *time.Time `json:"used_at_start,omitempty"`
|
||||
UsedAtEnd *time.Time `json:"used_at_end,omitempty"`
|
||||
}
|
||||
|
||||
func couponUserPageConditions(req CouponUserPageFilter) []gen.Condition {
|
||||
conds := make([]gen.Condition, 0)
|
||||
if req.CouponID != nil {
|
||||
conds = append(conds, q.CouponUser.CouponID.Eq(*req.CouponID))
|
||||
}
|
||||
if req.CouponName != nil {
|
||||
conds = append(conds, q.Coupon.As("Coupon").Name.Like("%"+*req.CouponName+"%"))
|
||||
}
|
||||
if req.Status != nil {
|
||||
conds = append(conds, q.CouponUser.Status.Eq(int(*req.Status)))
|
||||
}
|
||||
if req.Expired != nil {
|
||||
if *req.Expired {
|
||||
conds = append(conds, q.CouponUser.ExpireAt.IsNotNull(), q.CouponUser.ExpireAt.Lte(time.Now()))
|
||||
} else {
|
||||
conds = append(conds, q.CouponUser.Where(q.CouponUser.ExpireAt.IsNull()).Or(q.CouponUser.ExpireAt.Gt(time.Now())))
|
||||
}
|
||||
}
|
||||
if req.CreatedAtStart != nil {
|
||||
conds = append(conds, q.CouponUser.CreatedAt.Gte(u.DateHead(*req.CreatedAtStart)))
|
||||
}
|
||||
if req.CreatedAtEnd != nil {
|
||||
conds = append(conds, q.CouponUser.CreatedAt.Lte(u.DateTail(*req.CreatedAtEnd)))
|
||||
}
|
||||
if req.ExpireAtStart != nil {
|
||||
conds = append(conds, q.CouponUser.ExpireAt.Gte(u.DateHead(*req.ExpireAtStart)))
|
||||
}
|
||||
if req.ExpireAtEnd != nil {
|
||||
conds = append(conds, q.CouponUser.ExpireAt.Lte(u.DateTail(*req.ExpireAtEnd)))
|
||||
}
|
||||
if req.UsedAtStart != nil {
|
||||
conds = append(conds, q.CouponUser.UsedAt.Gte(u.DateHead(*req.UsedAtStart)))
|
||||
}
|
||||
if req.UsedAtEnd != nil {
|
||||
conds = append(conds, q.CouponUser.UsedAt.Lte(u.DateTail(*req.UsedAtEnd)))
|
||||
}
|
||||
return conds
|
||||
}
|
||||
|
||||
func couponUserSelect(includeUser bool) []field.Expr {
|
||||
cols := []field.Expr{
|
||||
q.CouponUser.ALL,
|
||||
q.Coupon.As("Coupon").ID.As("Coupon__id"),
|
||||
q.Coupon.As("Coupon").Name.As("Coupon__name"),
|
||||
q.Coupon.As("Coupon").Amount.As("Coupon__amount"),
|
||||
q.Coupon.As("Coupon").MinAmount.As("Coupon__min_amount"),
|
||||
q.Coupon.As("Coupon").Count_.As("Coupon__count"),
|
||||
q.Coupon.As("Coupon").Status.As("Coupon__status"),
|
||||
q.Coupon.As("Coupon").ExpireType.As("Coupon__expire_type"),
|
||||
q.Coupon.As("Coupon").ExpireAt.As("Coupon__expire_at"),
|
||||
q.Coupon.As("Coupon").ExpireIn.As("Coupon__expire_in"),
|
||||
q.Coupon.As("Coupon").CreatedAt.As("Coupon__created_at"),
|
||||
q.Coupon.As("Coupon").UpdatedAt.As("Coupon__updated_at"),
|
||||
}
|
||||
|
||||
if includeUser {
|
||||
cols = append(cols,
|
||||
q.User.As("User").ID.As("User__id"),
|
||||
q.User.As("User").Phone.As("User__phone"),
|
||||
q.User.As("User").Name.As("User__name"),
|
||||
)
|
||||
}
|
||||
|
||||
return cols
|
||||
}
|
||||
@@ -20,6 +20,7 @@ type CouponUser struct {
|
||||
type CouponUserStatus int
|
||||
|
||||
const (
|
||||
CouponUserStatusUnused CouponUserStatus = 0 // 未使用
|
||||
CouponUserStatusUsed CouponUserStatus = 1 // 已使用
|
||||
CouponUserStatusUnused CouponUserStatus = 0 // 未使用
|
||||
CouponUserStatusUsed CouponUserStatus = 1 // 已使用
|
||||
CouponUserStatusDisabled CouponUserStatus = 2 // 已禁用
|
||||
)
|
||||
|
||||
@@ -95,6 +95,11 @@ func userRouter(api fiber.Router) {
|
||||
balance := api.Group("/balance")
|
||||
balance.Post("/page", handlers.PageBalanceActivity)
|
||||
|
||||
// 已发放优惠券
|
||||
couponUser := api.Group("/coupon-user")
|
||||
couponUser.Post("/page", handlers.PageCouponUser)
|
||||
couponUser.Post("/get", handlers.GetCouponUser)
|
||||
|
||||
// 公告
|
||||
announcement := api.Group("/announcement")
|
||||
announcement.Post("/list", handlers.ListAnnouncements)
|
||||
@@ -254,4 +259,13 @@ func adminRouter(api fiber.Router) {
|
||||
coupon.Post("/update", handlers.UpdateCoupon)
|
||||
coupon.Post("/remove", handlers.DeleteCoupon)
|
||||
coupon.Post("/update/assign", handlers.AssignCoupon)
|
||||
|
||||
// coupon-user 已发放优惠券
|
||||
var couponUser = api.Group("/coupon-user")
|
||||
couponUser.Post("/page", handlers.PageCouponUserByAdmin)
|
||||
couponUser.Post("/page/of-user", handlers.PageCouponUserOfUserByAdmin)
|
||||
couponUser.Post("/get", handlers.GetCouponUserByAdmin)
|
||||
couponUser.Post("/create", handlers.CreateCouponUserByAdmin)
|
||||
couponUser.Post("/update", handlers.UpdateCouponUserByAdmin)
|
||||
couponUser.Post("/remove", handlers.DeleteCouponUserByAdmin)
|
||||
}
|
||||
|
||||
@@ -108,54 +108,11 @@ func (s *couponService) Delete(id int32) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 发放优惠券
|
||||
func (s *couponService) Assign(couponID int32, userID int32) error {
|
||||
// 获取优惠券信息
|
||||
coupon, err := q.Coupon.Where(q.Coupon.ID.Eq(couponID)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewBizErr("获取优惠券数据失败", err)
|
||||
}
|
||||
|
||||
// 检查优惠券是否可用
|
||||
// 1. 状态必须为正常
|
||||
if coupon.Status != m.CouponStatusEnabled {
|
||||
return core.NewBizErr("优惠券不可用")
|
||||
}
|
||||
// 2. 数量必须大于0
|
||||
if coupon.Count <= 0 {
|
||||
return core.NewBizErr("优惠券已发放完")
|
||||
}
|
||||
|
||||
// 发放优惠券
|
||||
err = q.Q.Transaction(func(q *q.Query) error {
|
||||
// 创建用户优惠券记录
|
||||
err := q.CouponUser.Create(&m.CouponUser{
|
||||
UserID: userID,
|
||||
CouponID: couponID,
|
||||
Status: m.CouponUserStatusUnused,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 扣减优惠券数量
|
||||
_, err = q.Coupon.
|
||||
Where(q.Coupon.ID.Eq(couponID), q.Coupon.Count_.Eq(coupon.Count)).
|
||||
UpdateSimple(q.Coupon.Count_.Value(coupon.Count - 1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return CouponUser.Create(CreateCouponUserData{
|
||||
CouponID: couponID,
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return core.NewBizErr("发放优惠券失败", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserCoupon 获取用户的指定优惠券
|
||||
@@ -165,7 +122,7 @@ func (s *couponService) GetUserCoupon(uid int32, cuid int32, amount decimal.Deci
|
||||
q.CouponUser.ID.Eq(cuid),
|
||||
q.CouponUser.UserID.Eq(uid),
|
||||
q.CouponUser.Status.Eq(int(m.CouponUserStatusUnused)),
|
||||
q.CouponUser.ExpireAt.Gt(time.Now()),
|
||||
q.CouponUser.Where(q.CouponUser.ExpireAt.IsNull()).Or(q.CouponUser.ExpireAt.Gt(time.Now())),
|
||||
).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, core.NewBizErr("优惠券不存在或已失效")
|
||||
|
||||
255
web/services/coupon_user.go
Normal file
255
web/services/coupon_user.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"platform/pkg/u"
|
||||
"platform/web/core"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"time"
|
||||
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var CouponUser = &couponUserService{}
|
||||
|
||||
type couponUserService struct{}
|
||||
|
||||
func (s *couponUserService) Create(data CreateCouponUserData) error {
|
||||
now := time.Now()
|
||||
status := u.Else(data.Status, m.CouponUserStatusUnused)
|
||||
if err := validateCouponUserStatus(status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.Q.Transaction(func(tx *q.Query) error {
|
||||
coupon, err := tx.Coupon.Where(tx.Coupon.ID.Eq(data.CouponID)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("获取优惠券数据失败", err)
|
||||
}
|
||||
if coupon.Status != m.CouponStatusEnabled {
|
||||
return core.NewBizErr("优惠券不可用")
|
||||
}
|
||||
if coupon.Count <= 0 {
|
||||
return core.NewBizErr("优惠券已发放完")
|
||||
}
|
||||
|
||||
_, err = tx.User.Where(tx.User.ID.Eq(data.UserID)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("用户不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("获取用户数据失败", err)
|
||||
}
|
||||
|
||||
expireAt := data.ExpireAt
|
||||
if expireAt == nil {
|
||||
expireAt = couponUserExpireAt(coupon, now)
|
||||
}
|
||||
|
||||
usedAt := data.UsedAt
|
||||
if status == m.CouponUserStatusUsed && usedAt == nil {
|
||||
usedAt = &now
|
||||
}
|
||||
if status == m.CouponUserStatusUnused {
|
||||
usedAt = nil
|
||||
}
|
||||
|
||||
err = tx.CouponUser.Create(&m.CouponUser{
|
||||
UserID: data.UserID,
|
||||
CouponID: data.CouponID,
|
||||
Status: status,
|
||||
ExpireAt: expireAt,
|
||||
UsedAt: usedAt,
|
||||
})
|
||||
if err != nil {
|
||||
return core.NewServErr("发放优惠券失败", err)
|
||||
}
|
||||
|
||||
return adjustCouponCount(tx, coupon.ID, -1)
|
||||
})
|
||||
}
|
||||
|
||||
type CreateCouponUserData struct {
|
||||
CouponID int32 `json:"coupon_id" validate:"required"`
|
||||
UserID int32 `json:"user_id" validate:"required"`
|
||||
Status *m.CouponUserStatus `json:"status"`
|
||||
ExpireAt *time.Time `json:"expire_at"`
|
||||
UsedAt *time.Time `json:"used_at"`
|
||||
}
|
||||
|
||||
func (s *couponUserService) Update(data UpdateCouponUserData) error {
|
||||
return q.Q.Transaction(func(tx *q.Query) error {
|
||||
current, err := tx.CouponUser.Where(tx.CouponUser.ID.Eq(data.ID)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("已发放优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("获取已发放优惠券失败", err)
|
||||
}
|
||||
|
||||
do := make([]field.AssignExpr, 0)
|
||||
if data.ExpireAtClear != nil && *data.ExpireAtClear {
|
||||
do = append(do, tx.CouponUser.ExpireAt.Null())
|
||||
} else if data.ExpireAt != nil {
|
||||
do = append(do, tx.CouponUser.ExpireAt.Value(*data.ExpireAt))
|
||||
}
|
||||
|
||||
if data.UsedAtClear != nil && *data.UsedAtClear {
|
||||
do = append(do, tx.CouponUser.UsedAt.Null())
|
||||
} else if data.UsedAt != nil {
|
||||
do = append(do, tx.CouponUser.UsedAt.Value(*data.UsedAt))
|
||||
}
|
||||
|
||||
if data.Status != nil {
|
||||
if err := validateCouponUserStatus(*data.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
if current.Status != *data.Status {
|
||||
if current.Status == m.CouponUserStatusUsed {
|
||||
return core.NewBizErr("已使用的优惠券不能修改状态")
|
||||
}
|
||||
if current.Status == m.CouponUserStatusDisabled && *data.Status == m.CouponUserStatusUsed {
|
||||
return core.NewBizErr("已禁用的优惠券不能标记为已使用")
|
||||
}
|
||||
|
||||
switch *data.Status {
|
||||
case m.CouponUserStatusUnused:
|
||||
if current.Status == m.CouponUserStatusDisabled {
|
||||
if err := adjustCouponCount(tx, current.CouponID, -1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if data.UsedAt == nil && (data.UsedAtClear == nil || !*data.UsedAtClear) {
|
||||
do = append(do, tx.CouponUser.UsedAt.Null())
|
||||
}
|
||||
|
||||
case m.CouponUserStatusUsed:
|
||||
if data.UsedAt == nil && (data.UsedAtClear == nil || !*data.UsedAtClear) {
|
||||
do = append(do, tx.CouponUser.UsedAt.Value(time.Now()))
|
||||
}
|
||||
|
||||
case m.CouponUserStatusDisabled:
|
||||
if current.Status == m.CouponUserStatusUnused {
|
||||
if err := adjustCouponCount(tx, current.CouponID, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
do = append(do, tx.CouponUser.Status.Value(int(*data.Status)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(do) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result, err := tx.CouponUser.
|
||||
Where(
|
||||
tx.CouponUser.ID.Eq(data.ID),
|
||||
tx.CouponUser.Status.Eq(int(current.Status)),
|
||||
).
|
||||
UpdateSimple(do...)
|
||||
if err != nil {
|
||||
return core.NewServErr("更新已发放优惠券失败", err)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return core.NewBizErr("已发放优惠券状态已变化,请重试")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type UpdateCouponUserData struct {
|
||||
ID int32 `json:"id" validate:"required"`
|
||||
Status *m.CouponUserStatus `json:"status"`
|
||||
ExpireAt *time.Time `json:"expire_at"`
|
||||
ExpireAtClear *bool `json:"expire_at_clear"`
|
||||
UsedAt *time.Time `json:"used_at"`
|
||||
UsedAtClear *bool `json:"used_at_clear"`
|
||||
}
|
||||
|
||||
func (s *couponUserService) Delete(id int32) error {
|
||||
status := m.CouponUserStatusDisabled
|
||||
return s.Update(UpdateCouponUserData{
|
||||
ID: id,
|
||||
Status: &status,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *couponUserService) DeleteOfUser(id int32, userID int32) error {
|
||||
assigned, err := q.CouponUser.Where(
|
||||
q.CouponUser.ID.Eq(id),
|
||||
q.CouponUser.UserID.Eq(userID),
|
||||
).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("已发放优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("获取已发放优惠券失败", err)
|
||||
}
|
||||
if assigned.Status != m.CouponUserStatusUnused {
|
||||
return core.NewBizErr("只能撤销未使用的优惠券")
|
||||
}
|
||||
|
||||
return s.Delete(id)
|
||||
}
|
||||
|
||||
func couponUserExpireAt(coupon *m.Coupon, now time.Time) *time.Time {
|
||||
if coupon == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch coupon.ExpireType {
|
||||
case m.CouponExpireTypeFixed:
|
||||
return coupon.ExpireAt
|
||||
case m.CouponExpireTypeRelative:
|
||||
if coupon.ExpireIn == nil {
|
||||
return nil
|
||||
}
|
||||
expireAt := now.Add(time.Duration(*coupon.ExpireIn) * 24 * time.Hour)
|
||||
return &expireAt
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func validateCouponUserStatus(status m.CouponUserStatus) error {
|
||||
switch status {
|
||||
case m.CouponUserStatusUnused, m.CouponUserStatusUsed, m.CouponUserStatusDisabled:
|
||||
return nil
|
||||
default:
|
||||
return core.NewBizErr("优惠券发放状态无效")
|
||||
}
|
||||
}
|
||||
|
||||
func adjustCouponCount(tx *q.Query, couponID int32, delta int32) error {
|
||||
coupon, err := tx.Coupon.Where(tx.Coupon.ID.Eq(couponID)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return core.NewBizErr("优惠券不存在")
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("获取优惠券数据失败", err)
|
||||
}
|
||||
|
||||
next := coupon.Count + delta
|
||||
if next < 0 {
|
||||
return core.NewBizErr("优惠券已发放完")
|
||||
}
|
||||
|
||||
result, err := tx.Coupon.
|
||||
Where(tx.Coupon.ID.Eq(couponID), tx.Coupon.Count_.Eq(coupon.Count)).
|
||||
UpdateSimple(tx.Coupon.Count_.Value(next))
|
||||
if err != nil {
|
||||
return core.NewServErr("更新优惠券数量失败", err)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return core.NewBizErr("优惠券库存已变化,请重试")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user