Skip to main content

AS IS — US 198113 | Inativação de usuário (job)

Data: 2026-07-09
Repo principal: coezzion-service-jobs
US: context/us.md


1. Problema descrito na US

A rotina de inativação hoje:

  • grava DateDeleted em Users
  • altera Status em Users

e não:

  • grava DateDeleted em Sellers
  • remove vínculos em UserOrganization

Consequência: usuário deixa de acessar o sistema, mas continua referenciado como vendedor e em organizações.


2. Rotina no jobs que casa com a US

2.1 DeleteUserAsync

Arquivo: coezzion-service-jobs/src/Jobs.Infrastructure/Data/Repositories/UserRepository.cs
Interface: Jobs.Domain/Interfaces/Data/Repositories/IUserRepository.cs

public async Task DeleteUserAsync(int userId)
{
var sql = """
update "Users"
set "DateDeleted" = NOW() AT TIME ZONE 'America/Sao_Paulo'
, "Status" = 2
where "Id" = @userId;
""";

using var connection =
await _dbConnectionFactory.GetConnectionAsync(EConnectionType.Write, EDatabaseType.CoreDb);
var result = await connection.ExecuteAsync(sql, new { userId });
}
AspectoValor
StackDapper + IDbConnectionFactory
DBWrite / CoreDb (PostgresCoreDbCoreDB)
Status2 = EntityStatus.Inactive
TimezoneAmerica/Sao_Paulo
Sellersnão tocado
UserOrganizationnão tocado
Schema paramnão recebe (caller tem schema, não passa)

2.2 Caller único no jobs

Handler: Jobs.API/Application/Messages/Commands/LogoutUsersCommands/LogoutUsersCommandHandler.cs
Job: LogoutOldAppVersionUsersJob → command com argument.Schema
Scheduler: JobSchedulerLogoutOldAppVersionUsersJobArgument("arezzo")

Fluxo:

Hangfire recurrent: LogoutOldAppVersionUsers
→ SchemaNameScopedWrapper = "arezzo"
→ GetOldAppVersionUsersAsync(schema, daysBack=30)
→ filtro: LastLogin <= hoje - 60 dias → canDeleteUser
→ se canDeleteUser: DeleteUserAsync(userId) // só Users
→ sempre: LogoutUserEvent → SQS (desloga sessão)

Constantes do handler:

  • DaysBack = 30 (seleção de candidatos por app version / login)
  • MaxDaysToConsider = 60 (critério para deletar vs só logout)
  • MaxDegreeOfParallelism = 3

2.3 Job correlato (não é a rotina da US)

LogoutDeletedUserJob: varre refresh tokens no Redis; se IsUserActive(userId) for false, remove tokens.
Não inativa usuários — apenas reage a users já inativos. Sem mudança obrigatória se Status continuar sendo setado.


3. Modelagem de dados

ConceitoTabelaSoft/HardObservação
Userpublic."Users"Soft (DateDeleted) + Status Active=1 / Inactive=2UserModel : BaseEntity
UserOrganizationpublic."UserOrganization"Hard delete das linhasJoin N:N EF; colunas UsersId, OrganizationsId; sem DateDeleted
Seller{tenant}."Sellers"Soft (DateDeleted)SellerModel : BaseEntity; UserId, StoreId, voucher, etc. Sem Status

BaseEntity.Delete() (coezzion-nuget-common): só seta DateDeleted.
UserModel.SetDateDeleted(): base.Delete() + UserForceLogoutEvent + DateUpdated.
UserModel.UpdateStatus: só muda Status.

Mapeamento UO: Core.DB/Data/Mappings/UsersMapping.cs.UsingEntity(x => x.ToTable("UserOrganization")).


4. Multi-tenancy e banco

Connection string (dev)DatabaseUso
PostgresCoreDbCoreDBUsers, UserOrganization, Dapper delete
PostgresCoreDbOrgCoreDB + SearchPath={0}Sellers/Stores via EF multi-tenant

Conclusão: public.Users, public.UserOrganization e {schema}.Sellers estão no mesmo Postgres CoreDB. Uma connection Write/CoreDb pode atualizar os três schemas em uma transaction.

Jobs já fazem joins cross-schema em uma connection (ex.: Users + SellersVacations, Sellers + Users).


5. Caminhos paralelos (admin / store) — contraste

CaminhoServiçoUsersSellersUserOrganization
DeleteUserAsyncjobsDateDeleted + Status=2
DELETE api/userDeleteUserCommandadminuser.Delete() DateDeleted
UpdateStatusUserCommandadminUpdateStatus❌ (evento vacation se vendedor)
UpdateUserCommand Status=InactiveadminSetDateDeleted + Status✅ via Store PUT /api/seller/all-by-userIdsó se orgs mudarem no payload
UpdateSellersByUserCommandstoresoft-delete Sellers por loja/user

A descrição literal da US (DateDeleted e Status na mesma operação, sem Sellers/UO) casa com o jobs, não com admin Delete.


6. Efeito prático do gap

Após DeleteUserAsync atual:

  1. Login bloqueado (Status/DateDeleted; LogoutDeletedUserJob limpa token).
  2. Linhas em UserOrganization permanecem.
  3. Linhas em {schema}.Sellers com DateDeleted IS NULL permanecem → vendedor “fantasma” onde queries filtram só uma das dimensões.

7. Fora de escopo desta US (residual documentado)

  • Completar admin DeleteUser / UpdateStatusUser
  • Limpeza de users inativados no passado com Sellers/UO órfãos
  • Reativação de usuário
  • Projeto de testes automatizados no jobs (não existe na solution atual)