swagger

移除 merchant

swagger

改造首页内容为通用后台管理系统定位

- 修改首页标题从 'LINUX DO Credit' 改为 'Modern Platform'
- 更新副标题为 '为二次开发而生'
- 更新首页描述为通用平台的特点
- 更新首页特性标签为 '开箱即用、高度可扩展、工业级基建'
- 修改展示卡片为技术栈和二次开发相关
- 更新开发者示例代码为通用的注册和 API Key 获取示例
- 更新页脚品牌名为 'Modern Platform'
- 调整页脚导航链接为通用平台相关内容

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

去除遗留

裁剪
移除 /api/v1/user/pay-key 相关代码

- 删除后端 UpdatePayKey 处理器函数和 UpdatePayKeyRequest 结构体
- 删除 User 模型中的 PayKey 字段
- 删除 User.VerifyPayKey 方法
- 删除 EncryptPayKeyFailed 错误常量
- 删除 /api/v1/user/pay-key PUT 路由
- 删除 OAuth 返回中的 IsPayKey 字段
- 删除前端 UserService.updatePayKey 方法
- 删除前端所有支付密钥 UI 和逻辑
- 更新相关的导出和注释

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

去除遗留

api 修正

系统配置

前端裁剪

后端裁剪

init
This commit is contained in:
ryan
2026-06-07 15:32:14 +08:00
parent 8a782525de
commit 48d414e197
180 changed files with 1538 additions and 27463 deletions
+21 -76
View File
@@ -18,7 +18,6 @@ package model
import (
"context"
"crypto/subtle"
"encoding/json"
"errors"
"fmt"
@@ -26,11 +25,7 @@ import (
"strconv"
"time"
"github.com/hibiken/asynq"
"github.com/linux-do/credit/internal/common"
"github.com/linux-do/credit/internal/logger"
"github.com/linux-do/credit/internal/task"
"github.com/linux-do/credit/internal/task/scheduler"
"github.com/linux-do/credit/internal/util"
"github.com/shopspring/decimal"
"gorm.io/gorm"
@@ -97,7 +92,6 @@ type User struct {
AvatarUrl string `json:"avatar_url" gorm:"size:255"`
TrustLevel TrustLevel `json:"trust_level" gorm:"index"`
PayScore int64 `json:"pay_score" gorm:"default:0;index"`
PayKey string `json:"pay_key" gorm:"size:128"`
SignKey string `json:"sign_key" gorm:"size:64;uniqueIndex;not null"`
TotalReceive decimal.Decimal `json:"total_receive" gorm:"type:numeric(20,2);default:0"`
TotalPayment decimal.Decimal `json:"total_payment" gorm:"type:numeric(20,2);default:0"`
@@ -129,21 +123,14 @@ func GetByIDs(tx *gorm.DB, ids []uint64) ([]User, error) {
return users, nil
}
// VerifyPayKey 验证用户支付密码
// 使用用户的 SignKey 解密存储的加密密码,然后与输入的明文密码比较
func (u *User) VerifyPayKey(inputPayKey string) bool {
if u.PayKey == "" {
return false
}
decrypted, err := util.Decrypt(u.SignKey, u.PayKey)
if err != nil {
return false
}
return subtle.ConstantTimeCompare([]byte(decrypted), []byte(inputPayKey)) == 1
}
func (u *User) GetUserGamificationScore(ctx context.Context) (*UserGamificationScoreResponse, error) {
if u.Username == "dev_user" {
var response UserGamificationScoreResponse
response.User.GamificationScore = 12345
return &response, nil
}
url := fmt.Sprintf("https://linux.do/u/%s.json", u.Username)
resp, err := util.Request(ctx, http.MethodGet, url, nil, nil, nil)
if err != nil {
@@ -190,6 +177,9 @@ func (u *User) UpdateFromOAuthInfo(oauthInfo *OAuthUserInfo) {
u.IsActive = oauthInfo.Active
u.TrustLevel = oauthInfo.TrustLevel
u.LastLoginAt = time.Now()
if oauthInfo.Username == "dev_user" {
u.IsAdmin = true
}
}
// CheckActive 检查用户账户是否激活,未激活则返回错误
@@ -200,69 +190,24 @@ func (u *User) CheckActive() error {
return nil
}
// EnqueueBadgeScoreTask 为用户下发积分计算任务
func (u *User) EnqueueBadgeScoreTask(ctx context.Context, delay time.Duration) error {
payload, _ := json.Marshal(map[string]interface{}{
"user_id": u.ID,
})
opts := []asynq.Option{
asynq.Queue(task.QueueWhitelistOnly),
asynq.MaxRetry(5),
asynq.TaskID(fmt.Sprintf("user_gamification_score_%d", u.ID)),
}
if delay > 0 {
opts = append(opts, asynq.ProcessIn(delay))
}
if _, err := scheduler.AsynqClient.Enqueue(asynq.NewTask(task.UpdateSingleUserGamificationScoreTask, payload), opts...); err != nil {
logger.ErrorF(ctx, "下发用户[%s]积分计算任务失败: %v", u.Username, err)
return err
}
return nil
}
// CreateWithInitialCredit 创建新用户并初始化积分、订单
func (u *User) CreateWithInitialCredit(tx *gorm.DB, oauthInfo *OAuthUserInfo) error {
ctx := tx.Statement.Context
newUserInitialCredit, err := GetDecimalByKey(ctx, ConfigKeyNewUserInitialCredit, 2)
if err != nil {
return err
}
// CreateUser 创建新用户
func (u *User) CreateUser(tx *gorm.DB, oauthInfo *OAuthUserInfo) error {
now := time.Now()
newUser := User{
ID: oauthInfo.GetID(),
Username: oauthInfo.Username,
Nickname: oauthInfo.Name,
AvatarUrl: oauthInfo.AvatarUrl,
IsActive: oauthInfo.Active,
TrustLevel: oauthInfo.TrustLevel,
SignKey: util.GenerateUniqueIDSimple(),
TotalReceive: newUserInitialCredit,
AvailableBalance: newUserInitialCredit,
LastLoginAt: now,
ID: oauthInfo.GetID(),
Username: oauthInfo.Username,
Nickname: oauthInfo.Name,
AvatarUrl: oauthInfo.AvatarUrl,
IsActive: oauthInfo.Active,
TrustLevel: oauthInfo.TrustLevel,
SignKey: util.GenerateUniqueIDSimple(),
LastLoginAt: now,
IsAdmin: oauthInfo.Username == "dev_user",
}
if err = tx.Create(&newUser).Error; err != nil {
return err
}
order := Order{
OrderName: "新用户注册奖励",
PayerUserID: 0,
PayeeUserID: newUser.ID,
Amount: newUserInitialCredit,
Status: OrderStatusSuccess,
Type: OrderTypeCommunity,
Remark: fmt.Sprintf("新用户 %s 注册赠送初始积分 %s", newUser.Username, newUserInitialCredit.String()),
TradeTime: now,
ExpiresAt: now,
}
if err = tx.Create(&order).Error; err != nil {
if err := tx.Create(&newUser).Error; err != nil {
return err
}
*u = newUser
return u.EnqueueBadgeScoreTask(ctx, 0)
return nil
}