门店账号管理修改

This commit is contained in:
2026-07-07 00:01:16 +08:00
parent e85c4b9bcb
commit 7ca9fc8a35
25 changed files with 599 additions and 134 deletions
@@ -80,6 +80,8 @@ export class AuthService {
switch (scene) {
case SmsScene.STORE_LOGIN:
return ClientApp.SHOP_H5;
case SmsScene.STORE_ACCOUNT_OPEN:
return ClientApp.HQ_WEB;
case SmsScene.PARTNER_LOGIN:
case SmsScene.PARTNER_STAFF_ADD:
return ClientApp.PARTNER_H5;
@@ -148,6 +150,24 @@ export class AuthService {
});
}
private async assertSmsSendAllowed(phone: string, scene: SmsScene) {
if (scene === SmsScene.STORE_LOGIN) {
const account = await this.prisma.storeAccount.findUnique({ where: { phone } });
if (!account) throw new BadRequestException('该手机号未绑定门店');
if (account.status !== 'ACTIVE') throw new BadRequestException('门店账号已停用');
return;
}
if (scene === SmsScene.STORE_ACCOUNT_OPEN) {
const existing = await this.prisma.storeAccount.findUnique({ where: { phone } });
if (existing) throw new BadRequestException('该手机号已绑定门店');
}
}
async verifySmsCode(phone: string, code: string, scene: SmsScene) {
const normalizedPhone = this.assertMobilePhone(phone);
await this.smsProvider.verify(normalizedPhone, code, scene);
}
private async verifySmsForUser(
phone: string,
code: string,
@@ -178,6 +198,7 @@ export class AuthService {
if (!Object.values(SmsScene).includes(scene as SmsScene)) {
throw new BadRequestException('无效的验证码场景');
}
await this.assertSmsSendAllowed(normalizedPhone, scene as SmsScene);
const clientApp = opts?.clientApp ?? this.clientAppForScene(scene);
const actorRef = await this.resolveSmsActorRef(normalizedPhone, scene, opts?.guestUserId);
const userId = actorRef?.refType === 'USER' ? actorRef.refId : opts?.guestUserId;
@@ -254,17 +275,40 @@ export class AuthService {
async refreshAccessToken(refreshToken: string, clientApp: ClientApp) {
try {
const payload = this.jwtService.verify(refreshToken);
if (payload.clientApp !== clientApp || payload.actorType !== 'USER') {
if (payload.clientApp !== clientApp) {
throw new UnauthorizedException('Invalid refresh token');
}
const user = await this.assertActiveUser(BigInt(payload.actorId));
return this.buildSessionResponse(user, clientApp, user.deviceKey);
if (payload.actorType === 'USER') {
const user = await this.assertActiveUser(BigInt(payload.actorId));
return this.buildSessionResponse(user, clientApp, user.deviceKey);
}
if (payload.actorType === 'STORE' && clientApp === ClientApp.SHOP_H5) {
return this.buildStoreSessionResponse(BigInt(payload.actorId), clientApp);
}
throw new UnauthorizedException('Invalid refresh token');
} catch (err) {
if (err instanceof UnauthorizedException) throw err;
throw new UnauthorizedException('Invalid refresh token');
}
}
private async buildStoreSessionResponse(accountId: bigint, clientApp: ClientApp) {
const account = await this.prisma.storeAccount.findUnique({
where: { id: accountId },
include: { store: true },
});
if (!account || account.status !== 'ACTIVE') {
throw new UnauthorizedException('Invalid refresh token');
}
return this.issueToken('STORE', account.id, clientApp, false, undefined, {
id: account.id.toString(),
storeId: account.storeId.toString(),
name: account.name,
phone: account.phone,
storeName: account.store.name,
});
}
async loginUser(phone: string, code: string, clientApp: ClientApp, guestId?: bigint) {
const normalizedPhone = this.assertMobilePhone(phone);
const existingUser = await this.prisma.user.findUnique({
@@ -396,7 +440,8 @@ export class AuthService {
where: { phone: normalizedPhone },
include: { store: true },
});
if (!account) throw new BadRequestException('门店账号不存在');
if (!account) throw new BadRequestException('该手机号未绑定门店');
if (account.status !== 'ACTIVE') throw new BadRequestException('门店账号已停用');
await this.prisma.storeAccount.update({
where: { id: account.id },
data: { lastLoginAt: new Date() },