fix: deduplicate account vehicle permissions

This commit is contained in:
lingniu
2026-07-16 14:16:22 +08:00
parent a1195fb97d
commit 48e2ce9fd4
5 changed files with 175 additions and 20 deletions

View File

@@ -41,20 +41,26 @@ var customerMenuSet = map[string]bool{
var adminMenus = []string{"monitor", "vehicles", "tracks", "history", "statistics", "alerts", "access", "operations", "users"}
type authUser struct {
ID uint64 `json:"id"`
Username string `json:"username"`
DisplayName string `json:"displayName"`
UserType string `json:"userType"`
Status string `json:"status"`
CustomerRef string `json:"customerRef"`
TenantRef string `json:"tenantRef"`
AuthProvider string `json:"authProvider"`
ExternalSubject string `json:"externalSubject,omitempty"`
MenuKeys []string `json:"menuKeys"`
VehicleVINs []string `json:"vehicleVins"`
LastLoginAt *time.Time `json:"lastLoginAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID uint64 `json:"id"`
Username string `json:"username"`
DisplayName string `json:"displayName"`
UserType string `json:"userType"`
Status string `json:"status"`
CustomerRef string `json:"customerRef"`
TenantRef string `json:"tenantRef"`
AuthProvider string `json:"authProvider"`
ExternalSubject string `json:"externalSubject,omitempty"`
MenuKeys []string `json:"menuKeys"`
VehicleVINs []string `json:"vehicleVins"`
Vehicles []authVehicleGrant `json:"vehicles"`
LastLoginAt *time.Time `json:"lastLoginAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type authVehicleGrant struct {
VIN string `json:"vin"`
Plate string `json:"plate"`
}
type localCredential struct {
@@ -385,6 +391,7 @@ func (s *authStore) listUsers(w http.ResponseWriter, r *http.Request) {
}
defer rows.Close()
users := []authUser{}
allVINs := []string{}
for rows.Next() {
var user authUser
var external sql.NullString
@@ -400,11 +407,75 @@ func (s *authStore) listUsers(w http.ResponseWriter, r *http.Request) {
}
user.MenuKeys = principal.MenuKeys
user.VehicleVINs = principal.VehicleVINs
allVINs = append(allVINs, principal.VehicleVINs...)
users = append(users, user)
}
if err := rows.Err(); err != nil {
httpx.WriteError(w, http.StatusInternalServerError, "USER_LIST_FAILED", "无法读取账号列表", "", requestTraceID(r))
return
}
vehicleLabels, err := loadAuthVehicleGrants(r.Context(), s.db, normalizeVINs(allVINs))
if err != nil {
httpx.WriteError(w, http.StatusInternalServerError, "USER_LIST_FAILED", "无法读取车辆权限信息", "", requestTraceID(r))
return
}
for index := range users {
users[index].Vehicles = make([]authVehicleGrant, 0, len(users[index].VehicleVINs))
for _, vin := range users[index].VehicleVINs {
vehicle := vehicleLabels[vin]
if vehicle.VIN == "" {
vehicle.VIN = vin
}
users[index].Vehicles = append(users[index].Vehicles, vehicle)
}
}
httpx.WriteOK(w, requestTraceID(r), users)
}
func loadAuthVehicleGrants(ctx context.Context, db *sql.DB, vins []string) (map[string]authVehicleGrant, error) {
result := make(map[string]authVehicleGrant, len(vins))
for start := 0; start < len(vins); start += 250 {
end := start + 250
if end > len(vins) {
end = len(vins)
}
part := vins[start:end]
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(part)), ",")
args := make([]any, 0, len(part)*2)
for range 2 {
for _, vin := range part {
args = append(args, vin)
}
}
query := `SELECT vin, COALESCE(` +
`MAX(CASE WHEN source_priority=0 THEN NULLIF(plate,'') END),` +
`MAX(CASE WHEN source_priority=1 THEN NULLIF(plate,'') END),'') ` +
`FROM (` +
`SELECT UPPER(TRIM(vin)) AS vin,plate,0 AS source_priority FROM vehicle_identity_binding WHERE vin IN (` + placeholders + `) ` +
`UNION ALL ` +
`SELECT UPPER(TRIM(vin)) AS vin,plate,1 AS source_priority FROM vehicle_realtime_snapshot WHERE vin IN (` + placeholders + `)` +
`) vehicle_grants GROUP BY vin`
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
for rows.Next() {
var vehicle authVehicleGrant
if err := rows.Scan(&vehicle.VIN, &vehicle.Plate); err != nil {
rows.Close()
return nil, err
}
vehicle.VIN = strings.ToUpper(strings.TrimSpace(vehicle.VIN))
vehicle.Plate = strings.TrimSpace(vehicle.Plate)
result[vehicle.VIN] = vehicle
}
if err := rows.Close(); err != nil {
return nil, err
}
}
return result, nil
}
func (s *authStore) createCustomer(w http.ResponseWriter, r *http.Request, actor platform.Principal) {
var input userMutation
if !decodeAuthJSON(w, r, &input) {