feat: 完善对象存储上传与真实条码二维码引擎,彻底剥离保底Mock数据
这个提交包含在:
@@ -1,7 +1,9 @@
|
||||
export enum UserRoleFlag {
|
||||
NONE = 0,
|
||||
PLAYER = 1 << 0, // 1
|
||||
EXPERT = 1 << 1, // 2
|
||||
PLAYER = 1 << 0, // 1: 普通玩家/买家
|
||||
EXPERT = 1 << 1, // 2: 陪玩大神/接单人
|
||||
HOST = 1 << 2, // 4: 车队房主/局长
|
||||
ADMIN = 1 << 3, // 8: 平台管理员/运营端
|
||||
}
|
||||
|
||||
export enum WorkMode {
|
||||
@@ -24,57 +26,181 @@ export interface UserAccount {
|
||||
currentMode: WorkMode;
|
||||
}
|
||||
|
||||
export interface ExpertCertRecord {
|
||||
userId: number;
|
||||
status: CertStatus;
|
||||
realName?: string;
|
||||
idCardNo?: string;
|
||||
rejectReason?: string;
|
||||
appliedAt: Date;
|
||||
auditedAt?: Date;
|
||||
auditedBy?: number;
|
||||
}
|
||||
|
||||
export class AuthRoleManager {
|
||||
private users = new Map<number, UserAccount>();
|
||||
private certs = new Map<number, { status: CertStatus; realName?: string; idCardNo?: string }>();
|
||||
private certs = new Map<number, ExpertCertRecord>();
|
||||
private autoIncId = 1000;
|
||||
|
||||
registerUser(params: { openid: string; nickname: string }): UserAccount {
|
||||
registerUser(params: { openid: string; nickname: string; initialRoles?: number }): UserAccount {
|
||||
const id = ++this.autoIncId;
|
||||
const user: UserAccount = {
|
||||
id,
|
||||
openid: params.openid,
|
||||
nickname: params.nickname,
|
||||
roleFlags: UserRoleFlag.PLAYER,
|
||||
roleFlags: params.initialRoles ?? UserRoleFlag.PLAYER,
|
||||
currentMode: WorkMode.PLAYER,
|
||||
};
|
||||
this.users.set(id, user);
|
||||
this.certs.set(id, { status: CertStatus.NONE });
|
||||
this.certs.set(id, {
|
||||
userId: id,
|
||||
status: CertStatus.NONE,
|
||||
appliedAt: new Date(),
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
registerAdmin(params: { openid: string; nickname: string }): UserAccount {
|
||||
return this.registerUser({
|
||||
openid: params.openid,
|
||||
nickname: params.nickname,
|
||||
initialRoles: UserRoleFlag.PLAYER | UserRoleFlag.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
getUser(userId: number): UserAccount {
|
||||
const user = this.users.get(userId);
|
||||
if (!user) throw new Error('用户不存在');
|
||||
return user;
|
||||
}
|
||||
|
||||
hasRole(userId: number, role: UserRoleFlag): boolean {
|
||||
const user = this.users.get(userId);
|
||||
if (!user) return false;
|
||||
return (user.roleFlags & role) === role;
|
||||
}
|
||||
|
||||
grantRole(userId: number, role: UserRoleFlag): void {
|
||||
const user = this.getUser(userId);
|
||||
user.roleFlags |= role;
|
||||
}
|
||||
|
||||
revokeRole(userId: number, role: UserRoleFlag): void {
|
||||
const user = this.getUser(userId);
|
||||
user.roleFlags &= ~role;
|
||||
}
|
||||
|
||||
isPlayer(userId: number): boolean {
|
||||
return this.hasRole(userId, UserRoleFlag.PLAYER);
|
||||
}
|
||||
|
||||
isExpert(userId: number): boolean {
|
||||
return this.hasRole(userId, UserRoleFlag.EXPERT);
|
||||
}
|
||||
|
||||
isHost(userId: number): boolean {
|
||||
return this.hasRole(userId, UserRoleFlag.HOST);
|
||||
}
|
||||
|
||||
isAdmin(userId: number): boolean {
|
||||
return this.hasRole(userId, UserRoleFlag.ADMIN);
|
||||
}
|
||||
|
||||
getCertStatus(userId: number): CertStatus {
|
||||
return this.certs.get(userId)?.status ?? CertStatus.NONE;
|
||||
}
|
||||
|
||||
submitExpertCert(userId: number, info: { realName: string; idCardNo: string }) {
|
||||
this.certs.set(userId, {
|
||||
status: CertStatus.PENDING,
|
||||
realName: info.realName,
|
||||
idCardNo: info.idCardNo,
|
||||
});
|
||||
getCertRecord(userId: number): ExpertCertRecord | undefined {
|
||||
return this.certs.get(userId);
|
||||
}
|
||||
|
||||
approveExpertCert(userId: number) {
|
||||
submitExpertCert(userId: number, info: { realName: string; idCardNo: string }): ExpertCertRecord {
|
||||
if (!info.realName || !info.realName.trim()) {
|
||||
throw new Error('真实姓名不能为空');
|
||||
}
|
||||
if (!info.idCardNo || !info.idCardNo.trim()) {
|
||||
throw new Error('身份证号不能为空');
|
||||
}
|
||||
const record: ExpertCertRecord = {
|
||||
userId,
|
||||
status: CertStatus.PENDING,
|
||||
realName: info.realName.trim(),
|
||||
idCardNo: info.idCardNo.trim(),
|
||||
appliedAt: new Date(),
|
||||
};
|
||||
this.certs.set(userId, record);
|
||||
return record;
|
||||
}
|
||||
|
||||
approveExpertCert(userId: number, operatorAdminId?: number): ExpertCertRecord {
|
||||
if (operatorAdminId !== undefined && !this.isAdmin(operatorAdminId)) {
|
||||
throw new Error('只有平台管理员有权审核大神认证申请');
|
||||
}
|
||||
const cert = this.certs.get(userId);
|
||||
if (!cert) throw new Error('未找到认证申请');
|
||||
cert.status = CertStatus.APPROVED;
|
||||
cert.auditedAt = new Date();
|
||||
if (operatorAdminId !== undefined) {
|
||||
cert.auditedBy = operatorAdminId;
|
||||
}
|
||||
|
||||
const user = this.getUser(userId);
|
||||
user.roleFlags |= UserRoleFlag.EXPERT;
|
||||
return cert;
|
||||
}
|
||||
|
||||
switchMode(userId: number, targetMode: WorkMode) {
|
||||
rejectExpertCert(userId: number, reason: string, operatorAdminId?: number): ExpertCertRecord {
|
||||
if (operatorAdminId !== undefined && !this.isAdmin(operatorAdminId)) {
|
||||
throw new Error('只有平台管理员有权审核大神认证申请');
|
||||
}
|
||||
const cert = this.certs.get(userId);
|
||||
if (!cert) throw new Error('未找到认证申请');
|
||||
cert.status = CertStatus.REJECTED;
|
||||
cert.rejectReason = reason;
|
||||
cert.auditedAt = new Date();
|
||||
if (operatorAdminId !== undefined) {
|
||||
cert.auditedBy = operatorAdminId;
|
||||
}
|
||||
return cert;
|
||||
}
|
||||
|
||||
switchMode(userId: number, targetMode: WorkMode): void {
|
||||
const user = this.getUser(userId);
|
||||
if (targetMode === WorkMode.EXPERT && !(user.roleFlags & UserRoleFlag.EXPERT)) {
|
||||
throw new Error('尚未开通或通过大神认证,无法切换至接单模式');
|
||||
}
|
||||
user.currentMode = targetMode;
|
||||
}
|
||||
|
||||
checkCanClaimOrder(userId: number): void {
|
||||
const user = this.getUser(userId);
|
||||
if (!(user.roleFlags & UserRoleFlag.EXPERT) || this.getCertStatus(userId) !== CertStatus.APPROVED) {
|
||||
throw new Error('未认证大神无法接单');
|
||||
}
|
||||
}
|
||||
|
||||
checkCanAudit(operatorId: number): void {
|
||||
if (!this.isAdmin(operatorId)) {
|
||||
throw new Error('只有平台管理员有权审核大神认证申请');
|
||||
}
|
||||
}
|
||||
|
||||
checkCanArbitrate(operatorId: number): void {
|
||||
if (!this.isAdmin(operatorId)) {
|
||||
throw new Error('只有平台管理员有权执行争议仲裁');
|
||||
}
|
||||
}
|
||||
|
||||
checkCanAdjustPrice(operatorId: number, creatorId: number): void {
|
||||
if (operatorId !== creatorId && !this.isAdmin(operatorId)) {
|
||||
throw new Error('只有发起人有权调整活动出价');
|
||||
}
|
||||
}
|
||||
|
||||
checkCanDissolveActivity(operatorId: number, creatorId: number): void {
|
||||
if (operatorId !== creatorId && !this.isAdmin(operatorId)) {
|
||||
throw new Error('只有发起人有权解散此组局');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户