package onboarding import ( "context" "encoding/json" "errors" "strings" "data-terminal-cloud/internal/device" "data-terminal-cloud/internal/deviceauth" "data-terminal-cloud/internal/deviceconfig" "github.com/google/uuid" "github.com/jackc/pgx/v5" ) // These routes use DEVREQ1 factory authentication. A device never supplies the // platform UUID or tenant used to read configurations or acknowledge commands. type DeviceControlReport struct { Firmware string `json:"firmware_version"` Revision int64 `json:"reported_revision"` Speaker int `json:"speaker"` Busy bool `json:"busy"` RSSI int `json:"rssi"` DisplayName *string `json:"display_name,omitempty"` Power *DevicePowerReport `json:"power,omitempty"` ScreenWake *DeviceScreenWake `json:"screen_wake,omitempty"` } type DeviceScreenWake struct { ID string `json:"id"` Revision int64 `json:"revision"` } type DeviceScreenWakeReceipt struct { DeviceScreenWake Result string `json:"result"` } type DevicePowerReport struct { Mode string `json:"mode"` MicrophonePaused bool `json:"microphone_paused"` BacklightPercent int `json:"backlight_percent"` MicrophoneReads uint64 `json:"microphone_reads"` } func controlIdentity(d Device) deviceauth.Identity { return deviceauth.Identity{ID: d.ID, PublicID: d.PublicID, TenantID: d.TenantID, Status: d.Status, ActivationState: d.Lifecycle, EnrollmentMode: d.Mode, BindingGeneration: d.Generation} } func validateControlReport(in DeviceControlReport) error { if wake := in.ScreenWake; wake != nil { if _, err := uuid.Parse(wake.ID); err != nil || len(wake.ID) != 36 || wake.Revision <= 0 || in.Power == nil || in.Power.Mode != "active" { return fail(400, "INVALID_CONTROL_REPORT", "屏幕唤醒状态不正确") } } if p := in.Power; p != nil { if p.BacklightPercent < -1 || p.BacklightPercent > 100 || p.MicrophoneReads > 4294967295 || (p.Mode != "active" && p.Mode != "standby" && p.Mode != "transition") || (p.Mode == "standby" && (!p.MicrophonePaused || p.BacklightPercent != 0)) || (p.Mode == "active" && (p.MicrophonePaused || p.BacklightPercent <= 0)) { return fail(400, "INVALID_CONTROL_REPORT", "待机状态不正确") } } if in.DisplayName != nil && !deviceconfig.ValidDisplayName(*in.DisplayName) { return fail(400, "INVALID_CONTROL_REPORT", "设备名称不正确") } if in.Firmware == "" || len(in.Firmware) > 80 || strings.TrimSpace(in.Firmware) != in.Firmware || in.Revision < 0 || in.Speaker < 0 || in.Speaker > 100 || in.RSSI < -127 || in.RSSI > 0 { return fail(400, "INVALID_CONTROL_REPORT", "设备运行状态不正确") } return nil } // An authenticated physical wake only changes power. A revision fence prevents // an offline gesture or a lost-response retry from undoing newer cloud settings. // Its receipt and the actual report commit with the desired state under one lock. func reconcileScreenWake(ctx context.Context, tx pgx.Tx, d Device, wake *DeviceScreenWake) (*DeviceScreenWakeReceipt, error) { if wake == nil { return nil, nil } var revision int64 var desired, previous []byte if err := tx.QueryRow(ctx, `SELECT desired_revision,desired_config, COALESCE(reported_config->'screen_wake','null'::jsonb) FROM device_twin WHERE device_id=$1 AND tenant_id=$2 FOR UPDATE`, d.ID, d.TenantID).Scan(&revision, &desired, &previous); err != nil { return nil, err } var receipt DeviceScreenWakeReceipt if json.Unmarshal(previous, &receipt) == nil && receipt.ID == wake.ID { if receipt.Revision != wake.Revision { return nil, fail(409, "SCREEN_WAKE_CONFLICT", "唤醒请求已变化") } return &receipt, nil } receipt = DeviceScreenWakeReceipt{DeviceScreenWake: *wake, Result: "superseded"} if revision != wake.Revision { return &receipt, nil } var config map[string]json.RawMessage if err := json.Unmarshal(desired, &config); err != nil { return nil, err } var power struct { Mode string `json:"mode"` } if err := json.Unmarshal(config["power"], &power); err != nil || (power.Mode != "standby" && power.Mode != "active") { return &receipt, nil } if power.Mode == "standby" { if _, err := tx.Exec(ctx, `UPDATE device_twin SET desired_config=jsonb_set(desired_config,'{power}','{"mode":"active"}'::jsonb), desired_revision=desired_revision+1,updated_at=now() WHERE device_id=$1 AND tenant_id=$2`, d.ID, d.TenantID); err != nil { return nil, err } } receipt.Result = "applied" return &receipt, nil } func (s *Service) DeviceControlSync(ctx context.Context, d Device, in DeviceControlReport) (map[string]any, error) { if !deviceAllowed(d) { return nil, errAccess } if err := validateControlReport(in); err != nil { return nil, err } tx, err := s.pool.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) wakeReceipt, err := reconcileScreenWake(ctx, tx, d, in.ScreenWake) if err != nil { return nil, err } // Revision zero means the device has not applied cloud configuration yet. // Report hardware readings, never copy desired values into reported_config. values := map[string]any{"volume": map[string]any{"speaker": in.Speaker}, "network": map[string]any{"type": "wifi", "rssi": in.RSSI}} // Older firmware omits the field; omission must not claim a name was applied. if in.DisplayName != nil { values["identity"] = map[string]any{"display_name": *in.DisplayName} } if in.Power != nil { values["power"] = in.Power } if wakeReceipt != nil { values["screen_wake"] = wakeReceipt } reported, _ := json.Marshal(values) tag, err := tx.Exec(ctx, `UPDATE device_twin SET firmware_version=$3, reported_config=reported_config || $4::jsonb, reported_revision=NULLIF($5,0), last_reported_at=now(),updated_at=now() WHERE device_id=$1 AND tenant_id=$2 AND $5<=desired_revision AND ($5=0 OR reported_revision IS NULL OR $5>=reported_revision)`, d.ID, d.TenantID, in.Firmware, reported, in.Revision) if err != nil { return nil, err } if tag.RowsAffected() != 1 { return nil, fail(409, "CONFIG_REVISION_CONFLICT", "配置版本已变化,请重新同步") } if err := tx.Commit(ctx); err != nil { return nil, err } repo := device.NewRepository(s.pool) config, err := repo.GetConfiguration(ctx, controlIdentity(d)) if err != nil { return nil, err } items := []device.Command{} if !in.Busy { items, err = repo.PullCommands(ctx, controlIdentity(d), 1) if err != nil { return nil, err } } out := map[string]any{"configuration": config, "commands": items} if wakeReceipt != nil { out["screen_wake"] = wakeReceipt } return out, nil } type DeviceControlAck struct { ID string `json:"command_id"` Status string `json:"status"` Result map[string]any `json:"result_summary"` } func validateControlAck(in DeviceControlAck) error { if _, err := uuid.Parse(in.ID); err != nil { return fail(400, "INVALID_COMMAND_ACK", "命令编号不正确") } if in.Status != "ACKNOWLEDGED" && in.Status != "SUCCEEDED" && in.Status != "FAILED" { return fail(400, "INVALID_COMMAND_ACK", "命令状态不正确") } raw, err := json.Marshal(in.Result) if err != nil || len(raw) > 4096 { return fail(400, "INVALID_COMMAND_ACK", "诊断结果过大或格式不正确") } return nil } func (s *Service) DeviceControlAcknowledge(ctx context.Context, d Device, in DeviceControlAck) (map[string]any, error) { if !deviceAllowed(d) { return nil, errAccess } if err := validateControlAck(in); err != nil { return nil, err } if in.Result == nil { in.Result = map[string]any{} } _, err := device.NewRepository(s.pool).AcknowledgeCommand(ctx, controlIdentity(d), in.ID, device.CommandAcknowledgement{Status: in.Status, ResultSummary: in.Result}) if err != nil { // A missing, foreign, expired, or terminal command cannot gain authority. if !errors.Is(err, pgx.ErrNoRows) { return nil, err } return nil, fail(409, "DEVICE_COMMAND_STATE_CONFLICT", "命令不存在、已过期或状态已变化") } return map[string]any{"status": "ok"}, nil }