perf: 优化邀请注册流程

This commit is contained in:
xiaojunnuo
2026-06-04 18:22:21 +08:00
parent fdb1d1e6dd
commit 7a71e45799
11 changed files with 115 additions and 30 deletions
@@ -107,6 +107,9 @@ function install(app: App, options: any = {}) {
scroll: {
x: 960,
},
rowSelection: {
fixed: "left",
},
size: "small",
pagination: false,
onResizeColumn: (w: number, col: any) => {
@@ -389,7 +389,7 @@ export const certdResources = [
meta: {
show: () => {
const settingStore = useSettingStore();
return settingStore.isComm;
return settingStore.isInviteCommissionEnabled;
},
icon: "ion:gift-outline",
auth: true,
@@ -136,12 +136,12 @@ export const useUserStore = defineStore({
} catch (e) {
console.error("注销登录请求失败:", e);
}
// 第三方登录注销
this.oauthLogout();
}
this.resetState();
resetAllStores();
// 第三方登录注销
await this.oauthLogout();
goLogin && router.push("/login");
mitter.emit("app.logout");
},
@@ -300,7 +300,7 @@ async function handleTabChange() {
}
async function refreshInvitePage(autoOpenAgreement = true) {
await settingStore.initOnce();
await settingStore.init();
enabled.value = settingStore.isInviteCommissionEnabled;
loaded.value = true;
if (!enabled.value) {
@@ -26,13 +26,14 @@ export async function OauthToken(type: string, validationCode: string) {
});
}
export async function AutoRegister(type: string, code: string) {
export async function AutoRegister(type: string, code: string, inviteCode?: string) {
return await request({
url: apiPrefix + `/autoRegister`,
method: "post",
data: {
validationCode: code,
type,
inviteCode,
},
});
}
@@ -29,6 +29,7 @@ import { useRoute, useRouter } from "vue-router";
import { useUserStore } from "/@/store/user";
import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
import { inviteUtils } from "/@/utils/util.invite";
const route = useRoute();
const router = useRouter();
@@ -99,7 +100,7 @@ async function goBindUser() {
async function autoRegister() {
//自动注册账号
const res = await api.AutoRegister(oauthType, bindCode.value);
const res = await api.AutoRegister(oauthType, bindCode.value, inviteUtils.get());
//登录成功
userStore.onLoginSuccess(res);
//跳转到首页
@@ -215,6 +215,8 @@ export default defineComponent({
const handleFinish = async (values: any) => {
try {
//先注销登录
userStore.resetState();
await userStore.register(
toRaw({
type: registerType.value,
@@ -8,7 +8,6 @@ import { LoginService } from "../../../modules/login/service/login-service.js";
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
type OauthProviderSetting = {
@@ -42,8 +41,6 @@ export class ConnectController extends BaseController {
loginService: LoginService;
@Inject()
codeService: CodeService;
@Inject()
userService: UserService;
@Inject()
oauthBoundService: OauthBoundService;
@@ -199,7 +196,7 @@ export class ConnectController extends BaseController {
}
@Post("/autoRegister", { description: Constants.per.guest })
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string }) {
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string; inviteCode?: string }) {
const validationValue = this.codeService.getValidationValue(body.validationCode);
if (!validationValue) {
throw new Error("第三方认证授权已过期");
@@ -212,7 +209,7 @@ export class ConnectController extends BaseController {
newUser.nickName = userInfo.nickName || simpleNanoId(6);
newUser.email = userInfo.email || "";
newUser = await this.userService.register("username", newUser, async txManager => {
newUser = await this.loginService.register("username", newUser, body.inviteCode, async txManager => {
const oauthBound: OauthBoundEntity = new OauthBoundEntity();
oauthBound.userId = newUser.id;
oauthBound.type = oauthType;
@@ -1,9 +1,9 @@
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { BaseController, Constants, SysSettingsService } from "@certd/lib-server";
import { RegisterType, UserService } from "../../../modules/sys/authority/service/user-service.js";
import { RegisterType } from "../../../modules/sys/authority/service/user-service.js";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { checkComm, checkPlus } from "@certd/plus-core";
import { InviteService } from "@certd/commercial-core";
import { LoginService } from "../../../modules/login/service/login-service.js";
export type RegisterReq = {
type: RegisterType;
@@ -24,16 +24,13 @@ export type RegisterReq = {
@Controller("/api/")
export class RegisterController extends BaseController {
@Inject()
userService: UserService;
loginService: LoginService;
@Inject()
codeService: CodeService;
@Inject()
sysSettingsService: SysSettingsService;
@Inject()
inviteService: InviteService;
@Post("/register", { description: Constants.per.guest })
public async register(
@Body(ALL)
@@ -62,9 +59,7 @@ export class RegisterController extends BaseController {
username: body.username,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
} else if (body.type === "mobile") {
if (sysPublicSettings.mobileRegisterEnabled === false) {
@@ -84,9 +79,7 @@ export class RegisterController extends BaseController {
mobile: body.mobile,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
} else if (body.type === "email") {
if (sysPublicSettings.emailRegisterEnabled === false) {
@@ -103,9 +96,7 @@ export class RegisterController extends BaseController {
email: body.email,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
}
}
@@ -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);