Align console behavior with operator routing and image workflow expectations

Constraint: Must preserve current exchange-rate logic, same-group routing boundary, and temporary image-file behavior while fixing UX-visible inconsistencies.
Rejected: Large frontend chunk refactor | introduced risky circular chunking and did not improve deliverability for this rollout.
Confidence: medium
Scope-risk: moderate
Directive: Keep channel failover constrained to the selected group and preserve Beijing-day semantics for user cache dashboard metrics.
Tested: go test ./controller ./model ./service ./common -count=1; npm run build; remote smoke on source deployment at http://38.76.218.56:18081/ plus /api/status and /api/image-studio/settings.
Not-tested: Browser-driven end-to-end verification of every console page interaction.
This commit is contained in:
ikmkj
2026-05-05 15:27:51 +08:00
parent 92ee465a41
commit 8ee89478a9
46 changed files with 1341 additions and 289 deletions

View File

@@ -1,9 +1,7 @@
package model
import (
"errors"
"fmt"
"math/rand"
"sort"
"strings"
"sync"
@@ -115,13 +113,6 @@ func GetRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
return nil, nil
}
if len(channels) == 1 {
if channel, ok := channelsIDM[channels[0]]; ok {
return channel, nil
}
return nil, fmt.Errorf("数据库一致性错误,渠道# %d 不存在,请联系管理员修复", channels[0])
}
uniquePriorities := make(map[int]bool)
for _, channelId := range channels {
if channel, ok := channelsIDM[channelId]; ok {
@@ -136,58 +127,50 @@ func GetRandomSatisfiedChannel(group string, model string, retry int) (*Channel,
}
sort.Sort(sort.Reverse(sort.IntSlice(sortedUniquePriorities)))
if retry >= len(uniquePriorities) {
retry = len(uniquePriorities) - 1
if len(sortedUniquePriorities) == 0 {
return nil, nil
}
if retry < 0 {
retry = 0
}
if retry >= len(channels) {
retry = len(channels) - 1
}
targetPriority := int64(sortedUniquePriorities[retry])
// get the priority for the given retry number
var sumWeight = 0
var targetChannels []*Channel
for _, channelId := range channels {
if channel, ok := channelsIDM[channelId]; ok {
orderedChannels := make([]*Channel, 0, len(channels))
for _, priority := range sortedUniquePriorities {
targetPriority := int64(priority)
targetChannels := make([]*Channel, 0)
for _, channelId := range channels {
channel, ok := channelsIDM[channelId]
if !ok {
return nil, fmt.Errorf("数据库一致性错误,渠道# %d 不存在,请联系管理员修复", channelId)
}
if channel.GetPriority() == targetPriority {
sumWeight += channel.GetWeight()
targetChannels = append(targetChannels, channel)
}
} else {
return nil, fmt.Errorf("数据库一致性错误,渠道# %d 不存在,请联系管理员修复", channelId)
}
sort.SliceStable(targetChannels, func(i, j int) bool {
left := targetChannels[i]
right := targetChannels[j]
if left.GetWeight() != right.GetWeight() {
return left.GetWeight() > right.GetWeight()
}
if left.GetPriority() != right.GetPriority() {
return left.GetPriority() > right.GetPriority()
}
return left.Id < right.Id
})
orderedChannels = append(orderedChannels, targetChannels...)
}
if len(targetChannels) == 0 {
return nil, errors.New(fmt.Sprintf("no channel found, group: %s, model: %s, priority: %d", group, model, targetPriority))
if len(orderedChannels) == 0 {
return nil, nil
}
// smoothing factor and adjustment
smoothingFactor := 1
smoothingAdjustment := 0
if sumWeight == 0 {
// when all channels have weight 0, set sumWeight to the number of channels and set smoothing adjustment to 100
// each channel's effective weight = 100
sumWeight = len(targetChannels) * 100
smoothingAdjustment = 100
} else if sumWeight/len(targetChannels) < 10 {
// when the average weight is less than 10, set smoothing factor to 100
smoothingFactor = 100
if retry >= len(orderedChannels) {
retry = len(orderedChannels) - 1
}
// Calculate the total weight of all channels up to endIdx
totalWeight := sumWeight * smoothingFactor
// Generate a random value in the range [0, totalWeight)
randomWeight := rand.Intn(totalWeight)
// Find a channel based on its weight
for _, channel := range targetChannels {
randomWeight -= channel.GetWeight()*smoothingFactor + smoothingAdjustment
if randomWeight < 0 {
return channel, nil
}
}
// return null if no channel is not found
return nil, errors.New("channel not found")
return orderedChannels[retry], nil
}
func CacheGetChannel(id int) (*Channel, error) {