From de395be400043370780d1eabe0d4e21e3edce97f Mon Sep 17 00:00:00 2001 From: small-lovely-cat <77799160+small-lovely-cat@users.noreply.github.com> Date: Sun, 4 Oct 2026 03:28:49 +0000 Subject: [PATCH 1/4] feat(redenvelope): add risk control for red envelope claim and add QR Code Sharing --- .../common/redenvelope/red-envelope-card.tsx | 2 +- .../common/redenvelope/red-envelope-share.tsx | 70 +++++++++++ .../components/common/trade/red-envelope.tsx | 24 +++- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 12 ++ frontend/public/red-envelope-scan.svg | 54 ++++++++ internal/apps/dashboard/routers.go | 4 +- internal/apps/oauth/risk.go | 6 +- internal/apps/redenvelope/risk.go | 116 ++++++++++++++++++ internal/apps/redenvelope/routers.go | 30 ++++- internal/common/errs.go | 1 + internal/db/migrator/migrator.go | 10 ++ internal/db/redis.go | 39 +++++- internal/model/system_configs.go | 32 ++--- 14 files changed, 370 insertions(+), 31 deletions(-) create mode 100644 frontend/components/common/redenvelope/red-envelope-share.tsx create mode 100644 frontend/public/red-envelope-scan.svg create mode 100644 internal/apps/redenvelope/risk.go diff --git a/frontend/components/common/redenvelope/red-envelope-card.tsx b/frontend/components/common/redenvelope/red-envelope-card.tsx index b3903e49..eb1706dc 100644 --- a/frontend/components/common/redenvelope/red-envelope-card.tsx +++ b/frontend/components/common/redenvelope/red-envelope-card.tsx @@ -118,7 +118,7 @@ export function RedEnvelopeCard({ animate={status === "opening" ? { scale: [1, 0.92, 0], opacity: [1, 0] - } : {}} + } : { scale: 1, opacity: 1 }} transition={status === "opening" ? { duration: 0.55, ease: "easeInOut" diff --git a/frontend/components/common/redenvelope/red-envelope-share.tsx b/frontend/components/common/redenvelope/red-envelope-share.tsx new file mode 100644 index 00000000..b4d87437 --- /dev/null +++ b/frontend/components/common/redenvelope/red-envelope-share.tsx @@ -0,0 +1,70 @@ +"use client" + +import { useState } from "react" +import Image from "next/image" +import { motion, useReducedMotion } from "motion/react" +import { QRCodeSVG } from "qrcode.react" + +export function RedEnvelopeShare({ link }: { link: string }) { + const [playing, setPlaying] = useState(false) + const [replay, setReplay] = useState(0) + const reduceMotion = useReducedMotion() + + return ( + <> +
+
+ +
+
+
{ + if (event.pointerType === "mouse") setPlaying(true) + }} + onPointerLeave={(event) => { + if (event.pointerType === "mouse") setPlaying(false) + }} + onFocus={() => setPlaying(true)} + onBlur={(event) => { + if (!event.currentTarget.contains(event.relatedTarget)) setPlaying(false) + }} + > + { + setPlaying(true) + setReplay(value => value + 1) + }} + > + + + + + {playing && ( + + )} + +
+ + ) +} diff --git a/frontend/components/common/trade/red-envelope.tsx b/frontend/components/common/trade/red-envelope.tsx index f7527297..875767f3 100644 --- a/frontend/components/common/trade/red-envelope.tsx +++ b/frontend/components/common/trade/red-envelope.tsx @@ -4,7 +4,7 @@ import * as React from "react" import { useState, useEffect } from "react" import Image from "next/image" import { toast } from "sonner" -import { Gift, Copy, Check, ExternalLink, Pencil, X, ImagePlus, History } from "lucide-react" +import { Gift, Copy, Check, Share2, Pencil, X, ImagePlus, History } from "lucide-react" import type { UploadImageResponse } from "@/lib/services" import { getFileUrl } from "@/lib/services/upload/upload.service" import { Input } from "@/components/ui/input" @@ -18,6 +18,7 @@ import { ListData } from "@/components/common/general/list-data" import { ImageCropper } from "@/components/common/redenvelope/image-cropper" import { motion } from "motion/react" import { RedEnvelopeCard } from "@/components/common/redenvelope/red-envelope-card" +import { RedEnvelopeShare } from "@/components/common/redenvelope/red-envelope-share" import { useUser } from "@/contexts/user-context" import services from "@/lib/services" import type { RedEnvelopeType, CreateRedEnvelopeRequest, PublicConfigResponse, RedEnvelope, RedEnvelopeListResponse } from "@/lib/services" @@ -696,7 +697,7 @@ export function RedEnvelope({ onSuccess }: { onSuccess?: () => void }) { -
+
void }) {
+ {isResultOpen && resultLink && } +