|
|
|
|
@@ -5,6 +5,7 @@ import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"sort"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
@@ -5087,6 +5088,407 @@ func (s *super) ListCouponGrants(ctx context.Context, filter *super_dto.SuperCou
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *super) ListCouponRisks(ctx context.Context, filter *super_dto.SuperCouponRiskListFilter) (*requests.Pager, error) {
|
|
|
|
|
if filter == nil {
|
|
|
|
|
filter = &super_dto.SuperCouponRiskListFilter{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 风控类型校验,避免无效筛选导致误判。
|
|
|
|
|
targetRiskType := ""
|
|
|
|
|
if filter.RiskType != nil {
|
|
|
|
|
targetRiskType = strings.TrimSpace(*filter.RiskType)
|
|
|
|
|
}
|
|
|
|
|
if targetRiskType != "" {
|
|
|
|
|
switch targetRiskType {
|
|
|
|
|
case "used_without_order", "order_status_mismatch", "used_outside_window", "unused_has_order_or_used_at", "duplicate_grant":
|
|
|
|
|
default:
|
|
|
|
|
return nil, errorx.ErrBadRequest.WithMsg("risk_type 无效")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 先做基础筛选,减少异常核查的候选数据量。
|
|
|
|
|
tbl, q := models.UserCouponQuery.QueryContext(ctx)
|
|
|
|
|
if filter.UserID != nil && *filter.UserID > 0 {
|
|
|
|
|
q = q.Where(tbl.UserID.Eq(*filter.UserID))
|
|
|
|
|
}
|
|
|
|
|
if filter.Status != nil && *filter.Status != "" {
|
|
|
|
|
q = q.Where(tbl.Status.Eq(*filter.Status))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userIDs, userFilter, err := s.lookupUserIDs(ctx, filter.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if userFilter {
|
|
|
|
|
if len(userIDs) == 0 {
|
|
|
|
|
filter.Pagination.Format()
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: 0,
|
|
|
|
|
Items: []super_dto.SuperCouponRiskItem{},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
q = q.Where(tbl.UserID.In(userIDs...))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
couponIDs, couponFilter, err := s.filterCouponRiskCouponIDs(ctx, filter)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if couponFilter {
|
|
|
|
|
if len(couponIDs) == 0 {
|
|
|
|
|
filter.Pagination.Format()
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: 0,
|
|
|
|
|
Items: []super_dto.SuperCouponRiskItem{},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
q = q.Where(tbl.CouponID.In(couponIDs...))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if filter.CreatedAtFrom != nil {
|
|
|
|
|
from, err := s.parseFilterTime(filter.CreatedAtFrom)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if from != nil {
|
|
|
|
|
q = q.Where(tbl.CreatedAt.Gte(*from))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if filter.CreatedAtTo != nil {
|
|
|
|
|
to, err := s.parseFilterTime(filter.CreatedAtTo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if to != nil {
|
|
|
|
|
q = q.Where(tbl.CreatedAt.Lte(*to))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if filter.UsedAtFrom != nil {
|
|
|
|
|
from, err := s.parseFilterTime(filter.UsedAtFrom)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if from != nil {
|
|
|
|
|
q = q.Where(tbl.UsedAt.Gte(*from))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if filter.UsedAtTo != nil {
|
|
|
|
|
to, err := s.parseFilterTime(filter.UsedAtTo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if to != nil {
|
|
|
|
|
q = q.Where(tbl.UsedAt.Lte(*to))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if filter.OrderStatus != nil && *filter.OrderStatus != "" {
|
|
|
|
|
q = q.Where(tbl.OrderID.Gt(0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if targetRiskType != "" {
|
|
|
|
|
switch targetRiskType {
|
|
|
|
|
case "used_without_order":
|
|
|
|
|
q = q.Where(tbl.Status.Eq(consts.UserCouponStatusUsed))
|
|
|
|
|
case "order_status_mismatch":
|
|
|
|
|
q = q.Where(tbl.Status.Eq(consts.UserCouponStatusUsed)).Where(tbl.OrderID.Gt(0))
|
|
|
|
|
case "used_outside_window":
|
|
|
|
|
q = q.Where(tbl.Status.Eq(consts.UserCouponStatusUsed))
|
|
|
|
|
case "unused_has_order_or_used_at":
|
|
|
|
|
q = q.Where(tbl.Status.Eq(consts.UserCouponStatusUnused))
|
|
|
|
|
case "duplicate_grant":
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 异常规则较为复杂,先拉取候选数据,再做细粒度判断与分页。
|
|
|
|
|
list, err := q.Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
if len(list) == 0 {
|
|
|
|
|
filter.Pagination.Format()
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: 0,
|
|
|
|
|
Items: []super_dto.SuperCouponRiskItem{},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
couponIDSet := make(map[int64]struct{})
|
|
|
|
|
userIDSet := make(map[int64]struct{})
|
|
|
|
|
orderIDSet := make(map[int64]struct{})
|
|
|
|
|
for _, uc := range list {
|
|
|
|
|
if uc.CouponID > 0 {
|
|
|
|
|
couponIDSet[uc.CouponID] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if uc.UserID > 0 {
|
|
|
|
|
userIDSet[uc.UserID] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if uc.OrderID > 0 {
|
|
|
|
|
orderIDSet[uc.OrderID] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
couponIDs = couponIDs[:0]
|
|
|
|
|
for id := range couponIDSet {
|
|
|
|
|
couponIDs = append(couponIDs, id)
|
|
|
|
|
}
|
|
|
|
|
userIDs = userIDs[:0]
|
|
|
|
|
for id := range userIDSet {
|
|
|
|
|
userIDs = append(userIDs, id)
|
|
|
|
|
}
|
|
|
|
|
orderIDs := make([]int64, 0, len(orderIDSet))
|
|
|
|
|
for id := range orderIDSet {
|
|
|
|
|
orderIDs = append(orderIDs, id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 批量加载关联信息,避免 N+1 查询。
|
|
|
|
|
couponMap := make(map[int64]*models.Coupon, len(couponIDs))
|
|
|
|
|
tenantMap := make(map[int64]*models.Tenant)
|
|
|
|
|
if len(couponIDs) > 0 {
|
|
|
|
|
couponTbl, couponQuery := models.CouponQuery.QueryContext(ctx)
|
|
|
|
|
coupons, err := couponQuery.Where(couponTbl.ID.In(couponIDs...)).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
tenantSet := make(map[int64]struct{})
|
|
|
|
|
for _, coupon := range coupons {
|
|
|
|
|
couponMap[coupon.ID] = coupon
|
|
|
|
|
if coupon.TenantID > 0 {
|
|
|
|
|
tenantSet[coupon.TenantID] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tenantIDs := make([]int64, 0, len(tenantSet))
|
|
|
|
|
for id := range tenantSet {
|
|
|
|
|
tenantIDs = append(tenantIDs, id)
|
|
|
|
|
}
|
|
|
|
|
if len(tenantIDs) > 0 {
|
|
|
|
|
tenantTbl, tenantQuery := models.TenantQuery.QueryContext(ctx)
|
|
|
|
|
tenants, err := tenantQuery.Where(tenantTbl.ID.In(tenantIDs...)).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
for _, tenant := range tenants {
|
|
|
|
|
tenantMap[tenant.ID] = tenant
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userMap := make(map[int64]*models.User, len(userIDs))
|
|
|
|
|
if len(userIDs) > 0 {
|
|
|
|
|
userTbl, userQuery := models.UserQuery.QueryContext(ctx)
|
|
|
|
|
users, err := userQuery.Where(userTbl.ID.In(userIDs...)).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
userMap[user.ID] = user
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
orderMap := make(map[int64]*models.Order, len(orderIDs))
|
|
|
|
|
if len(orderIDs) > 0 {
|
|
|
|
|
orderTbl, orderQuery := models.OrderQuery.QueryContext(ctx)
|
|
|
|
|
orders, err := orderQuery.Where(orderTbl.ID.In(orderIDs...)).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
for _, order := range orders {
|
|
|
|
|
orderMap[order.ID] = order
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type couponGrantKey struct {
|
|
|
|
|
userID int64
|
|
|
|
|
couponID int64
|
|
|
|
|
}
|
|
|
|
|
grantCounts := make(map[couponGrantKey]int, len(list))
|
|
|
|
|
for _, uc := range list {
|
|
|
|
|
if uc.UserID == 0 || uc.CouponID == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
key := couponGrantKey{userID: uc.UserID, couponID: uc.CouponID}
|
|
|
|
|
grantCounts[key]++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type couponRiskRecord struct {
|
|
|
|
|
item super_dto.SuperCouponRiskItem
|
|
|
|
|
createdAt time.Time
|
|
|
|
|
usedAt time.Time
|
|
|
|
|
}
|
|
|
|
|
records := make([]couponRiskRecord, 0, len(list))
|
|
|
|
|
|
|
|
|
|
matchRisk := func(target string) bool {
|
|
|
|
|
return targetRiskType == "" || targetRiskType == target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, uc := range list {
|
|
|
|
|
order := orderMap[uc.OrderID]
|
|
|
|
|
if filter.OrderStatus != nil && *filter.OrderStatus != "" {
|
|
|
|
|
if order == nil || order.Status != *filter.OrderStatus {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coupon := couponMap[uc.CouponID]
|
|
|
|
|
user := userMap[uc.UserID]
|
|
|
|
|
key := couponGrantKey{userID: uc.UserID, couponID: uc.CouponID}
|
|
|
|
|
isDuplicate := grantCounts[key] > 1
|
|
|
|
|
|
|
|
|
|
riskType := ""
|
|
|
|
|
riskReason := ""
|
|
|
|
|
|
|
|
|
|
if matchRisk("used_without_order") && uc.Status == consts.UserCouponStatusUsed {
|
|
|
|
|
if uc.OrderID == 0 {
|
|
|
|
|
riskType = "used_without_order"
|
|
|
|
|
riskReason = "已核销但未关联订单"
|
|
|
|
|
} else if order == nil {
|
|
|
|
|
riskType = "used_without_order"
|
|
|
|
|
riskReason = "已核销但订单不存在"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if riskType == "" && matchRisk("order_status_mismatch") && uc.Status == consts.UserCouponStatusUsed {
|
|
|
|
|
if uc.OrderID > 0 && order != nil && order.Status != consts.OrderStatusPaid {
|
|
|
|
|
riskType = "order_status_mismatch"
|
|
|
|
|
riskReason = "已核销但订单状态为" + order.Status.Description()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if riskType == "" && matchRisk("used_outside_window") && uc.Status == consts.UserCouponStatusUsed && coupon != nil && !uc.UsedAt.IsZero() {
|
|
|
|
|
if !coupon.StartAt.IsZero() && uc.UsedAt.Before(coupon.StartAt) {
|
|
|
|
|
riskType = "used_outside_window"
|
|
|
|
|
riskReason = "核销时间早于优惠券生效时间"
|
|
|
|
|
} else if !coupon.EndAt.IsZero() && uc.UsedAt.After(coupon.EndAt) {
|
|
|
|
|
riskType = "used_outside_window"
|
|
|
|
|
riskReason = "核销时间晚于优惠券截止时间"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if riskType == "" && matchRisk("unused_has_order_or_used_at") && uc.Status == consts.UserCouponStatusUnused {
|
|
|
|
|
if uc.OrderID > 0 && !uc.UsedAt.IsZero() {
|
|
|
|
|
riskType = "unused_has_order_or_used_at"
|
|
|
|
|
riskReason = "未使用但存在订单与使用时间"
|
|
|
|
|
} else if uc.OrderID > 0 {
|
|
|
|
|
riskType = "unused_has_order_or_used_at"
|
|
|
|
|
riskReason = "未使用但已关联订单"
|
|
|
|
|
} else if !uc.UsedAt.IsZero() {
|
|
|
|
|
riskType = "unused_has_order_or_used_at"
|
|
|
|
|
riskReason = "未使用但存在使用时间"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if riskType == "" && matchRisk("duplicate_grant") && isDuplicate {
|
|
|
|
|
riskType = "duplicate_grant"
|
|
|
|
|
riskReason = "同一用户重复领券"
|
|
|
|
|
}
|
|
|
|
|
if riskType == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
item := super_dto.SuperCouponRiskItem{
|
|
|
|
|
ID: uc.ID,
|
|
|
|
|
RiskType: riskType,
|
|
|
|
|
RiskReason: riskReason,
|
|
|
|
|
CouponID: uc.CouponID,
|
|
|
|
|
UserID: uc.UserID,
|
|
|
|
|
Status: uc.Status,
|
|
|
|
|
StatusDescription: uc.Status.Description(),
|
|
|
|
|
OrderID: uc.OrderID,
|
|
|
|
|
UsedAt: s.formatTime(uc.UsedAt),
|
|
|
|
|
CreatedAt: s.formatTime(uc.CreatedAt),
|
|
|
|
|
}
|
|
|
|
|
if user != nil {
|
|
|
|
|
item.Username = user.Username
|
|
|
|
|
} else if uc.UserID > 0 {
|
|
|
|
|
item.Username = "ID:" + strconv.FormatInt(uc.UserID, 10)
|
|
|
|
|
}
|
|
|
|
|
if coupon != nil {
|
|
|
|
|
item.CouponTitle = coupon.Title
|
|
|
|
|
item.TenantID = coupon.TenantID
|
|
|
|
|
if tenant := tenantMap[coupon.TenantID]; tenant != nil {
|
|
|
|
|
item.TenantCode = tenant.Code
|
|
|
|
|
item.TenantName = tenant.Name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if order != nil {
|
|
|
|
|
item.OrderStatus = order.Status
|
|
|
|
|
item.OrderStatusDescription = order.Status.Description()
|
|
|
|
|
item.OrderAmountPaid = order.AmountPaid
|
|
|
|
|
item.PaidAt = s.formatTime(order.PaidAt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
records = append(records, couponRiskRecord{
|
|
|
|
|
item: item,
|
|
|
|
|
createdAt: uc.CreatedAt,
|
|
|
|
|
usedAt: uc.UsedAt,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filter.Pagination.Format()
|
|
|
|
|
if len(records) == 0 {
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: 0,
|
|
|
|
|
Items: []super_dto.SuperCouponRiskItem{},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sortField := "created_at"
|
|
|
|
|
desc := true
|
|
|
|
|
if filter.Asc != nil && strings.TrimSpace(*filter.Asc) != "" {
|
|
|
|
|
sortField = strings.TrimSpace(*filter.Asc)
|
|
|
|
|
desc = false
|
|
|
|
|
}
|
|
|
|
|
if filter.Desc != nil && strings.TrimSpace(*filter.Desc) != "" {
|
|
|
|
|
sortField = strings.TrimSpace(*filter.Desc)
|
|
|
|
|
desc = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.Slice(records, func(i, j int) bool {
|
|
|
|
|
left := records[i]
|
|
|
|
|
right := records[j]
|
|
|
|
|
var less bool
|
|
|
|
|
switch sortField {
|
|
|
|
|
case "id":
|
|
|
|
|
less = left.item.ID < right.item.ID
|
|
|
|
|
case "used_at":
|
|
|
|
|
less = left.usedAt.Before(right.usedAt)
|
|
|
|
|
case "created_at":
|
|
|
|
|
less = left.createdAt.Before(right.createdAt)
|
|
|
|
|
default:
|
|
|
|
|
less = left.createdAt.Before(right.createdAt)
|
|
|
|
|
}
|
|
|
|
|
if desc {
|
|
|
|
|
return !less
|
|
|
|
|
}
|
|
|
|
|
return less
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
total := int64(len(records))
|
|
|
|
|
start := int(filter.Pagination.Offset())
|
|
|
|
|
if start >= len(records) {
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: total,
|
|
|
|
|
Items: []super_dto.SuperCouponRiskItem{},
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
end := start + int(filter.Pagination.Limit)
|
|
|
|
|
if end > len(records) {
|
|
|
|
|
end = len(records)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
items := make([]super_dto.SuperCouponRiskItem, 0, end-start)
|
|
|
|
|
for _, record := range records[start:end] {
|
|
|
|
|
items = append(items, record.item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &requests.Pager{
|
|
|
|
|
Pagination: filter.Pagination,
|
|
|
|
|
Total: total,
|
|
|
|
|
Items: items,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *super) UpdateCouponStatus(ctx context.Context, operatorID, couponID int64, form *super_dto.SuperCouponStatusUpdateForm) error {
|
|
|
|
|
if operatorID == 0 {
|
|
|
|
|
return errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
|
|
|
|
@@ -5722,6 +6124,56 @@ func (s *super) filterCouponGrantCouponIDs(ctx context.Context, filter *super_dt
|
|
|
|
|
return ids, true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *super) filterCouponRiskCouponIDs(ctx context.Context, filter *super_dto.SuperCouponRiskListFilter) ([]int64, bool, error) {
|
|
|
|
|
if filter == nil {
|
|
|
|
|
return nil, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if filter.CouponID != nil && *filter.CouponID > 0 {
|
|
|
|
|
return []int64{*filter.CouponID}, true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
couponTbl, couponQuery := models.CouponQuery.QueryContext(ctx)
|
|
|
|
|
applied := false
|
|
|
|
|
|
|
|
|
|
if filter.TenantID != nil && *filter.TenantID > 0 {
|
|
|
|
|
applied = true
|
|
|
|
|
couponQuery = couponQuery.Where(couponTbl.TenantID.Eq(*filter.TenantID))
|
|
|
|
|
} else {
|
|
|
|
|
tenantIDs, tenantFilter, err := s.lookupTenantIDs(ctx, filter.TenantCode, filter.TenantName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, true, err
|
|
|
|
|
}
|
|
|
|
|
if tenantFilter {
|
|
|
|
|
applied = true
|
|
|
|
|
if len(tenantIDs) == 0 {
|
|
|
|
|
return []int64{}, true, nil
|
|
|
|
|
}
|
|
|
|
|
couponQuery = couponQuery.Where(couponTbl.TenantID.In(tenantIDs...))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if filter.Keyword != nil && strings.TrimSpace(*filter.Keyword) != "" {
|
|
|
|
|
applied = true
|
|
|
|
|
keyword := "%" + strings.TrimSpace(*filter.Keyword) + "%"
|
|
|
|
|
couponQuery = couponQuery.Where(field.Or(couponTbl.Title.Like(keyword), couponTbl.Description.Like(keyword)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !applied {
|
|
|
|
|
return nil, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coupons, err := couponQuery.Select(couponTbl.ID).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, true, errorx.ErrDatabaseError.WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
ids := make([]int64, 0, len(coupons))
|
|
|
|
|
for _, coupon := range coupons {
|
|
|
|
|
ids = append(ids, coupon.ID)
|
|
|
|
|
}
|
|
|
|
|
return ids, true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *super) RejectWithdrawal(ctx context.Context, operatorID, id int64, reason string) error {
|
|
|
|
|
if operatorID == 0 {
|
|
|
|
|
return errorx.ErrUnauthorized.WithMsg("缺少操作者信息")
|
|
|
|
|
|