1. O policial entra em serviço e a guarnição fica disponível
Sem em não há a quem despachar. O pool de são as guarnições com status disponivel, e uma guarnição só chega lá depois de quatro atos. A é da pessoa; a é da guarnição. Quem serve na central tem jornada igual, só não entra em guarnição.
A cena
A cena 3D deste caso (roteiro entrar-em-servico) entra aqui quando a operação 3D publicar os roteiros dos casos de uso; até lá, o diagrama abaixo é a cena.
A ordem é cobrada pelo domínio, e as recusas dizem o que falta: declararProntidao antes de assumirComando devolve comando-nao-assumido; assumirComando com a jornada do ainda apresentada devolve jornada-nao-iniciada. Pausas (iniciarPausa, da pessoa; iniciarPausaDaGuarnicao, da ) tiram do pool e devolvem ao estado anterior ao encerrar. O não alcança pausa.
| quem | verbos |
|---|---|
| policial | registrarApresentacao, iniciarJornada, iniciarPausa, encerrarJornada |
| comandante | assumirComando, declararProntidao, retirarDeProntidao, iniciarPausaDaGuarnicao |
| supervisor | constituirGuarnicao, substituirIntegrante, dissolverGuarnicao, liberarPolicial |
| integrador () | tudo acima menos pausa, e liberação: ele declara a escala dele |
Pelo app do policial
O primeiro GET depois do login responde "onde estou": tenho jornada? integro guarnição?
/** O primeiro GET depois do login: tenho jornada? integro guarnição? */
async ondeEstou(): Promise<SituacaoDoPolicial> {
const [jornada, guarnicao] = await Promise.all([
this.jornada.obterJornadaAtiva(this.usuario),
this.jornada.obterGuarnicaoDoUsuario(this.usuario),
]);
if (jornada === undefined) return { situacao: 'fora-de-servico' };
if (guarnicao === undefined) return { jornada, situacao: 'em-jornada' };
return { guarnicao, jornada, situacao: 'em-guarnicao' };
}/** O primeiro GET depois do login: tenho jornada? integro guarnição? */
async ondeEstou() {
const [jornada, guarnicao] = await Promise.all([
this.jornada.obterJornadaAtiva(this.usuario),
this.jornada.obterGuarnicaoDoUsuario(this.usuario),
]);
if (jornada === undefined) return { situacao: 'fora-de-servico' };
if (guarnicao === undefined) return { jornada, situacao: 'em-jornada' };
return { guarnicao, jornada, situacao: 'em-guarnicao' };
}curl https://geohub.exemplo.gov.br/plugins/policiais/$USUARIO/jornada-ativa -H "Authorization: Bearer $TOKEN"
# {"dados":{"id":"…","status":"iniciada",…}} ou {"dados":null}
curl https://geohub.exemplo.gov.br/plugins/policiais/$USUARIO/guarnicao -H "Authorization: Bearer $TOKEN"
# {"dados":{"id":"…","viatura":"RP-1207","status":"disponivel",…}} ou {"dados":null}Comparecer e entrar em serviço são dois verbos, porque entre um e outro existe um intervalo real: quem se apresentou ainda não trabalha.
/**
* Comparecer (UC-06) e entrar em serviço (UC-14) são dois verbos: entre um
* e outro existe um intervalo real, e quem se apresentou ainda não
* trabalha. O app que só tem "entrou de serviço" chama os dois em seguida.
*/
async apresentarSeEIniciar(posto: PostoOcupado): Promise<JornadaId> {
const jornadaId = exigir(
await this.jornada.registrarApresentacao(this.usuario),
);
exigir(await this.jornada.iniciarJornada({ id: jornadaId, posto }));
return jornadaId;
}/**
* Comparecer (UC-06) e entrar em serviço (UC-14) são dois verbos: entre um
* e outro existe um intervalo real, e quem se apresentou ainda não
* trabalha. O app que só tem "entrou de serviço" chama os dois em seguida.
*/
async apresentarSeEIniciar(posto) {
const jornadaId = exigir(
await this.jornada.registrarApresentacao(this.usuario),
);
exigir(await this.jornada.iniciarJornada({ id: jornadaId, posto }));
return jornadaId;
}curl -X POST https://geohub.exemplo.gov.br/plugins/policiais/$USUARIO/apresentacao -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)"
# {"dados":"<jornadaId>"}
curl -X POST https://geohub.exemplo.gov.br/plugins/jornadas/$JORNADA/inicio -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" -H "Content-Type: application/json" \
-d '{"posto":{"modalidade":"radio_patrulhamento"}}'
# {"dados":null}Com a guarnição constituída pelo supervisor, o comandante assume e põe a equipe no pool.
/**
* Só o comandante faz isto, e só com a guarnição já constituída: assumir
* o comando e pôr a equipe no pool de despacho. Fora de ordem, o domínio
* responde `comando-nao-assumido` ou `jornada-nao-iniciada`.
*/
async assumirComandoEDeclararProntidao(): Promise<void> {
const onde = await this.ondeEstou();
if (onde.situacao !== 'em-guarnicao') {
throw new SituacaoInesperadaError(onde.situacao, 'em-guarnicao');
}
exigir(
await this.jornada.assumirComando({
guarnicaoId: onde.guarnicao.id,
jornadaId: onde.jornada.id,
}),
);
exigir(await this.jornada.declararProntidao(onde.guarnicao.id));
}/**
* Só o comandante faz isto, e só com a guarnição já constituída: assumir
* o comando e pôr a equipe no pool de despacho. Fora de ordem, o domínio
* responde `comando-nao-assumido` ou `jornada-nao-iniciada`.
*/
async assumirComandoEDeclararProntidao() {
const onde = await this.ondeEstou();
if (onde.situacao !== 'em-guarnicao') {
throw new SituacaoInesperadaError(onde.situacao, 'em-guarnicao');
}
exigir(
await this.jornada.assumirComando({
guarnicaoId: onde.guarnicao.id,
jornadaId: onde.jornada.id,
}),
);
exigir(await this.jornada.declararProntidao(onde.guarnicao.id));
}curl -X POST https://geohub.exemplo.gov.br/plugins/guarnicoes/$GUARNICAO/comando -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" -H "Content-Type: application/json" \
-d '{"jornadaId":"…"}'
curl -X POST https://geohub.exemplo.gov.br/plugins/guarnicoes/$GUARNICAO/prontidao -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)"
# {"dados":null} (a guarnição entra no pool, status disponivel)Pelo CAD
O integrador faz o mesmo em nome da escala dele. Aqui o usuario é a matrícula no sistema do CAD, que a exposição traduz para o UsuarioId pela tabela integrador_identidade.
import {
criarClienteJornada,
matricula,
modalidadeDeServicoId,
usuarioId,
} from '@geocontrol/geohub-sdk/jornada';
import { CONEXAO } from './conexao.js';
const jornada = criarClienteJornada(CONEXAO);
// Na sessão de integrador, o usuário dos verbos é a matrícula do policial.
const policial = usuarioId(matricula('123456'));
// Dois verbos: comparecer à unidade e entrar em serviço no posto ocupado.
const apresentacao = await jornada.registrarApresentacao(policial);
if (!apresentacao.success) throw new Error(apresentacao.error.codigo);
export const jornadaId = apresentacao.data;
export const inicio = await jornada.iniciarJornada({
id: jornadaId,
posto: { modalidade: modalidadeDeServicoId('radio_patrulhamento') },
});import {
criarClienteJornada,
matricula,
modalidadeDeServicoId,
usuarioId,
} from '@geocontrol/geohub-sdk/jornada';
import { CONEXAO } from './conexao.js';
const jornada = criarClienteJornada(CONEXAO);
// Na sessão de integrador, o usuário dos verbos é a matrícula do policial.
const policial = usuarioId(matricula('123456'));
// Dois verbos: comparecer à unidade e entrar em serviço no posto ocupado.
const apresentacao = await jornada.registrarApresentacao(policial);
if (!apresentacao.success) throw new Error(apresentacao.error.codigo);
export const jornadaId = apresentacao.data;
export const inicio = await jornada.iniciarJornada({
id: jornadaId,
posto: { modalidade: modalidadeDeServicoId('radio_patrulhamento') },
});# O usuário é a matrícula: a exposição a traduz pela tabela de identidades.
curl -X POST https://geohub.exemplo.gov.br/plugins/policiais/123456/apresentacao -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" \
-H 'x-autoria: {"tipo":"integrador","id":"'$INTEGRADOR'"}'
# {"dados":"<jornadaId>"}
curl -X POST https://geohub.exemplo.gov.br/plugins/jornadas/$JORNADA/inicio -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" -H "Content-Type: application/json" \
-d '{"posto":{"modalidade":"radio_patrulhamento"}}'
# {"dados":null}import {
criarClienteJornada,
matricula,
modalidadeDeServicoId,
usuarioId,
viaturaId,
} from '@geocontrol/geohub-sdk/jornada';
import { CONEXAO } from './conexao.js';
const jornada = criarClienteJornada(CONEXAO);
const posto = { modalidade: modalidadeDeServicoId('radio_patrulhamento') };
// Os dois policiais já em serviço (apresentação e início, cada um).
const comandante = usuarioId(matricula('123456'));
const motorista = usuarioId(matricula('654321'));
const jornadaDoComandante = await jornada.registrarApresentacao(comandante);
if (!jornadaDoComandante.success)
throw new Error(jornadaDoComandante.error.codigo);
await jornada.iniciarJornada({ id: jornadaDoComandante.data, posto });
const jornadaDoMotorista = await jornada.registrarApresentacao(motorista);
if (!jornadaDoMotorista.success)
throw new Error(jornadaDoMotorista.error.codigo);
await jornada.iniciarJornada({ id: jornadaDoMotorista.data, posto });
// A guarnição na viatura: constituir, o comandante assume, prontidão.
const constituida = await jornada.constituirGuarnicao({
composicao: [
{ posto: 'comandante', usuario: comandante },
{ posto: 'motorista', usuario: motorista },
],
viatura: viaturaId('RP-1207'),
});
if (!constituida.success) throw new Error(constituida.error.codigo);
export const guarnicaoId = constituida.data;
await jornada.assumirComando({
guarnicaoId,
jornadaId: jornadaDoComandante.data,
});
export const prontidao = await jornada.declararProntidao(guarnicaoId);import {
criarClienteJornada,
matricula,
modalidadeDeServicoId,
usuarioId,
viaturaId,
} from '@geocontrol/geohub-sdk/jornada';
import { CONEXAO } from './conexao.js';
const jornada = criarClienteJornada(CONEXAO);
const posto = { modalidade: modalidadeDeServicoId('radio_patrulhamento') };
// Os dois policiais já em serviço (apresentação e início, cada um).
const comandante = usuarioId(matricula('123456'));
const motorista = usuarioId(matricula('654321'));
const jornadaDoComandante = await jornada.registrarApresentacao(comandante);
if (!jornadaDoComandante.success)
throw new Error(jornadaDoComandante.error.codigo);
await jornada.iniciarJornada({ id: jornadaDoComandante.data, posto });
const jornadaDoMotorista = await jornada.registrarApresentacao(motorista);
if (!jornadaDoMotorista.success)
throw new Error(jornadaDoMotorista.error.codigo);
await jornada.iniciarJornada({ id: jornadaDoMotorista.data, posto });
// A guarnição na viatura: constituir, o comandante assume, prontidão.
const constituida = await jornada.constituirGuarnicao({
composicao: [
{ posto: 'comandante', usuario: comandante },
{ posto: 'motorista', usuario: motorista },
],
viatura: viaturaId('RP-1207'),
});
if (!constituida.success) throw new Error(constituida.error.codigo);
export const guarnicaoId = constituida.data;
await jornada.assumirComando({
guarnicaoId,
jornadaId: jornadaDoComandante.data,
});
export const prontidao = await jornada.declararProntidao(guarnicaoId);curl -X POST https://geohub.exemplo.gov.br/plugins/guarnicoes -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" -H "Content-Type: application/json" \
-d '{"viatura":"RP-1207","composicao":[{"usuario":"123456","posto":"comandante"},{"usuario":"654321","posto":"motorista"}]}'
# {"dados":"<guarnicaoId>"}
curl -X POST https://geohub.exemplo.gov.br/plugins/guarnicoes/$GUARNICAO/comando -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)" -H "Content-Type: application/json" \
-d '{"jornadaId":"<jornadaId do comandante>"}'
curl -X POST https://geohub.exemplo.gov.br/plugins/guarnicoes/$GUARNICAO/prontidao -H "Authorization: Bearer $TOKEN" -H "x-solicitacao-id: $(uuidv7)"
# {"dados":null}Pela REST
Cada rota está na API, com corpo, respostas e erros.
| rota | verbo |
|---|---|
POST /plugins/policiais/{usuario}/apresentacao | registrarApresentacao |
POST /plugins/jornadas/{id}/inicio | iniciarJornada |
POST /plugins/guarnicoes | constituirGuarnicao |
POST /plugins/guarnicoes/{guarnicaoId}/comando | assumirComando |
POST /plugins/guarnicoes/{guarnicaoId}/prontidao | declararProntidao |
GET /plugins/policiais/{usuario}/jornada-ativa | obterJornadaAtiva |
GET /plugins/policiais/{usuario}/guarnicao | obterGuarnicaoDoUsuario |
GET /plugins/viaturas/{viatura}/guarnicao-disponivel | guarnicaoDisponivelNaViatura |
POST /plugins/policiais/{usuario}/apresentacao → 200 {"dados":"<jornadaId>"}
POST /plugins/jornadas/{jornadaId}/inicio
{"posto":{"modalidade":"radio-patrulhamento"}} → 200 {"dados":null}
POST /plugins/guarnicoes
{"viatura":"RP-1207","composicao":[{"usuario":"…","posto":"comandante"},{"usuario":"…","posto":"motorista"}]}
→ 200 {"dados":"<guarnicaoId>"}
POST /plugins/guarnicoes/{guarnicaoId}/comando {"jornadaId":"…"}
POST /plugins/guarnicoes/{guarnicaoId}/prontidao → guarnição disponivelOs cabeçalhos e o formato das respostas estão em REST e WebSocket.