功能:发货链接支持永久有效,默认 TTL=0
- DELIVERY_LINK_TTL_MINUTES 设为 0 或负数时链接永久有效(expires_at 存 NULL), 默认值由 120 改为 0,本地 .env 同步改为永久 - 新增 delivery_link_generated_at 列区分从未生成与永久链接,兼容历史数据 - 永久链接授权仅校验 HMAC 签名、不校验过期(exp=0),作废/恢复语义不变 - 前端链接有效期列对永久链接显示"永久有效"
This commit is contained in:
@@ -73,8 +73,9 @@ func NewDeliveryService(fulfillment *FulfillmentService, bffBaseURL, channel, li
|
||||
if linkSecret == "" {
|
||||
linkSecret = "affiliate-dash-delivery-link-secret"
|
||||
}
|
||||
if linkTTLMinutes <= 0 {
|
||||
linkTTLMinutes = 120
|
||||
// linkTTLMinutes <= 0 表示链接永久有效(0 为默认值)。
|
||||
if linkTTLMinutes < 0 {
|
||||
linkTTLMinutes = 0
|
||||
}
|
||||
return &DeliveryService{
|
||||
fulfillment: fulfillment,
|
||||
@@ -133,11 +134,11 @@ type DeliveryBindResult struct {
|
||||
}
|
||||
|
||||
type DeliveryLinkResult struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
DeliveryURL string `json:"delivery_url"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Exp int64 `json:"exp"`
|
||||
Sign string `json:"sign"`
|
||||
OrderNo string `json:"order_no"`
|
||||
DeliveryURL string `json:"delivery_url"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"` // null 表示永久有效
|
||||
Exp int64 `json:"exp"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type DeliverySubmitResult struct {
|
||||
@@ -627,7 +628,12 @@ func (s *DeliveryService) GetOrCreateDeliveryLink(merchantID uint, orderNo, requ
|
||||
}
|
||||
// 复用未过期链接:已交付/发货中订单也允许查看兑换结果或进度,仅停止发放新链接。
|
||||
if order.DeliveryLinkExpiresAt != nil && order.DeliveryLinkExpiresAt.After(now) {
|
||||
return s.buildDeliveryLinkResult(order.OrderNo, order.DeliveryLinkExpiresAt.UTC().Truncate(time.Second), requestBaseURL), nil
|
||||
expiresAt := order.DeliveryLinkExpiresAt.UTC().Truncate(time.Second)
|
||||
return s.buildDeliveryLinkResult(order.OrderNo, &expiresAt, requestBaseURL), nil
|
||||
}
|
||||
// 永久有效链接(已生成、未作废、无有效期)直接复用,不重复生成。
|
||||
if order.DeliveryLinkGeneratedAt != nil && order.DeliveryLinkExpiresAt == nil {
|
||||
return s.buildDeliveryLinkResult(order.OrderNo, nil, requestBaseURL), nil
|
||||
}
|
||||
// 仅待发货(含可重试的发货失败)订单能生成或续期新链接。
|
||||
canShip, reason := CanFulfill(order)
|
||||
@@ -637,12 +643,17 @@ func (s *DeliveryService) GetOrCreateDeliveryLink(merchantID uint, orderNo, requ
|
||||
}
|
||||
return nil, newDeliveryHTTPError(http.StatusBadRequest, reason)
|
||||
}
|
||||
expiresAt := now.Add(s.linkTTL).UTC().Truncate(time.Second)
|
||||
var expiresAt *time.Time
|
||||
if s.linkTTL > 0 {
|
||||
value := now.Add(s.linkTTL).UTC().Truncate(time.Second)
|
||||
expiresAt = &value
|
||||
}
|
||||
if err := s.fulfillment.db.Model(&model.FulfillmentOrder{}).
|
||||
Where("id = ?", order.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"delivery_link_expires_at": expiresAt,
|
||||
"delivery_link_revoked_at": nil,
|
||||
"delivery_link_expires_at": expiresAt,
|
||||
"delivery_link_revoked_at": nil,
|
||||
"delivery_link_generated_at": now,
|
||||
}).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -689,22 +700,34 @@ func (s *DeliveryService) RestoreDeliveryLink(merchantID, actorUserID uint, orde
|
||||
}
|
||||
return nil, newDeliveryHTTPError(http.StatusBadRequest, reason)
|
||||
}
|
||||
expiresAt := time.Now().UTC().Add(s.linkTTL).Truncate(time.Second)
|
||||
if order.DeliveryLinkExpiresAt != nil && expiresAt.Equal(order.DeliveryLinkExpiresAt.UTC().Truncate(time.Second)) {
|
||||
expiresAt = expiresAt.Add(time.Second)
|
||||
now := time.Now().UTC()
|
||||
var expiresAt *time.Time
|
||||
if s.linkTTL > 0 {
|
||||
value := now.Add(s.linkTTL).Truncate(time.Second)
|
||||
if order.DeliveryLinkExpiresAt != nil && value.Equal(order.DeliveryLinkExpiresAt.UTC().Truncate(time.Second)) {
|
||||
value = value.Add(time.Second)
|
||||
}
|
||||
expiresAt = &value
|
||||
}
|
||||
if err := s.fulfillment.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&model.FulfillmentOrder{}).
|
||||
Where("id = ?", order.ID).
|
||||
Updates(map[string]interface{}{
|
||||
"delivery_link_expires_at": expiresAt,
|
||||
"delivery_link_revoked_at": nil,
|
||||
"delivery_link_expires_at": expiresAt,
|
||||
"delivery_link_revoked_at": nil,
|
||||
"delivery_link_generated_at": now,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return writeAudit(tx, &merchantID, optionalUint(actorUserID), nil, "delivery_link.restore", "fulfillment_order", order.OrderNo, map[string]interface{}{
|
||||
"expires_at": timeutil.FormatAPITime(expiresAt),
|
||||
})
|
||||
metadata := map[string]interface{}{
|
||||
"generated_at": timeutil.FormatAPITime(now),
|
||||
}
|
||||
if expiresAt != nil {
|
||||
metadata["expires_at"] = timeutil.FormatAPITime(*expiresAt)
|
||||
} else {
|
||||
metadata["permanent"] = true
|
||||
}
|
||||
return writeAudit(tx, &merchantID, optionalUint(actorUserID), nil, "delivery_link.restore", "fulfillment_order", order.OrderNo, metadata)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user