Files
story/internal/onboarding/ble_voice.go
T

101 lines
3.4 KiB
Go
Raw Normal View History

package onboarding
import (
"context"
"encoding/json"
"errors"
"time"
"data-terminal-cloud/internal/outbox"
)
// Wait in this HTTP request, not in the binding transaction. The outbox worker
// uses the same durable registration fence and may finish the work concurrently.
func (s *Service) finishBLEVoice(ctx context.Context, d Device, in BLEBindInput) (out map[string]any, err error) {
ctx, cancel := context.WithTimeout(ctx, 35*time.Second)
defer cancel()
defer func() {
if errors.Is(err, context.DeadlineExceeded) {
err = fail(504, "VOICE_REGISTRATION_TIMEOUT", "获取火山配置超时,绑定已保存,请使用原绑定令牌重试")
}
}()
started := false
for {
fields, event, e := s.bleVoiceSnapshot(ctx, d, in)
if e != nil {
return nil, e
}
switch fields["voice_status"] {
case "ready":
v, ok := fields["voice"].(DeviceVoiceCredential)
if !ok || v.InstanceID == "" || v.ProductKey == "" || v.DeviceName == "" || !validVoiceSecret(v.DeviceSecret) || !validRTCAppID(v.RTCAppID) {
return nil, fail(502, "VOICE_CONFIG_INVALID", "火山配置不完整,绑定已保存,请联系平台处理")
}
return map[string]any{"status": "success", "instance_id": v.InstanceID, "product_key": v.ProductKey, "device_name": v.DeviceName, "device_secret": v.DeviceSecret, "RTCAppID": v.RTCAppID}, nil
case "paused":
return nil, fail(503, "VOICE_REGISTRATION_PAUSED", "语音服务暂不可用,绑定已保存,请稍后使用原绑定令牌重试")
case "migration_required":
return nil, fail(409, "VOICE_MIGRATION_REQUIRED", "绑定已保存,旧设备需要补交原火山凭据,请联系平台处理")
case "reconcile_required":
return nil, fail(502, "VOICE_REGISTRATION_FAILED", "绑定已保存,火山注册结果未确认,请联系平台核对")
}
if !started {
started = true
if e = s.processServerActivation(ctx, event); e != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
return nil, fail(502, "VOICE_REGISTRATION_FAILED", "绑定已保存,获取火山配置失败,请稍后重试或联系平台处理")
}
continue
}
timer := time.NewTimer(250 * time.Millisecond)
select {
case <-ctx.Done():
timer.Stop()
return nil, ctx.Err()
case <-timer.C:
}
}
}
func (s *Service) bleVoiceSnapshot(ctx context.Context, authenticated Device, in BLEBindInput) (map[string]any, outbox.Event, error) {
var event outbox.Event
tx, err := s.pool.Begin(ctx)
if err != nil {
return nil, event, err
}
defer tx.Rollback(ctx)
d, err := readDevice(ctx, tx, authenticated.ID, true)
if err != nil {
return nil, event, err
}
c, err := readBLEClaim(ctx, tx, in.Token, d.ID)
if err != nil {
return nil, event, err
}
result, err := bleResult(ctx, tx, d, c, s.now())
if err != nil {
return nil, event, err
}
if !deviceAllowed(d) || result["status"] != "success" {
return nil, event, fail(409, "BINDING_CHANGED", "设备已解绑或归属发生变化,请重新绑定")
}
if err = bleClaimPermission(ctx, tx, c); err != nil {
return nil, event, err
}
fields, err := s.voiceFields(ctx, tx, d)
if err != nil {
return nil, event, err
}
j, err := readJob(ctx, tx, d.ID)
if err != nil {
return nil, event, err
}
event.Payload, err = json.Marshal(map[string]any{"device_id": d.ID, "activation_id": j.ID, "binding_generation": d.Generation})
if err != nil {
return nil, event, err
}
return fields, event, tx.Commit(ctx)
}