- 订单状态新增「6 申请退款中」,用户申请退款原子流转并落待审核退款流水 - 退款失败回退订单至已支付并恢复被取消的权益发放任务 - 新增虚拟支付退款查单兜底任务,反查超时订单并自动订正 - 会员包来源额度不叠加,扣减/回退/回收口径统一为只取生效一份 - 退款回收流水按吊销前后有效剩余差值记账,避免无效回收记录 - 同步更新 README 与 Stitch 设计文档
109 lines
3.5 KiB
Go
109 lines
3.5 KiB
Go
package redisdao
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/global"
|
|
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/redismodel"
|
|
)
|
|
|
|
// VirtualPayUnpaidSet 标记某虚拟支付订单「最近一次查单确认未支付」,TTL 内不再重复查询。
|
|
func VirtualPayUnpaidSet(ctx context.Context, orderNo string, ttl time.Duration) error {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualPayUnpaidSet")
|
|
defer span.End()
|
|
|
|
key := fmt.Sprintf(redismodel.KeyVirtualPayUnpaid, orderNo)
|
|
if err := global.RedisEngine.Set(ctx, key, 1, ttl).Err(); err != nil {
|
|
span.RecordError(err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VirtualPayUnpaidDel 清除负缓存(订单已支付/已处理后调用,让后续流程不再跳过它)。
|
|
func VirtualPayUnpaidDel(ctx context.Context, orderNo string) error {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualPayUnpaidDel")
|
|
defer span.End()
|
|
|
|
key := fmt.Sprintf(redismodel.KeyVirtualPayUnpaid, orderNo)
|
|
if err := global.RedisEngine.Del(ctx, key).Err(); err != nil {
|
|
span.RecordError(err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VirtualRefundCheckedSetNX 标记某订单的退款结果本轮已完成反查(TTL 内不重复查询),返回是否为首次标记。
|
|
func VirtualRefundCheckedSetNX(ctx context.Context, orderNo string, ttl time.Duration) (bool, error) {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualRefundCheckedSetNX")
|
|
defer span.End()
|
|
|
|
key := fmt.Sprintf(redismodel.KeyVirtualRefundChecked, orderNo)
|
|
ok, err := global.RedisEngine.SetNX(ctx, key, 1, ttl).Result()
|
|
if err != nil {
|
|
span.RecordError(err)
|
|
return false, err
|
|
}
|
|
return ok, nil
|
|
}
|
|
|
|
// VirtualRefundCheckedDel 撤销「已核对」标记:查单失败时调用,让下一轮立即重试而非等待 TTL 过期。
|
|
func VirtualRefundCheckedDel(ctx context.Context, orderNo string) error {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualRefundCheckedDel")
|
|
defer span.End()
|
|
|
|
key := fmt.Sprintf(redismodel.KeyVirtualRefundChecked, orderNo)
|
|
if err := global.RedisEngine.Del(ctx, key).Err(); err != nil {
|
|
span.RecordError(err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// VirtualRefundStuckAlertSetNX 标记某订单的「退款长期未完成」告警已发出(TTL 内不重复告警),返回是否为首次。
|
|
func VirtualRefundStuckAlertSetNX(ctx context.Context, orderNo string, ttl time.Duration) (bool, error) {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualRefundStuckAlertSetNX")
|
|
defer span.End()
|
|
|
|
key := fmt.Sprintf(redismodel.KeyVirtualRefundStuckAlert, orderNo)
|
|
ok, err := global.RedisEngine.SetNX(ctx, key, 1, ttl).Result()
|
|
if err != nil {
|
|
span.RecordError(err)
|
|
return false, err
|
|
}
|
|
return ok, nil
|
|
}
|
|
|
|
// VirtualPayUnpaidBatchGet 批量判断哪些订单命中负缓存。
|
|
// 用一次 MGET 代替 N 次 GET,避免逐单往返 Redis 放大单轮耗时。
|
|
func VirtualPayUnpaidBatchGet(ctx context.Context, orderNos []string) (map[string]bool, error) {
|
|
ctx, span := global.Tracer.Start(ctx, "redisdao.VirtualPayUnpaidBatchGet")
|
|
defer span.End()
|
|
|
|
result := make(map[string]bool, len(orderNos))
|
|
if len(orderNos) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
keys := make([]string, 0, len(orderNos))
|
|
for _, no := range orderNos {
|
|
keys = append(keys, fmt.Sprintf(redismodel.KeyVirtualPayUnpaid, no))
|
|
}
|
|
vals, err := global.RedisEngine.MGet(ctx, keys...).Result()
|
|
if err != nil {
|
|
span.RecordError(err)
|
|
return nil, err
|
|
}
|
|
for i, v := range vals {
|
|
if i >= len(orderNos) {
|
|
break
|
|
}
|
|
if v != nil {
|
|
result[orderNos[i]] = true
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|