Files
story/internal/onboarding/device_reset.go
T
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

67 lines
3.0 KiB
Go

package onboarding
import (
"context"
"data-terminal-cloud/internal/devicebinding"
"data-terminal-cloud/internal/devicereset"
"data-terminal-cloud/internal/idempotency"
"errors"
)
// This is a DEVREQ1 adapter for the existing reset transaction, not an owner
// authorization shortcut. The account must release ownership before reset.
func (s *Service) DeviceResetState(ctx context.Context, d Device) (map[string]any, error) {
if !deviceAllowed(d) || d.Mode != "physical_confirm" {
return nil, errAccess
}
if err := devicebinding.RequireUnboundForReset(ctx, s.pool, d.ID); err != nil {
return nil, resetError(err)
}
var counter int64
if err := s.pool.QueryRow(ctx, `SELECT factory_reset_counter FROM device WHERE id=$1 AND tenant_id=$2`, d.ID, d.TenantID).Scan(&counter); err != nil {
return nil, err
}
return map[string]any{"reset_counter": counter, "binding_generation": d.Generation}, nil
}
func (s *Service) DeviceFactoryReset(ctx context.Context, d Device, requestID string, raw []byte, in devicereset.Input) (devicereset.Response, error) {
if !deviceAllowed(d) || d.Mode != "physical_confirm" {
return devicereset.Response{}, errAccess
}
service, err := devicereset.NewService(s.pool, idempotency.NewExecutor(s.pool), s.audit)
if err != nil {
return devicereset.Response{}, err
}
// The reset ID lives in the signed and persisted body. HTTP requests may use
// fresh nonce/request IDs while retrying an uncertain result after a reboot.
response, _, err := service.Reset(ctx, controlIdentity(d), "factory-reset:"+in.ResetID, requestID, raw, in)
return response, resetError(err)
}
func resetError(err error) error {
switch {
case err == nil:
return nil
case errors.Is(err, devicebinding.ErrConfirmationRequired):
return fail(409, "DEVICE_RESET_AUTHORIZATION_REQUIRED", "请先由当前账号授权解绑,再恢复出厂")
case errors.Is(err, devicereset.ErrCallInProgress):
return fail(409, "DEVICE_RESET_CALL_IN_PROGRESS", "设备正在通话,请结束后再恢复出厂")
case errors.Is(err, devicereset.ErrFirmwareInProgress):
return fail(409, "DEVICE_RESET_FIRMWARE_IN_PROGRESS", "设备正在升级,暂时不能恢复出厂")
case errors.Is(err, devicereset.ErrPendingTranscripts):
return fail(409, "DEVICE_RESET_PENDING_TRANSCRIPTS", "设备仍有未上传的聊天记录")
case errors.Is(err, devicereset.ErrResetCounterConflict):
return fail(409, "DEVICE_RESET_COUNTER_CONFLICT", "设备重置计数器已变化")
case errors.Is(err, devicereset.ErrStateConflict):
return fail(409, "DEVICE_RESET_STATE_CONFLICT", "设备状态已变化,请重新确认")
case errors.Is(err, devicereset.ErrInvalid):
return fail(400, "INVALID_RESET_REQUEST", "设备重置参数不正确")
case errors.Is(err, idempotency.ErrKeyConflict):
return fail(409, "IDEMPOTENCY_KEY_CONFLICT", "同一重置请求不能修改内容")
case errors.Is(err, idempotency.ErrInProgress):
return fail(409, "IDEMPOTENCY_IN_PROGRESS", "设备重置请求仍在处理中")
default:
return err
}
}