mirror of
https://github.com/certd/certd.git
synced 2026-07-05 19:37:34 +08:00
perf: 优化邀请注册流程
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { LoginService } from "./login-service.js";
|
||||
|
||||
function createLoginService() {
|
||||
const service = new LoginService();
|
||||
const calls: any = {
|
||||
register: [],
|
||||
bindInvitee: [],
|
||||
withTx: [],
|
||||
};
|
||||
const txManager = { tx: true };
|
||||
const newUser = { id: 1001, username: "new_user" };
|
||||
service.userService = {
|
||||
async register(type: string, user: any, withTx?: any) {
|
||||
calls.register.push({ type, user, withTx });
|
||||
user.id = newUser.id;
|
||||
if (withTx) {
|
||||
await withTx(txManager);
|
||||
}
|
||||
return newUser;
|
||||
},
|
||||
} as any;
|
||||
service.inviteService = {
|
||||
async bindInvitee(ctx: any, req: any) {
|
||||
calls.bindInvitee.push({ ctx, req });
|
||||
return { id: 1 };
|
||||
},
|
||||
} as any;
|
||||
return { service, calls, newUser, txManager };
|
||||
}
|
||||
|
||||
describe("LoginService.register", () => {
|
||||
it("registers user and binds invite code after user creation", async () => {
|
||||
const { service, calls, newUser } = createLoginService();
|
||||
const registerUser = { username: "alice" } as any;
|
||||
|
||||
const result = await service.register("username", registerUser, "ABC123");
|
||||
|
||||
assert.equal(result, newUser);
|
||||
assert.equal(calls.register.length, 1);
|
||||
assert.equal(calls.register[0].type, "username");
|
||||
assert.equal(calls.register[0].user, registerUser);
|
||||
assert.equal(calls.bindInvitee.length, 1);
|
||||
assert.deepEqual(calls.bindInvitee[0].ctx, {});
|
||||
assert.deepEqual(calls.bindInvitee[0].req, {
|
||||
inviteeUserId: newUser.id,
|
||||
inviteCode: "ABC123",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps registration successful when invite binding fails", async () => {
|
||||
const { service, calls, newUser } = createLoginService();
|
||||
service.inviteService = {
|
||||
async bindInvitee(ctx: any, req: any) {
|
||||
calls.bindInvitee.push({ ctx, req });
|
||||
throw new Error("invalid invite code");
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = await service.register("username", { username: "alice" } as any, "BADCODE");
|
||||
|
||||
assert.equal(result, newUser);
|
||||
assert.equal(calls.register.length, 1);
|
||||
assert.equal(calls.bindInvitee.length, 1);
|
||||
});
|
||||
|
||||
it("keeps original registration transaction callback", async () => {
|
||||
const { service, calls, txManager } = createLoginService();
|
||||
|
||||
await service.register("username", { username: "alice" } as any, "", async tx => {
|
||||
calls.withTx.push(tx);
|
||||
});
|
||||
|
||||
assert.equal(calls.withTx.length, 1);
|
||||
assert.equal(calls.withTx[0], txManager);
|
||||
assert.equal(calls.bindInvitee.length, 0);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import jwt from "jsonwebtoken";
|
||||
import { AuthException, CommonException, Need2FAException, SysPrivateSettings, SysSettingsService } from "@certd/lib-server";
|
||||
import { RoleService } from "../../sys/authority/service/role-service.js";
|
||||
import { UserEntity } from "../../sys/authority/entity/user.js";
|
||||
import { cache, utils } from "@certd/basic";
|
||||
import { cache, logger, utils } from "@certd/basic";
|
||||
import { LoginErrorException } from "@certd/lib-server";
|
||||
import { CodeService } from "../../basic/service/code-service.js";
|
||||
import { TwoFactorService } from "../../mine/service/two-factor-service.js";
|
||||
@@ -14,6 +14,7 @@ import { AddonService } from "@certd/lib-server";
|
||||
import { OauthBoundService } from "./oauth-bound-service.js";
|
||||
import { PasskeyService } from "./passkey-service.js";
|
||||
import { InviteService } from "@certd/commercial-core";
|
||||
import { EntityManager } from "typeorm";
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -108,6 +109,19 @@ export class LoginService {
|
||||
throw new LoginErrorException(errorMessage, leftTimes);
|
||||
}
|
||||
|
||||
async register(type: string, user: UserEntity, inviteCode?: string, withTx?: (tx: EntityManager) => Promise<void>) {
|
||||
const newUser = await this.userService.register(type, user, withTx);
|
||||
if (!inviteCode) {
|
||||
return newUser;
|
||||
}
|
||||
try {
|
||||
await this.inviteService.bindInvitee({}, { inviteeUserId: newUser.id, inviteCode });
|
||||
} catch (e) {
|
||||
logger.error("绑定邀请关系失败,不影响用户注册", e);
|
||||
}
|
||||
return newUser;
|
||||
}
|
||||
|
||||
async loginBySmsCode(req: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; inviteCode?: string }) {
|
||||
this.checkIsBlocked(req.mobile);
|
||||
|
||||
@@ -130,9 +144,7 @@ export class LoginService {
|
||||
mobile,
|
||||
password: "",
|
||||
} as any;
|
||||
info = await this.userService.register("mobile", registerUser, async txManager => {
|
||||
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: req.inviteCode });
|
||||
});
|
||||
info = await this.register("mobile", registerUser, req.inviteCode);
|
||||
}
|
||||
this.clearCacheOnSuccess(mobile);
|
||||
return this.onLoginSuccess(info);
|
||||
|
||||
Reference in New Issue
Block a user