fix: 修复仪表盘时区统计偏移、重复订阅、新增map_apple字段
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 5m23s
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 5m23s
- fix(order/model): QueryDateOrders/QueryDailyOrdersList 使用 time.Date 替代 Truncate 修复 UTC+8 时区偏移 - fix(user/model): QueryResisterUserTotalByDate 同样修复时区截断 - fix(traffic/model): QueryServerTrafficByDay 同样修复时区截断 - fix(activateOrder): 兜底查询防止过期用户重购产生重复订阅 - feat(api): SubscribeDiscount 新增 map_apple 字段
This commit is contained in:
parent
800f9c8460
commit
c0d839deb9
@ -21,7 +21,7 @@ env:
|
||||
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
||||
# TG通知
|
||||
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
||||
TG_CHAT_ID: "-49402438031"
|
||||
TG_CHAT_ID: "-4940243803"
|
||||
# Go构建变量
|
||||
SERVICE: vpn
|
||||
SERVICE_STYLE: vpn
|
||||
|
||||
@ -227,6 +227,7 @@ type (
|
||||
SubscribeDiscount {
|
||||
Quantity int64 `json:"quantity"`
|
||||
Discount float64 `json:"discount"`
|
||||
MapApple string `json:"map_apple"`
|
||||
}
|
||||
TrafficLimit {
|
||||
StatType string `json:"stat_type"`
|
||||
|
||||
@ -166,8 +166,8 @@ func (m *customOrderModel) QueryMonthlyOrders(ctx context.Context, date time.Tim
|
||||
|
||||
// QueryDateOrders Query orders by date
|
||||
func (m *customOrderModel) QueryDateOrders(ctx context.Context, date time.Time) (OrdersTotal, error) {
|
||||
start := date.Truncate(24 * time.Hour)
|
||||
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
||||
start := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
end := start.AddDate(0, 0, 1).Add(-time.Nanosecond)
|
||||
var result OrdersTotal
|
||||
err := m.QueryNoCacheCtx(ctx, &result, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&Order{}).
|
||||
@ -276,7 +276,7 @@ func (m *customOrderModel) QueryDailyOrdersList(ctx context.Context, date time.T
|
||||
// 当月 1 号 00:00:00
|
||||
firstDay := time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, date.Location())
|
||||
// 第二天 00:00:00
|
||||
nextDay := date.AddDate(0, 0, 1).Truncate(24 * time.Hour)
|
||||
nextDay := time.Date(date.Year(), date.Month(), date.Day()+1, 0, 0, 0, 0, date.Location())
|
||||
|
||||
return conn.Model(&Order{}).
|
||||
Select(`
|
||||
|
||||
@ -27,8 +27,8 @@ func NewModel(conn *gorm.DB) Model {
|
||||
|
||||
func (m *customTrafficModel) QueryServerTrafficByDay(ctx context.Context, serverId int64, date time.Time) (*TotalTraffic, error) {
|
||||
var data TotalTraffic
|
||||
start := date.Truncate(24 * time.Hour)
|
||||
end := start.Add(24 * time.Hour).Add(-time.Nanosecond)
|
||||
start := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
end := start.AddDate(0, 0, 1).Add(-time.Nanosecond)
|
||||
err := m.Conn.WithContext(ctx).Model(&TrafficLog{}).
|
||||
Select("sum(download) as download, sum(upload) as upload").
|
||||
Where("server_id = ? AND timestamp BETWEEN ? AND ?", serverId, start, end).
|
||||
|
||||
@ -314,8 +314,8 @@ func (m *customUserModel) UpdateUserSubscribeWithTraffic(ctx context.Context, id
|
||||
|
||||
func (m *customUserModel) QueryResisterUserTotalByDate(ctx context.Context, date time.Time) (int64, error) {
|
||||
var total int64
|
||||
start := date.Truncate(24 * time.Hour)
|
||||
end := start.Add(24 * time.Hour).Add(-time.Second)
|
||||
start := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
|
||||
end := start.AddDate(0, 0, 1).Add(-time.Second)
|
||||
err := m.QueryNoCacheCtx(ctx, &total, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&User{}).Where("created_at > ? and created_at < ?", start, end).Count(&total).Error
|
||||
})
|
||||
|
||||
@ -2655,6 +2655,7 @@ type SubscribeDiscount struct {
|
||||
Quantity int64 `json:"quantity"`
|
||||
Discount float64 `json:"discount"`
|
||||
NewUserOnly bool `json:"new_user_only"`
|
||||
MapApple string `json:"map_apple"`
|
||||
}
|
||||
|
||||
type SubscribeGroup struct {
|
||||
|
||||
@ -292,7 +292,48 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有合并赠送订阅,则正常创建新订阅
|
||||
// 兜底:创建新订阅前,查找用户是否已有同套餐的订阅记录(含过期/赠送),
|
||||
// 有则复用旧记录续期,避免出现重复订阅。
|
||||
// 需要同时检查 UserId 和 SubscriptionUserId,因为家庭组绑定前后 owner 可能不同。
|
||||
if userSub == nil {
|
||||
candidateUserIds := []int64{orderInfo.UserId}
|
||||
if orderInfo.SubscriptionUserId > 0 && orderInfo.SubscriptionUserId != orderInfo.UserId {
|
||||
candidateUserIds = append(candidateUserIds, orderInfo.SubscriptionUserId)
|
||||
}
|
||||
var existingSub user.Subscribe
|
||||
if findErr := l.svc.DB.Model(&user.Subscribe{}).
|
||||
Where("user_id IN ? AND subscribe_id = ?", candidateUserIds, orderInfo.SubscribeId).
|
||||
Order("expire_time DESC").
|
||||
First(&existingSub).Error; findErr == nil {
|
||||
// 家庭组场景:订阅 owner 可能变更(如成员注册的试用 → 被家主收归),
|
||||
// 续期前把 user_id 校正为当前订单的 SubscriptionUserId
|
||||
effectiveOwner := orderInfo.UserId
|
||||
if orderInfo.SubscriptionUserId > 0 {
|
||||
effectiveOwner = orderInfo.SubscriptionUserId
|
||||
}
|
||||
if existingSub.UserId != effectiveOwner {
|
||||
existingSub.UserId = effectiveOwner
|
||||
}
|
||||
// 找到已有记录,走续期逻辑
|
||||
if renewErr := l.updateSubscriptionForRenewal(ctx, &existingSub, sub, orderInfo); renewErr != nil {
|
||||
logger.WithContext(ctx).Error("Fallback renew existing subscription failed, will create new",
|
||||
logger.Field("error", renewErr.Error()),
|
||||
logger.Field("existing_subscribe_id", existingSub.Id),
|
||||
logger.Field("order_no", orderInfo.OrderNo),
|
||||
)
|
||||
} else {
|
||||
userSub = &existingSub
|
||||
logger.WithContext(ctx).Infow("Fallback: renewed existing subscription instead of creating duplicate",
|
||||
logger.Field("existing_subscribe_id", existingSub.Id),
|
||||
logger.Field("order_no", orderInfo.OrderNo),
|
||||
logger.Field("candidate_user_ids", candidateUserIds),
|
||||
logger.Field("owner_corrected_to", effectiveOwner),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有可复用的订阅,才创建新订阅
|
||||
if userSub == nil {
|
||||
userSub, err = l.createUserSubscription(ctx, orderInfo, sub)
|
||||
if err != nil {
|
||||
|
||||
1
说明文档.md
1
说明文档.md
@ -16,6 +16,7 @@
|
||||
| 2026-03-12 | 分析并确认 Unknown column 错误 | [x] 已完成 | 确认为 `user_device` 缺少 `short_code` 字段,已提供 SQL |
|
||||
| 2026-03-12 | 提供 SSL 证书替换指令 | [x] 已完成 | 已提供备份与替换证书的组合指令 |
|
||||
| 2026-03-17 | 合并 internal 到 internal/main | [x] 已完成 | 已查验均为fast-forward,受限网络/权限,需手动push完成合并 |
|
||||
| 2026-04-14 | 排查支付成功但订阅未下发问题 | [x] 已完成 | 已提供 Docker 相关的日志排查与数据库核对命令 |
|
||||
|
||||
certbot certonly --manual --preferred-challenges dns -d airoport.win -d "*.airoport.win" -d hifastapp.com
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user