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
+22 -5
View File
@@ -31,6 +31,7 @@ import (
type CreateSystemConfigRequest struct {
Key string `json:"key" binding:"required,max=64"`
Value string `json:"value" binding:"required,max=255"`
Type string `json:"type" binding:"required,oneof=system business"`
Description string `json:"description" binding:"max=255"`
}
@@ -41,10 +42,12 @@ type UpdateSystemConfigRequest struct {
}
// CreateSystemConfig 创建系统配置
// @Summary 创建系统配置
// @Description 创建一条新的系统配置项,配置键不可重复,同时将新配置同步到 Redis,需要管理员权限
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreateSystemConfigRequest true "request body"
// @Param request body CreateSystemConfigRequest true "创建请求参数"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/system-configs [post]
func CreateSystemConfig(c *gin.Context) {
@@ -67,6 +70,7 @@ func CreateSystemConfig(c *gin.Context) {
config := model.SystemConfig{
Key: req.Key,
Value: req.Value,
Type: req.Type,
Description: req.Description,
}
@@ -90,15 +94,22 @@ func CreateSystemConfig(c *gin.Context) {
}
// ListSystemConfigs 获取系统配置列表
// @Summary 获取系统配置列表
// @Description 返回所有系统配置列表,支持按配置类型(system/business)过滤,需要管理员权限
// @Tags admin
// @Produce json
// @Param type query string false "配置类型(system/business)"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/system-configs [get]
func ListSystemConfigs(c *gin.Context) {
configType := c.Query("type")
query := db.DB(c.Request.Context()).Order("created_at DESC")
if configType != "" {
query = query.Where("type = ?", configType)
}
var configs []model.SystemConfig
if err := db.DB(c.Request.Context()).
Order("created_at DESC").
Find(&configs).Error; err != nil {
if err := query.Find(&configs).Error; err != nil {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
@@ -107,6 +118,8 @@ func ListSystemConfigs(c *gin.Context) {
}
// GetSystemConfig 获取单个系统配置
// @Summary 获取单个系统配置
// @Description 根据配置键获取对应的系统配置详情,需要管理员权限
// @Tags admin
// @Produce json
// @Param key path string true "配置键"
@@ -127,11 +140,13 @@ func GetSystemConfig(c *gin.Context) {
}
// UpdateSystemConfig 更新系统配置
// @Summary 更新系统配置
// @Description 根据配置键更新对应的配置内容,同时将更新同步到 Redis,需要管理员权限
// @Tags admin
// @Accept json
// @Produce json
// @Param key path string true "配置键"
// @Param request body UpdateSystemConfigRequest true "request body"
// @Param request body UpdateSystemConfigRequest true "更新请求参数"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/system-configs/{key} [put]
func UpdateSystemConfig(c *gin.Context) {
@@ -178,6 +193,8 @@ func UpdateSystemConfig(c *gin.Context) {
}
// DeleteSystemConfig 删除系统配置
// @Summary 删除系统配置
// @Description 根据配置键删除对应配置,同时从 Redis 中移除对应缓存,需要管理员权限
// @Tags admin
// @Produce json
// @Param key path string true "配置键"
+7 -37
View File
@@ -17,7 +17,6 @@ limitations under the License.
package task
import (
"encoding/json"
"fmt"
"net/http"
"time"
@@ -30,6 +29,8 @@ import (
)
// ListTaskTypes 获取支持的任务类型列表
// @Summary 获取支持的任务类型
// @Description 返回系统支持的所有可调度任务类型列表,需要管理员权限
// @Tags admin
// @Produce json
// @Success 200 {object} util.ResponseAny
@@ -47,10 +48,12 @@ type DispatchTaskRequest struct {
}
// DispatchTask 下发任务
// @Summary 下发异步任务
// @Description 手动触发指定类型的异步任务,支持指定时间范围和用户,需要管理员权限
// @Tags admin
// @Accept json
// @Produce json
// @Param request body DispatchTaskRequest true "request body"
// @Param request body DispatchTaskRequest true "任务请求参数"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/tasks/dispatch [post]
func DispatchTask(c *gin.Context) {
@@ -69,41 +72,8 @@ func DispatchTask(c *gin.Context) {
var taskInfo *asynq.Task
var taskID string
switch req.TaskType {
case task.TaskTypeOrderSync:
if req.StartTime != nil && req.EndTime != nil {
if req.EndTime.Before(*req.StartTime) {
c.JSON(http.StatusBadRequest, util.Err(InvalidTimeRange))
return
}
payload, _ := json.Marshal(map[string]interface{}{
"start_time": req.StartTime,
"end_time": req.EndTime,
})
taskInfo = asynq.NewTask(meta.AsynqTask, payload)
taskID = fmt.Sprintf("manual_%s_%s_%s", req.TaskType,
req.StartTime.Format("20060102150405"),
req.EndTime.Format("20060102150405"))
} else {
taskInfo = asynq.NewTask(meta.AsynqTask, nil)
taskID = fmt.Sprintf("manual_%s", req.TaskType)
}
case task.TaskTypeUserGamification:
if req.UserID == nil || *req.UserID == 0 {
taskInfo = asynq.NewTask(task.UpdateUserGamificationScoresTask, nil)
taskID = fmt.Sprintf("manual_%s", req.TaskType)
} else {
payload, _ := json.Marshal(map[string]interface{}{
"user_id": *req.UserID,
})
taskInfo = asynq.NewTask(meta.AsynqTask, payload)
taskID = fmt.Sprintf("manual_%s_user_%d", req.TaskType, *req.UserID)
}
default:
taskInfo = asynq.NewTask(meta.AsynqTask, nil)
taskID = fmt.Sprintf("manual_%s", req.TaskType)
}
taskInfo = asynq.NewTask(meta.AsynqTask, nil)
taskID = fmt.Sprintf("manual_%s", req.TaskType)
_, err := scheduler.AsynqClient.Enqueue(
taskInfo,
+6 -1
View File
@@ -64,6 +64,8 @@ type listUsersResponse struct {
}
// ListUsers 获取用户列表
// @Summary 获取用户列表
// @Description 分页返回用户列表,支持按用户 ID 和用户名筛选,需要管理员权限
// @Tags admin
// @Produce json
// @Param request query listUsersRequest true "查询参数"
@@ -122,10 +124,13 @@ type updateUserStatusRequest struct {
}
// UpdateUserStatus 更新用户状态(启用/禁用)
// @Summary 更新用户状态
// @Description 启用或禁用指定用户,管理员账号无法被禁用,需要管理员权限
// @Tags admin
// @Accept json
// @Produce json
// @Param id path int true "用户ID"
// @Param request body updateUserStatusRequest true "状态"
// @Param request body updateUserStatusRequest true "状态参数"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/users/{id}/status [put]
func UpdateUserStatus(c *gin.Context) {
@@ -1,25 +0,0 @@
/*
Copyright 2025 linux.do
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user_pay_config
const (
UserPayConfigNotFound = "支付配置不存在"
LevelRequired = "等级不能为空"
MinScoreRequired = "最小分数不能为空"
ScoreRangeInvalid = "分数范围无效:最大分数必须大于最小分数"
LevelExists = "等级已存在"
)
@@ -1,211 +0,0 @@
/*
Copyright 2025 linux.do
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user_pay_config
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/linux-do/credit/internal/db"
"github.com/linux-do/credit/internal/model"
"github.com/linux-do/credit/internal/util"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
// CreateUserPayConfigRequest 创建支付配置请求
type CreateUserPayConfigRequest struct {
Level model.PayLevel `json:"level"`
MinScore int64 `json:"min_score"`
MaxScore *int64 `json:"max_score" binding:"omitempty,gtfield=MinScore"`
DailyLimit *int64 `json:"daily_limit"`
FeeRate decimal.Decimal `json:"fee_rate" binding:"required"`
ScoreRate decimal.Decimal `json:"score_rate" binding:"required"`
DistributeRate decimal.Decimal `json:"distribute_rate" binding:"required"`
}
// UpdateUserPayConfigRequest 更新支付配置请求
type UpdateUserPayConfigRequest struct {
MinScore int64 `json:"min_score"`
MaxScore *int64 `json:"max_score" binding:"omitempty,gtfield=MinScore"`
DailyLimit *int64 `json:"daily_limit"`
FeeRate decimal.Decimal `json:"fee_rate" binding:"required"`
ScoreRate decimal.Decimal `json:"score_rate" binding:"required"`
DistributeRate decimal.Decimal `json:"distribute_rate" binding:"required"`
}
// CreateUserPayConfig 创建支付配置
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreateUserPayConfigRequest true "request body"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/user-pay-configs [post]
func CreateUserPayConfig(c *gin.Context) {
var req CreateUserPayConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, util.Err(err.Error()))
return
}
// 验证费率和积分倍率
if err := util.ValidateRates(req.FeeRate, req.ScoreRate, req.DistributeRate); err != nil {
c.JSON(http.StatusBadRequest, util.Err(err.Error()))
return
}
// 检查等级是否已存在
var existing model.UserPayConfig
if err := db.DB(c.Request.Context()).Where("level = ?", req.Level).First(&existing).Error; err == nil {
c.JSON(http.StatusBadRequest, util.Err(LevelExists))
return
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
config := model.UserPayConfig{
Level: req.Level,
MinScore: req.MinScore,
MaxScore: req.MaxScore,
DailyLimit: req.DailyLimit,
FeeRate: req.FeeRate,
ScoreRate: req.ScoreRate,
DistributeRate: req.DistributeRate,
}
if err := db.DB(c.Request.Context()).Create(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
c.JSON(http.StatusOK, util.OK(config))
}
// ListUserPayConfigs 获取支付配置列表
// @Tags admin
// @Produce json
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/user-pay-configs [get]
func ListUserPayConfigs(c *gin.Context) {
var configs []model.UserPayConfig
if err := db.DB(c.Request.Context()).
Order("min_score ASC").
Find(&configs).Error; err != nil {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
c.JSON(http.StatusOK, util.OK(configs))
}
// GetUserPayConfig 获取单个支付配置
// @Tags admin
// @Produce json
// @Param id path string true "配置ID"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/user-pay-configs/{id} [get]
func GetUserPayConfig(c *gin.Context) {
var config model.UserPayConfig
if err := db.DB(c.Request.Context()).Where("id = ?", c.Param("id")).First(&config).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, util.Err(UserPayConfigNotFound))
} else {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
}
return
}
c.JSON(http.StatusOK, util.OK(config))
}
// UpdateUserPayConfig 更新支付配置
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "配置ID"
// @Param request body UpdateUserPayConfigRequest true "request body"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/user-pay-configs/{id} [put]
func UpdateUserPayConfig(c *gin.Context) {
var req UpdateUserPayConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, util.Err(err.Error()))
return
}
// 验证费率和积分倍率
if err := util.ValidateRates(req.FeeRate, req.ScoreRate); err != nil {
c.JSON(http.StatusBadRequest, util.Err(err.Error()))
return
}
// 检查配置是否存在
var config model.UserPayConfig
if err := db.DB(c.Request.Context()).Where("id = ?", c.Param("id")).First(&config).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, util.Err(UserPayConfigNotFound))
} else {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
}
return
}
// 更新配置
if err := db.DB(c.Request.Context()).
Model(&config).
Updates(map[string]interface{}{
"min_score": req.MinScore,
"max_score": req.MaxScore,
"fee_rate": req.FeeRate,
"score_rate": req.ScoreRate,
"daily_limit": req.DailyLimit,
"distribute_rate": req.DistributeRate,
}).Error; err != nil {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
c.JSON(http.StatusOK, util.OKNil())
}
// DeleteUserPayConfig 删除支付配置
// @Tags admin
// @Produce json
// @Param id path string true "配置ID"
// @Success 200 {object} util.ResponseAny
// @Router /api/v1/admin/user-pay-configs/{id} [delete]
func DeleteUserPayConfig(c *gin.Context) {
// 检查配置是否存在
var config model.UserPayConfig
if err := db.DB(c.Request.Context()).Where("id = ?", c.Param("id")).First(&config).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, util.Err(UserPayConfigNotFound))
} else {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
}
return
}
if err := db.DB(c.Request.Context()).Delete(&config).Error; err != nil {
c.JSON(http.StatusInternalServerError, util.Err(err.Error()))
return
}
c.JSON(http.StatusOK, util.OKNil())
}