Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(desktop): Add Real Name Authentication User Rewards #5206

Merged
merged 6 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "AccountTransaction" ADD COLUMN "balance_before" INT8;
ALTER TABLE "AccountTransaction" ADD COLUMN "deduction_balance_before" INT8;
42 changes: 22 additions & 20 deletions frontend/desktop/prisma/global/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ model Account {
}

model AccountTransaction {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
type String
userUid String @db.Uuid
deduction_balance BigInt
balance BigInt
message String?
created_at DateTime @default(now()) @db.Timestamptz(3)
updated_at DateTime @default(now()) @db.Timestamptz(3)
billing_id String @db.Uuid
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
type String
userUid String @db.Uuid
deduction_balance BigInt
deduction_balance_before BigInt?
balance BigInt
balance_before BigInt?
message String?
created_at DateTime @default(now()) @db.Timestamptz(3)
updated_at DateTime @default(now()) @db.Timestamptz(3)
billing_id String @db.Uuid
}

model ErrorPaymentCreate {
Expand Down Expand Up @@ -349,24 +351,24 @@ model Task {
order Int
isActive Boolean @default(true)
isNewUserTask Boolean @default(false)
taskType TaskType
taskType TaskType
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
userTasks UserTask[]
}

model UserTask {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
userUid String @db.Uuid
taskId String @db.Uuid
status TaskStatus
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
userUid String @db.Uuid
taskId String @db.Uuid
status TaskStatus
rewardStatus TaskStatus
completedAt DateTime
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
completedAt DateTime
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)

user User @relation(fields: [userUid], references: [uid])
task Task @relation(fields: [taskId], references: [id])
user User @relation(fields: [userUid], references: [uid])
task Task @relation(fields: [taskId], references: [id])

@@unique([userUid, taskId])
@@index([taskId])
Expand All @@ -383,4 +385,4 @@ enum TaskType {
enum TaskStatus {
NOT_COMPLETED
COMPLETED
}
}
4 changes: 2 additions & 2 deletions frontend/desktop/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
"realName_verification": "Real Name Verification",
"realname_auth_now": "Click to verify your name",
"realname_auth_reminder": "Real-name authentication reminder",
"realname_auth_reminder_desc": "Domestic availability zones require real-name authentication, and use without real-name authentication will be restricted.",
"realname_auth_reminder_desc": "Real-name verification is required for regions in China. Without real-name verification, top-up will be restricted. Successful real-name verification will be rewarded with a {{reward}} Sealos balance.",
"realname_auth_tips_a": "1. Please ensure that the name, ID number and mobile phone number filled in are consistent.",
"realname_auth_tips_b": "2. The number segments provided by some virtual operators may not pass verification. Please apply for a work order and pass manual verification.",
"realname_info": "RealName",
Expand Down Expand Up @@ -267,4 +267,4 @@
"you_can_view_fees_through_the_fee_center": "You can view fees through the fee center",
"you_have_not_purchased_the_license": "You have not purchased the License",
"yuan": "Yuan"
}
}
4 changes: 2 additions & 2 deletions frontend/desktop/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
"realName_verification": "实名认证",
"realname_auth_now": "点击进行实名",
"realname_auth_reminder": "实名认证提醒",
"realname_auth_reminder_desc": "国内可用区需要实名认证,未实名认证将会被限制使用,",
"realname_auth_reminder_desc": "国内可用区需要实名认证,未实名认证将会被限制充值,实名认证成功奖励 Sealos 余额 {{reward}} 元。",
"realname_auth_tips_a": "1、请确保所填写的姓名、身份证号码和手机号码信息一致。",
"realname_auth_tips_b": "2、部分虚拟运营商提供的号段可能无法验证通过,请申请工单通过人工协助认证。",
"realname_info": "实名信息",
Expand Down Expand Up @@ -260,4 +260,4 @@
"you_can_view_fees_through_the_fee_center": "您可通过费用中心查看费用",
"you_have_not_purchased_the_license": "您还没有购买 License",
"yuan": "元"
}
}
9 changes: 8 additions & 1 deletion frontend/desktop/src/components/account/RealNameModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import { DeleteIcon, PictureIcon, UploadIcon, AttachmentIcon } from '../icons';

export function useRealNameAuthNotification(props?: UseToastOptions) {
const { t } = useTranslation();
const { commonConfig } = useConfigStore((s) => s);
const realNameReward = commonConfig?.realNameReward;

const realNameAuthNotification = useToast({
position: 'top',
Expand Down Expand Up @@ -108,7 +110,9 @@ export function useRealNameAuthNotification(props?: UseToastOptions) {
lineHeight="20px"
letterSpacing="0.25px"
>
{t('common:realname_auth_reminder_desc')}
{t('common:realname_auth_reminder_desc', {
reward: realNameReward
})}
<RealNameModal onFormSuccess={props.onClose}>
<Text
as="span"
Expand Down Expand Up @@ -364,7 +368,10 @@ export function FaceIdRealNameAuthORcode(
realName: result.data?.realName
});

// refetch user info
queryClient.invalidateQueries([session?.token, 'UserInfo']);
// refetch user amount
queryClient.invalidateQueries(['getAmount']);

stopPolling();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type TencentCloudFaceAuthConfig = {
secretId: string;
secretKey: string;
ruleId: string;
realNameAuthReward?: number;
};

type JsonValue = string | number | boolean | object | null;
Expand Down Expand Up @@ -88,6 +89,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
throw new Error('faceidRealNameAuth: Real name authentication configuration not found');
}

const realNameAuthReward = config.realNameAuthReward;

const userRealNameFaceAuthInfo = await getUserRealNameInfo(bizToken, config);
const isFaceRecognitionSuccess = userRealNameFaceAuthInfo.Text?.ErrCode === 0;

Expand Down Expand Up @@ -145,15 +148,70 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}

if (isFaceRecognitionSuccess) {
await globalPrisma.userRealNameInfo.update({
where: { userUid },
data: {
realName: userRealNameFaceAuthInfo.Text?.Name,
idCard: userRealNameFaceAuthInfo.Text?.IdCard,
isVerified: true,
additionalInfo
}
});
if (realNameAuthReward) {
await globalPrisma.$transaction(async (globalPrisma) => {
const currentAccount = await globalPrisma.account.findUniqueOrThrow({
where: { userUid }
});

if (!currentAccount.balance) {
throw new Error('faceidRealNameAuth: Account balance not found');
}

const currentActivityBonus = currentAccount.activityBonus || BigInt(0);
const rewardBigInt = BigInt(realNameAuthReward);

const newActivityBonus = currentActivityBonus + rewardBigInt;
const newBalance = currentAccount.balance + rewardBigInt;

const updatedAccount = await globalPrisma.account.update({
where: { userUid },
data: {
activityBonus: newActivityBonus,
balance: newBalance
}
});

const userRealNameInfo = await globalPrisma.userRealNameInfo.update({
where: { userUid },
data: {
realName: userRealNameFaceAuthInfo.Text?.Name,
idCard: userRealNameFaceAuthInfo.Text?.IdCard,
isVerified: true,
additionalInfo: additionalInfo
}
});

const accountTransaction = await globalPrisma.accountTransaction.create({
data: {
type: 'REALNAME_AUTH_REWARD',
userUid: userUid,
balance: rewardBigInt,
balance_before: currentAccount.balance,
deduction_balance: 0, // No deduction in this case
deduction_balance_before: currentAccount.deduction_balance,
message: 'Real name authentication reward',
billing_id: userRealNameInfo.id // You'll need to implement this function
}
});

return {
account: updatedAccount,
transaction: accountTransaction,
userRealNameInfo: userRealNameInfo
};
});
} else {
await globalPrisma.userRealNameInfo.update({
where: { userUid },
data: {
realName: userRealNameFaceAuthInfo.Text?.Name,
idCard: userRealNameFaceAuthInfo.Text?.IdCard,
isVerified: true,
additionalInfo: additionalInfo
}
});
}

res.setHeader('Content-Type', 'text/html');
return res.send(`
Expand Down Expand Up @@ -187,7 +245,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: {
isVerified: false,
idVerifyFailedTimes: { increment: 1 },
additionalInfo
additionalInfo: additionalInfo
}
});

Expand Down Expand Up @@ -221,7 +279,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
} catch (error) {
console.error('faceidRealNameAuth: Internal error');
console.error(error);
if (error instanceof Error) {
console.error('Error message:', error.message);
} else {
console.error('Unknown error:', error);
}
res.setHeader('Content-Type', 'text/html');
return res.status(500).send(`
<!DOCTYPE html>
Expand Down
1 change: 1 addition & 0 deletions frontend/desktop/src/pages/api/platform/getCommonConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function genResCommonClientConfig(common: CommonConfigType): CommonClientConfigT
return {
enterpriseRealNameAuthEnabled: !!common.enterpriseRealNameAuthEnabled,
realNameAuthEnabled: !!common.realNameAuthEnabled,
realNameReward: common.realNameReward || 0,
guideEnabled: !!common.guideEnabled,
rechargeEnabled: !!common.rechargeEnabled,
cfSiteKey: common.cfSiteKey || '',
Expand Down
2 changes: 2 additions & 0 deletions frontend/desktop/src/types/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type CommonConfigType = {
enterpriseRealNameAuthEnabled: boolean;
enterpriseSupportingMaterials: string;
realNameAuthEnabled: boolean;
realNameReward: number;
guideEnabled: boolean;
apiEnabled: boolean;
rechargeEnabled: boolean;
Expand Down Expand Up @@ -206,6 +207,7 @@ export const DefaultCommonClientConfig: CommonClientConfigType = {
enterpriseRealNameAuthEnabled: false,
enterpriseSupportingMaterials: '',
realNameAuthEnabled: false,
realNameReward: 0,
guideEnabled: false,
rechargeEnabled: false,
cfSiteKey: ''
Expand Down
Loading