套餐查询提供状态筛选字段;实名认证回调结果提供页面

This commit is contained in:
2026-03-13 17:03:32 +08:00
parent 3dc9bc5b1d
commit efce18e6f5
7 changed files with 169 additions and 10 deletions

View File

@@ -1,5 +1,7 @@
## TODO
后端默认用户名不能是完整手机号
前端需要 token 化改造,以避免每次 basic 认证流程中 bcrypt 对比导致的性能对比
优化中间件,配置通用限速

3
go.mod
View File

@@ -11,6 +11,7 @@ require (
github.com/go-redsync/redsync/v4 v4.14.1
github.com/gofiber/contrib/otelfiber/v2 v2.2.3
github.com/gofiber/fiber/v2 v2.52.10
github.com/gofiber/template/html/v2 v2.1.3
github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.1
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
@@ -54,6 +55,8 @@ require (
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect

6
go.sum
View File

@@ -115,6 +115,12 @@ github.com/gofiber/contrib/otelfiber/v2 v2.2.3 h1:WKW1XezHFAoohGZwnvC0R8TFJcNkab
github.com/gofiber/contrib/otelfiber/v2 v2.2.3/go.mod h1:WdQ1tYbL83IYC6oBaWvKBMVGSAYvSTRuUWTcr0wK1T4=
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o=
github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=

View File

@@ -101,6 +101,18 @@ func Identify(c *fiber.Ctx) error {
// endregion
type idenResultData struct {
Success bool
Message string
}
func renderIdenResult(c *fiber.Ctx, success bool, message string) error {
return c.Render("views/iden-result", idenResultData{
Success: success,
Message: message,
})
}
// IdentifyCallbackNew 更新用户实名认证状态
func IdentifyCallbackNew(c *fiber.Ctx) error {
@@ -110,18 +122,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
Success bool `json:"success" validate:"required"`
})
if err := c.QueryParser(req); err != nil {
return core.NewBizErr("解析请求参数失败", err)
return renderIdenResult(c, false, "解析请求参数失败")
}
// 获取 token
infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes()
if err != nil {
return core.NewBizErr("实名认证状态已失效", err)
return renderIdenResult(c, false, "实名认证状态已失效,请重新发起认证")
}
info := idenInfo{}
err = json.Unmarshal(infoStr, &info)
if err != nil {
return core.NewServErr("解析实名认证信息失败", err)
if err = json.Unmarshal(infoStr, &info); err != nil {
return renderIdenResult(c, false, "解析实名认证信息失败,请重新发起认证")
}
// 获取认证结果
@@ -131,13 +142,13 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
info.Token,
))
if err != nil {
return core.NewServErr("获取实名认证结果失败", err)
return renderIdenResult(c, false, "获取实名认证结果失败,请重新发起认证")
}
if resp.Error.Code != 0 {
return core.NewServErr(fmt.Sprintf("获取实名认证结果失败: %s", resp.Error.Message))
return renderIdenResult(c, false, fmt.Sprintf("获取实名认证结果失败%s", resp.Error.Message))
}
if resp.Result.H5Result != "ok" || resp.Result.SmResult != "ok" || resp.Result.RxResult != "ok" {
return core.NewBizErr(fmt.Sprintf("实名认证失败: %s", resp.Result.Desc))
return renderIdenResult(c, false, fmt.Sprintf("实名认证未通过:%s", resp.Result.Desc))
}
// 更新用户实名认证状态
@@ -150,11 +161,11 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
q.User.IDToken.Value(info.Token),
)
if err != nil {
return core.NewServErr("更新用户实名信息失败", err)
return renderIdenResult(c, false, "保存实名认证信息失败,请联系客服处理")
}
// 返回结果页面
return c.SendString("🎉认证成功!现在可以安全关闭这个页面")
return renderIdenResult(c, true, "实名认证成功,请在扫码页面点击按钮完成认证")
}
func idenKey(id string) string {

View File

@@ -55,6 +55,19 @@ func PageResourceShort(c *fiber.Ctx) error {
if req.ExpireBefore != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(*req.ExpireBefore))
}
if req.Status != nil {
var short = q.ResourceShort.As(q.Resource.Short.Name())
switch *req.Status {
case 1:
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Gte(time.Now()))
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.GtCol(short.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
case 2:
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Lte(time.Now()))
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.LteCol(short.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
}
}
resource, err := q.Resource.Where(do).
Joins(q.Resource.Short).
@@ -95,6 +108,7 @@ type PageResourceShortReq struct {
CreateBefore *time.Time `json:"create_before"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
Status *int `json:"status"` // 0 - 全部1 - 有效2 - 过期
}
// PageResourceLong 分页查询当前用户长效套餐
@@ -137,6 +151,19 @@ func PageResourceLong(c *fiber.Ctx) error {
if req.ExpireBefore != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(*req.ExpireBefore))
}
if req.Status != nil {
var long = q.ResourceLong.As(q.Resource.Long.Name())
switch *req.Status {
case 1:
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Gte(time.Now()))
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.GtCol(long.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
case 2:
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Lte(time.Now()))
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.LteCol(long.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
}
}
resource, err := q.Resource.Where(do).
Joins(q.Resource.Long).
@@ -177,6 +204,7 @@ type PageResourceLongReq struct {
CreateBefore *time.Time `json:"create_before"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
Status *int `json:"status"` // 0 - 全部1 - 有效2 - 过期
}
// PageResourceShortByAdmin 分页查询全部短效套餐

102
web/views/iden-result.html Normal file
View File

@@ -0,0 +1,102 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>认证结果</title>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
background: #f5f5f5;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.card {
background: #fff;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
padding: 48px 40px;
max-width: 400px;
width: 100%;
text-align: center;
}
.icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
font-size: 36px;
line-height: 1;
}
.icon--success {
background: #e6f9f0;
}
.icon--fail {
background: #fff1f0;
}
.title {
font-size: 22px;
font-weight: 600;
margin-bottom: 12px;
}
.title--success {
color: #0a6640;
}
.title--fail {
color: #a8071a;
}
.message {
font-size: 15px;
color: #666;
line-height: 1.6;
}
.divider {
border: none;
border-top: 1px solid #f0f0f0;
margin: 28px 0;
}
.hint {
font-size: 13px;
color: #aaa;
}
</style>
</head>
<body>
<div class="card">
{{if .Success}}
<div class="icon icon--success"></div>
<h1 class="title title--success">认证成功</h1>
{{else}}
<div class="icon icon--fail"></div>
<h1 class="title title--fail">认证失败</h1>
{{end}}
<p class="message">{{.Message}}</p>
</div>
</body>
</html>

View File

@@ -2,8 +2,10 @@ package web
import (
"context"
"embed"
"fmt"
"log/slog"
"net/http"
_ "net/http/pprof"
"platform/web/events"
deps "platform/web/globals"
@@ -11,6 +13,7 @@ import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
"github.com/hibiken/asynq"
"golang.org/x/sync/errgroup"
)
@@ -42,11 +45,15 @@ func RunApp(pCtx context.Context) error {
return g.Wait()
}
//go:embed views/*.html
var fs embed.FS
func RunWeb(ctx context.Context) error {
fiber := fiber.New(fiber.Config{
ProxyHeader: fiber.HeaderXForwardedFor,
ErrorHandler: ErrorHandler,
Views: html.NewFileSystem(http.FS(fs), ".html"),
})
ApplyMiddlewares(fiber)