套餐查询提供状态筛选字段;实名认证回调结果提供页面
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
后端默认用户名不能是完整手机号
|
||||||
|
|
||||||
前端需要 token 化改造,以避免每次 basic 认证流程中 bcrypt 对比导致的性能对比
|
前端需要 token 化改造,以避免每次 basic 认证流程中 bcrypt 对比导致的性能对比
|
||||||
|
|
||||||
优化中间件,配置通用限速
|
优化中间件,配置通用限速
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -11,6 +11,7 @@ require (
|
|||||||
github.com/go-redsync/redsync/v4 v4.14.1
|
github.com/go-redsync/redsync/v4 v4.14.1
|
||||||
github.com/gofiber/contrib/otelfiber/v2 v2.2.3
|
github.com/gofiber/contrib/otelfiber/v2 v2.2.3
|
||||||
github.com/gofiber/fiber/v2 v2.52.10
|
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/google/uuid v1.6.0
|
||||||
github.com/hibiken/asynq v0.25.1
|
github.com/hibiken/asynq v0.25.1
|
||||||
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
|
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/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.9.3 // 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/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -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/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 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
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 h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
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=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
|
|||||||
@@ -101,6 +101,18 @@ func Identify(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// endregion
|
// 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 更新用户实名认证状态
|
// IdentifyCallbackNew 更新用户实名认证状态
|
||||||
func IdentifyCallbackNew(c *fiber.Ctx) error {
|
func IdentifyCallbackNew(c *fiber.Ctx) error {
|
||||||
|
|
||||||
@@ -110,18 +122,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
|
|||||||
Success bool `json:"success" validate:"required"`
|
Success bool `json:"success" validate:"required"`
|
||||||
})
|
})
|
||||||
if err := c.QueryParser(req); err != nil {
|
if err := c.QueryParser(req); err != nil {
|
||||||
return core.NewBizErr("解析请求参数失败", err)
|
return renderIdenResult(c, false, "解析请求参数失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 token
|
// 获取 token
|
||||||
infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes()
|
infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewBizErr("实名认证状态已失效", err)
|
return renderIdenResult(c, false, "实名认证状态已失效,请重新发起认证")
|
||||||
}
|
}
|
||||||
info := idenInfo{}
|
info := idenInfo{}
|
||||||
err = json.Unmarshal(infoStr, &info)
|
if err = json.Unmarshal(infoStr, &info); err != nil {
|
||||||
if err != nil {
|
return renderIdenResult(c, false, "解析实名认证信息失败,请重新发起认证")
|
||||||
return core.NewServErr("解析实名认证信息失败", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取认证结果
|
// 获取认证结果
|
||||||
@@ -131,13 +142,13 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
|
|||||||
info.Token,
|
info.Token,
|
||||||
))
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("获取实名认证结果失败", err)
|
return renderIdenResult(c, false, "获取实名认证结果失败,请重新发起认证")
|
||||||
}
|
}
|
||||||
if resp.Error.Code != 0 {
|
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" {
|
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),
|
q.User.IDToken.Value(info.Token),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新用户实名信息失败", err)
|
return renderIdenResult(c, false, "保存实名认证信息失败,请联系客服处理")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回结果页面
|
// 返回结果页面
|
||||||
return c.SendString("🎉认证成功!现在可以安全关闭这个页面")
|
return renderIdenResult(c, true, "实名认证成功,请在扫码页面点击按钮完成认证")
|
||||||
}
|
}
|
||||||
|
|
||||||
func idenKey(id string) string {
|
func idenKey(id string) string {
|
||||||
|
|||||||
@@ -55,6 +55,19 @@ func PageResourceShort(c *fiber.Ctx) error {
|
|||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
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).
|
resource, err := q.Resource.Where(do).
|
||||||
Joins(q.Resource.Short).
|
Joins(q.Resource.Short).
|
||||||
@@ -95,6 +108,7 @@ type PageResourceShortReq struct {
|
|||||||
CreateBefore *time.Time `json:"create_before"`
|
CreateBefore *time.Time `json:"create_before"`
|
||||||
ExpireAfter *time.Time `json:"expire_after"`
|
ExpireAfter *time.Time `json:"expire_after"`
|
||||||
ExpireBefore *time.Time `json:"expire_before"`
|
ExpireBefore *time.Time `json:"expire_before"`
|
||||||
|
Status *int `json:"status"` // 0 - 全部,1 - 有效,2 - 过期
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageResourceLong 分页查询当前用户长效套餐
|
// PageResourceLong 分页查询当前用户长效套餐
|
||||||
@@ -137,6 +151,19 @@ func PageResourceLong(c *fiber.Ctx) error {
|
|||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
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).
|
resource, err := q.Resource.Where(do).
|
||||||
Joins(q.Resource.Long).
|
Joins(q.Resource.Long).
|
||||||
@@ -177,6 +204,7 @@ type PageResourceLongReq struct {
|
|||||||
CreateBefore *time.Time `json:"create_before"`
|
CreateBefore *time.Time `json:"create_before"`
|
||||||
ExpireAfter *time.Time `json:"expire_after"`
|
ExpireAfter *time.Time `json:"expire_after"`
|
||||||
ExpireBefore *time.Time `json:"expire_before"`
|
ExpireBefore *time.Time `json:"expire_before"`
|
||||||
|
Status *int `json:"status"` // 0 - 全部,1 - 有效,2 - 过期
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageResourceShortByAdmin 分页查询全部短效套餐
|
// PageResourceShortByAdmin 分页查询全部短效套餐
|
||||||
|
|||||||
102
web/views/iden-result.html
Normal file
102
web/views/iden-result.html
Normal 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>
|
||||||
@@ -2,8 +2,10 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
"platform/web/events"
|
"platform/web/events"
|
||||||
deps "platform/web/globals"
|
deps "platform/web/globals"
|
||||||
@@ -11,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/template/html/v2"
|
||||||
"github.com/hibiken/asynq"
|
"github.com/hibiken/asynq"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
@@ -42,11 +45,15 @@ func RunApp(pCtx context.Context) error {
|
|||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:embed views/*.html
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
func RunWeb(ctx context.Context) error {
|
func RunWeb(ctx context.Context) error {
|
||||||
|
|
||||||
fiber := fiber.New(fiber.Config{
|
fiber := fiber.New(fiber.Config{
|
||||||
ProxyHeader: fiber.HeaderXForwardedFor,
|
ProxyHeader: fiber.HeaderXForwardedFor,
|
||||||
ErrorHandler: ErrorHandler,
|
ErrorHandler: ErrorHandler,
|
||||||
|
Views: html.NewFileSystem(http.FS(fs), ".html"),
|
||||||
})
|
})
|
||||||
|
|
||||||
ApplyMiddlewares(fiber)
|
ApplyMiddlewares(fiber)
|
||||||
|
|||||||
Reference in New Issue
Block a user