Skip to content

Commit

Permalink
feat(desktop): Add Real Name Authentication User Rewards (#5206)
Browse files Browse the repository at this point in the history
* add realname auth reward

* ok

* change account transaction struct and add realname reward

* Optimized Copywriting

* ok
  • Loading branch information
HUAHUAI23 authored Nov 13, 2024
1 parent ec8fd15 commit aacf6dd
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 36 deletions.
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

0 comments on commit aacf6dd

Please sign in to comment.