This commit is contained in:
2026-06-30 10:33:56 +08:00
commit 6e047dc0a5
607 changed files with 65966 additions and 0 deletions
@@ -0,0 +1,145 @@
import {
BadRequestException,
Inject,
Injectable,
NotImplementedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ClientApp, SmsScene } from '@dukang/shared-types';
import { generateUserNo } from '@dukang/domain';
import { PrismaService } from '../../common/prisma/prisma.module';
import { SMS_PROVIDER } from '../../integrations/integrations.constants';
import { ISmsProvider } from '../../integrations/sms/sms.interface';
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
@Injectable()
export class AuthService {
constructor(
private readonly prisma: PrismaService,
private readonly jwtService: JwtService,
@Inject(SMS_PROVIDER) private readonly smsProvider: ISmsProvider,
) {}
async sendSms(phone: string, scene: string) {
await this.smsProvider.send(phone, scene);
return { sent: true };
}
async loginUser(phone: string, code: string, clientApp: ClientApp) {
await this.smsProvider.verify(phone, code, SmsScene.USER_LOGIN);
let user = await this.prisma.user.findUnique({ where: { phone } });
if (!user) {
user = await this.prisma.user.create({
data: {
phone,
userNo: generateUserNo(),
nickname: `用户${phone.slice(-4)}`,
},
});
await this.prisma.userCityPreference.create({
data: { userId: user.id, selectedCityCode: '410100', selectedDistrict: '郑州市' },
});
}
return this.issueToken('USER', user.id, clientApp, {
id: user.id.toString(),
userNo: user.userNo,
phone: user.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
nickname: user.nickname,
hasWechat: !!user.wxOpenId,
});
}
async loginStore(phone: string, code: string, clientApp: ClientApp) {
await this.smsProvider.verify(phone, code, SmsScene.STORE_LOGIN);
const account = await this.prisma.storeAccount.findUnique({
where: { phone },
include: { store: true },
});
if (!account) throw new BadRequestException('门店账号不存在');
await this.prisma.storeAccount.update({
where: { id: account.id },
data: { lastLoginAt: new Date() },
});
return this.issueToken('STORE', account.id, clientApp, undefined, {
id: account.id.toString(),
storeId: account.storeId.toString(),
name: account.name,
phone: account.phone,
storeName: account.store.name,
});
}
async loginPartner(phone: string, code: string, clientApp: ClientApp) {
await this.smsProvider.verify(phone, code, SmsScene.PARTNER_LOGIN);
const account = await this.prisma.partnerAccount.findUnique({
where: { phone },
include: { partner: true },
});
if (!account) throw new BadRequestException('合伙人账号不存在');
await this.prisma.partnerAccount.update({
where: { id: account.id },
data: { lastLoginAt: new Date() },
});
return this.issueToken('PARTNER', account.id, clientApp, undefined, undefined, {
id: account.id.toString(),
partnerId: account.partnerId.toString(),
name: account.name,
phone: account.phone,
isPrimary: account.isPrimary === 1,
companyName: account.partner.companyName,
});
}
async getMe(actorType: string, actorId: bigint) {
if (actorType === 'USER') {
const user = await this.prisma.user.findUnique({ where: { id: actorId } });
return serializeBigInt(user);
}
if (actorType === 'STORE') {
const account = await this.prisma.storeAccount.findUnique({
where: { id: actorId },
include: { store: true },
});
return serializeBigInt(account);
}
if (actorType === 'PARTNER') {
const account = await this.prisma.partnerAccount.findUnique({
where: { id: actorId },
include: { partner: true },
});
return serializeBigInt(account);
}
return null;
}
wechatDisabled() {
throw new NotImplementedException('FEATURE_DISABLED');
}
private issueToken(
actorType: string,
actorId: bigint,
clientApp: ClientApp,
user?: Record<string, unknown>,
store?: Record<string, unknown>,
partner?: Record<string, unknown>,
) {
const payload = {
sub: actorId.toString(),
actorType,
actorId: actorId.toString(),
clientApp,
};
const accessToken = this.jwtService.sign(payload);
const refreshToken = this.jwtService.sign(payload, { expiresIn: '30d' });
return {
accessToken,
refreshToken,
actorType,
actorId: actorId.toString(),
user,
store,
partner,
};
}
}