Initial commit: 网络验证平台

This commit is contained in:
Admin
2026-04-27 17:22:56 +08:00
commit afe67d704e
780 changed files with 88960 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
package main
import (
"fmt"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
models := []struct {
name string
query interface{}
}{
{"User", &[]model.User{}},
{"AppUser", &[]model.AppUser{}},
{"DynamicCode", &[]model.DynamicCode{}},
{"Application", &[]model.Application{}},
{"Card", &[]model.Card{}},
{"Order", &[]model.Order{}},
{"RechargeRecord", &[]model.RechargeRecord{}},
{"ConsumptionRecord", &[]model.ConsumptionRecord{}},
{"DocCategory", &[]model.DocCategory{}},
{"Doc", &[]model.Doc{}},
{"Setting", &[]model.Setting{}},
}
for _, m := range models {
var allCount int64
var activeCount int64
database.DB.Unscoped().Model(m.query).Count(&allCount)
database.DB.Model(m.query).Count(&activeCount)
deletedCount := allCount - activeCount
if deletedCount > 0 {
fmt.Printf("%s: 总数=%d, 活跃=%d, 已删除=%d\n", m.name, allCount, activeCount, deletedCount)
}
}
}
@@ -0,0 +1,39 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
var allDynamicCodes []model.DynamicCode
if err := database.DB.Unscoped().Find(&allDynamicCodes).Error; err != nil {
log.Printf("查询失败: %v", err)
return
}
log.Println("=== 所有动态代码(包括已删除) ===")
for _, dc := range allDynamicCodes {
deletedAt := ""
if dc.DeletedAt.Valid {
deletedAt = dc.DeletedAt.Time.Format("2006-01-02 15:04:05")
}
fmt.Printf("ID: %d, Name: %s, Key: %s, DeletedAt: %s\n", dc.ID, dc.Name, dc.Key, deletedAt)
}
var activeDynamicCodes []model.DynamicCode
if err := database.DB.Find(&activeDynamicCodes).Error; err != nil {
log.Printf("查询失败: %v", err)
return
}
log.Println("\n=== 未删除的动态代码 ===")
for _, dc := range activeDynamicCodes {
fmt.Printf("ID: %d, Name: %s, Key: %s\n", dc.ID, dc.Name, dc.Key)
}
}
+36
View File
@@ -0,0 +1,36 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
var dynamicCodes []model.DynamicCode
if err := database.DB.Preload("Creator").Preload("Application").Find(&dynamicCodes).Error; err != nil {
log.Printf("查询失败: %v", err)
return
}
log.Println("=== 动态代码列表 ===")
for _, dc := range dynamicCodes {
fmt.Printf("ID: %d\n", dc.ID)
fmt.Printf(" Name: %s\n", dc.Name)
fmt.Printf(" ApplicationID: %d\n", dc.ApplicationID)
fmt.Printf(" UserID: %d\n", dc.UserID)
fmt.Printf(" Application Name: %s\n", dc.Application.Name)
if dc.Creator.ID != 0 {
fmt.Printf(" Creator ID: %d\n", dc.Creator.ID)
fmt.Printf(" Creator Username: %s\n", dc.Creator.Username)
} else {
fmt.Printf(" Creator: (empty)\n")
}
fmt.Printf(" Status: %s\n", dc.Status)
fmt.Println()
}
}
+44
View File
@@ -0,0 +1,44 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
db := database.DB
var packages []model.Package
if err := db.Find(&packages).Error; err != nil {
log.Fatal("Failed to get packages:", err)
}
fmt.Println("=== Packages ===")
for _, pkg := range packages {
fmt.Printf("ID: %d, Name: %s, NameEn: %s, DescriptionEn: %s\n", pkg.ID, pkg.Name, pkg.NameEn, pkg.DescriptionEn)
}
var categories []model.DocCategory
if err := db.Find(&categories).Error; err != nil {
log.Fatal("Failed to get categories:", err)
}
fmt.Println("\n=== Doc Categories ===")
for _, cat := range categories {
fmt.Printf("ID: %d, Name: %s, NameEn: %s, DescriptionEn: %s\n", cat.ID, cat.Name, cat.NameEn, cat.DescriptionEn)
}
var docs []model.Doc
if err := db.Find(&docs).Error; err != nil {
log.Fatal("Failed to get docs:", err)
}
fmt.Println("\n=== Docs ===")
for _, doc := range docs {
fmt.Printf("ID: %d, Title: %s, TitleEn: %s, ContentEn: %d chars\n", doc.ID, doc.Title, doc.TitleEn, len(doc.ContentEn))
}
}
+33
View File
@@ -0,0 +1,33 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 查看套餐数据 ===")
var packages []model.Package
if err := database.DB.Find(&packages).Error; err != nil {
log.Printf("查询套餐失败: %v\n", err)
return
}
for _, pkg := range packages {
fmt.Printf("ID: %d, Name: '%s', Price: %.2f, Period: %s, Status: %s\n",
pkg.ID, pkg.Name, pkg.Price, pkg.Period, pkg.Status)
var permission model.PackagePermission
if err := database.DB.Where("package_id = ?", pkg.ID).First(&permission).Error; err != nil {
fmt.Printf(" 权限: 未找到\n")
} else {
fmt.Printf(" 权限: MaxApps=%d, MaxStorage=%d, MaxApiCalls=%d\n",
permission.MaxApplications, permission.MaxStorage, permission.MaxApiCalls)
}
}
}
@@ -0,0 +1,977 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 查看当前所有文档 ===")
var docs []model.Doc
if err := database.DB.Find(&docs).Error; err != nil {
log.Printf("查询文档失败: %v", err)
return
}
for _, doc := range docs {
log.Printf("ID: %d, Slug: %s, Title: %s", doc.ID, doc.Slug, doc.Title)
}
log.Println("\n=== 删除旧的云端函数文档 ===")
if err := database.DB.Where("slug = ?", "dynamic-code").Delete(&model.Doc{}).Error; err != nil {
log.Printf("删除文档失败: %v", err)
}
if err := database.DB.Where("slug = ?", "cloud-function").Delete(&model.Doc{}).Error; err != nil {
log.Printf("删除文档失败: %v", err)
}
log.Println("\n=== 创建云端函数文档 ===")
var apiCat model.DocCategory
if err := database.DB.Where("slug = ?", "api").First(&apiCat).Error; err != nil {
log.Printf("未找到API分类: %v", err)
return
}
cloudFunctionContent := "# 云端函数接口\n\n" +
"云端函数允许开发者为应用创建自定义的业务逻辑,通过JavaScript代码实现灵活的数据处理和业务规则。\n\n" +
"---\n\n" +
"## 概述\n\n" +
"云端函数是基于JavaScript的自定义代码,可以在应用运行时动态执行。开发者可以通过云端函数实现:\n\n" +
"- 动态定价计算\n" +
"- 权限验证逻辑\n" +
"- 业务规则判断\n" +
"- 用户数据处理\n" +
"- 自动化操作指令\n\n" +
"---\n\n" +
"## 获取云端函数列表\n\n" +
"### 接口地址\n\n" +
"`GET /api/v1/app/:appKey/cloud-function`\n\n" +
"### 请求头\n\n" +
"```\n" +
"Authorization: Bearer {token}\n" +
"```\n\n" +
"### 路径参数\n\n" +
"| 参数名 | 类型 | 必填 | 说明 |\n" +
"|--------|------|------|------|\n" +
"| appKey | string | 是 | 应用密钥 |\n\n" +
"### 响应示例\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"加法函数\",\n" +
" \"key\": \"add\",\n" +
" \"description\": \"实现数字加法运算\",\n" +
" \"status\": \"active\",\n" +
" \"created_at\": \"2024-01-01T00:00:00Z\"\n" +
" }\n" +
" ]\n" +
"}\n" +
"```\n\n" +
"### 字段说明\n\n" +
"| 字段名 | 类型 | 说明 |\n" +
"|--------|------|------|\n" +
"| id | number | 云端函数ID |\n" +
"| name | string | 云端函数名称 |\n" +
"| key | string | 云端函数唯一标识 |\n" +
"| description | string | 描述 |\n" +
"| status | string | 状态(active、inactive |\n" +
"| created_at | string | 创建时间 |\n\n" +
"---\n\n" +
"## 执行云端函数\n\n" +
"### 接口地址\n\n" +
"`POST /api/v1/app/:appKey/cloud-function/:key/execute`\n\n" +
"### 请求头\n\n" +
"```\n" +
"Authorization: Bearer {token}\n" +
"Content-Type: application/json\n" +
"```\n\n" +
"### 路径参数\n\n" +
"| 参数名 | 类型 | 必填 | 说明 |\n" +
"|--------|------|------|------|\n" +
"| appKey | string | 是 | 应用密钥 |\n" +
"| key | string | 是 | 云端函数标识 |\n\n" +
"### 请求体\n\n" +
"```json\n" +
"{\n" +
" \"params\": {\n" +
" \"param1\": \"value1\",\n" +
" \"param2\": \"value2\"\n" +
" },\n" +
" \"user_id\": 123\n" +
"}\n" +
"```\n\n" +
"### 参数说明\n\n" +
"| 参数名 | 类型 | 必填 | 说明 |\n" +
"|--------|------|------|------|\n" +
"| params | object | 否 | 自定义参数对象,键值对形式传递 |\n" +
"| user_id | number | 否 | 用户ID,提供后会注入当前用户信息 |\n\n" +
"### 响应示例\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": \"执行结果\",\n" +
" \"execution_time\": 1\n" +
" }\n" +
"}\n" +
"```\n\n" +
"### 字段说明\n\n" +
"| 字段名 | 类型 | 说明 |\n" +
"|--------|------|------|\n" +
"| result | any | 代码执行结果,可以是任意类型 |\n" +
"| execution_time | number | 执行时间(毫秒) |\n\n" +
"---\n\n" +
"## 注入的数据\n\n" +
"### 1. 应用配置数据 (app)\n\n" +
"云端函数可以访问应用的完整配置信息:\n\n" +
"```javascript\n" +
"app.name\n" +
"app.description\n" +
"app.app_key\n" +
"app.status\n" +
"app.billing_type\n" +
"app.point_price\n" +
"app.points_per_cycle\n" +
"app.point_deduction_cycle\n" +
"app.enable_trial\n" +
"app.trial_type\n" +
"app.trial_duration\n" +
"app.trial_points\n" +
"app.enable_free_period\n" +
"app.free_period_type\n" +
"app.free_period_start\n" +
"app.free_period_end\n" +
"app.free_period_weekdays\n" +
"app.free_period_start_time\n" +
"app.free_period_end_time\n" +
"app.max_devices\n" +
"app.bind_type\n" +
"app.multi_open\n" +
"app.multi_open_mode\n" +
"app.max_instances\n" +
"app.login_policy\n" +
"app.max_attempts\n" +
"app.lock_duration\n" +
"app.heartbeat_interval\n" +
"app.heartbeat_timeout\n" +
"app.change_limit\n" +
"app.change_interval\n" +
"app.change_exceed_action\n" +
"app.change_deduct_amount\n" +
"```\n\n" +
"#### 应用配置字段说明\n\n" +
"| 字段名 | 类型 | 说明 |\n" +
"|--------|------|------|\n" +
"| name | string | 应用名称 |\n" +
"| description | string | 应用描述 |\n" +
"| app_key | string | 应用密钥 |\n" +
"| status | string | 应用状态 |\n" +
"| billing_type | string | 计费方式 |\n" +
"| point_price | number | 点数价格 |\n" +
"| points_per_cycle | number | 每周期扣点数 |\n" +
"| point_deduction_cycle | string | 扣点周期 |\n" +
"| enable_trial | boolean | 是否启用试用 |\n" +
"| trial_type | string | 试用类型 |\n" +
"| trial_duration | number | 试用时长 |\n" +
"| trial_points | number | 试用点数 |\n" +
"| enable_free_period | boolean | 是否启用免费时段 |\n" +
"| free_period_type | string | 免费时段类型 |\n" +
"| free_period_start | string | 免费时段开始 |\n" +
"| free_period_end | string | 免费时段结束 |\n" +
"| free_period_weekdays | string | 免费时段星期 |\n" +
"| free_period_start_time | string | 免费时段开始时间 |\n" +
"| free_period_end_time | string | 免费时段结束时间 |\n" +
"| max_devices | number | 最大设备数 |\n" +
"| bind_type | string | 绑定方式 |\n" +
"| multi_open | boolean | 是否允许多开 |\n" +
"| multi_open_mode | string | 多开模式 |\n" +
"| max_instances | number | 最大实例数 |\n" +
"| login_policy | string | 登录策略 |\n" +
"| max_attempts | number | 最大尝试次数 |\n" +
"| lock_duration | number | 锁定时长 |\n" +
"| heartbeat_interval | number | 心跳间隔 |\n" +
"| heartbeat_timeout | number | 心跳超时 |\n" +
"| change_limit | number | 变更限制 |\n" +
"| change_interval | number | 变更间隔 |\n" +
"| change_exceed_action | string | 超限操作 |\n" +
"| change_deduct_amount | number | 超限扣点数 |\n\n" +
"### 2. 用户信息 (user)\n\n" +
"如果请求中提供了 `user_id`,会自动注入当前用户的信息:\n\n" +
"```javascript\n" +
"user.id\n" +
"user.username\n" +
"user.email\n" +
"user.status\n" +
"user.points\n" +
"user.is_trial_user\n" +
"user.expiry_at\n" +
"user.last_login_at\n" +
"```\n\n" +
"#### 用户信息字段说明\n\n" +
"| 字段名 | 类型 | 说明 |\n" +
"|--------|------|------|\n" +
"| id | number | 用户ID |\n" +
"| username | string | 用户名 |\n" +
"| email | string | 邮箱 |\n" +
"| status | string | 用户状态 |\n" +
"| points | number | 积分 |\n" +
"| is_trial_user | boolean | 是否试用用户 |\n" +
"| expiry_at | string | 到期时间 |\n" +
"| last_login_at | string | 最后登录时间 |\n\n" +
"### 3. 自定义参数\n\n" +
"通过请求的 `params` 参数传递的自定义数据,可以直接在代码中使用:\n\n" +
"```javascript\n" +
"// 请求参数: {\"params\": {\"a\": 10, \"b\": 20, \"name\": \"张三\"}}\n\n" +
"a + b\n" +
"name.toUpperCase()\n" +
"```\n\n" +
"---\n\n" +
"## JavaScript代码编写规范\n\n" +
"### 1. 参数使用\n\n" +
"云端函数可以直接使用请求参数中传递的变量,无需额外定义:\n\n" +
"```javascript\n" +
"a + b\n" +
"```\n\n" +
"**请求参数**`{\"params\": {\"a\": 10, \"b\": 20}}`\n\n" +
"**返回结果**`30`\n\n" +
"---\n\n" +
"### 2. 函数定义与调用\n\n" +
"可以定义函数并在最后一行调用:\n\n" +
"```javascript\n" +
"function calculate(x, y) {\n" +
" return Math.sqrt(x * x + y * y);\n" +
"}\n\n" +
"calculate(x, y)\n" +
"```\n\n" +
"**请求参数**`{\"params\": {\"x\": 3, \"y\": 4}}`\n\n" +
"**返回结果**`5`\n\n" +
"---\n\n" +
"### 3. 复杂逻辑\n\n" +
"支持完整的JavaScript语法,包括控制流、循环、对象操作等:\n\n" +
"```javascript\n" +
"function processScore(score) {\n" +
" if (score >= 90) {\n" +
" return \"优秀\";\n" +
" } else if (score >= 60) {\n" +
" return \"及格\";\n" +
" } else {\n" +
" return \"不及格\";\n" +
" }\n" +
"}\n\n" +
"processScore(score)\n" +
"```\n\n" +
"---\n\n" +
"### 4. 返回值规则\n\n" +
"- **最后一个表达式的值即为返回值**\n" +
"- **不要在全局作用域使用return语句**\n" +
"- 可以返回任意类型:数字、字符串、布尔值、对象、数组等\n\n" +
"---\n\n" +
"### 5. 使用注入数据\n\n" +
"#### 使用应用配置\n\n" +
"```javascript\n" +
"function getAppInfo() {\n" +
" return {\n" +
" name: app.name,\n" +
" status: app.status,\n" +
" billing_type: app.billing_type,\n" +
" max_devices: app.max_devices,\n" +
" enable_trial: app.enable_trial\n" +
" };\n" +
"}\n\n" +
"getAppInfo()\n" +
"```\n\n" +
"#### 使用用户信息\n\n" +
"```javascript\n" +
"function getUserInfo() {\n" +
" if (!user) {\n" +
" return { error: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" return {\n" +
" username: user.username,\n" +
" points: user.points,\n" +
" is_trial_user: user.is_trial_user,\n" +
" status: user.status\n" +
" };\n" +
"}\n\n" +
"getUserInfo()\n" +
"```\n\n" +
"#### 组合使用\n\n" +
"```javascript\n" +
"function checkUserPermission() {\n" +
" if (!user) {\n" +
" return { error: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" if (user.status !== \"active\") {\n" +
" return { error: \"用户状态异常\" };\n" +
" }\n" +
" \n" +
" if (app.max_devices > 0) {\n" +
" return {\n" +
" allowed: true,\n" +
" max_devices: app.max_devices,\n" +
" user_status: user.status\n" +
" };\n" +
" }\n" +
" \n" +
" return { allowed: false, reason: \"应用未设置设备限制\" };\n" +
"}\n\n" +
"checkUserPermission()\n" +
"```\n\n" +
"---\n\n" +
"### 6. 常见示例\n\n" +
"#### 简单计算\n\n" +
"```javascript\n" +
"price * quantity\n" +
"```\n\n" +
"#### 字符串拼接\n\n" +
"```javascript\n" +
"name + \"的年龄是\" + age + \"岁\"\n" +
"```\n\n" +
"#### 条件判断\n\n" +
"```javascript\n" +
"score >= 60 ? \"及格\" : \"不及格\"\n" +
"```\n\n" +
"#### 对象返回\n\n" +
"```javascript\n" +
"{\n" +
" sum: a + b,\n" +
" product: a * b,\n" +
" difference: a - b\n" +
"}\n" +
"```\n\n" +
"#### 数组操作\n\n" +
"```javascript\n" +
"function sumArray(numbers) {\n" +
" let total = 0;\n" +
" for (let i = 0; i < numbers.length; i++) {\n" +
" total += numbers[i];\n" +
" }\n" +
" return total;\n" +
"}\n\n" +
"sumArray([1, 2, 3, 4, 5])\n" +
"```\n\n" +
"---\n\n" +
"### 7. 注意事项\n\n" +
"1. **全局作用域不能使用return**return只能在函数内部使用\n" +
"2. **参数变量直接使用**:无需重新定义\n" +
"3. **最后一行是返回值**:确保最后一行是需要返回的表达式\n" +
"4. **支持ES6语法**:可以使用let、const、箭头函数等\n" +
"5. **内置对象可用**Math、Date、JSON等JavaScript内置对象都可以使用\n\n" +
"---\n\n" +
"## 操作指令\n\n" +
"云端函数可以返回操作指令,后端会安全地执行这些指令。\n\n" +
"### 1. 加时指令\n\n" +
"为用户延长使用时间。\n\n" +
"#### 指令格式\n\n" +
"```javascript\n" +
"{\n" +
" action: \"extend_time\",\n" +
" user_id: 123,\n" +
" days: 7,\n" +
" reason: \"用户申请延期\"\n" +
"}\n" +
"```\n\n" +
"#### 字段说明\n\n" +
"| 字段名 | 类型 | 必填 | 说明 |\n" +
"|--------|------|------|------|\n" +
"| action | string | 是 | 固定值:\"extend_time\" |\n" +
"| user_id | number | 是 | 用户ID |\n" +
"| days | number | 是 | 延长天数 |\n" +
"| reason | string | 否 | 延长原因 |\n\n" +
"#### 示例\n\n" +
"```javascript\n" +
"function autoExtendTime(userId, userLevel) {\n" +
" let days = 0;\n" +
" \n" +
" if (userLevel === \"vip\") {\n" +
" days = 30;\n" +
" } else if (userLevel === \"premium\") {\n" +
" days = 15;\n" +
" } else if (userLevel === \"normal\") {\n" +
" days = 7;\n" +
" }\n" +
" \n" +
" return {\n" +
" action: \"extend_time\",\n" +
" user_id: userId,\n" +
" days: days,\n" +
" reason: \"根据用户等级自动加时\"\n" +
" };\n" +
"}\n\n" +
"autoExtendTime(123, \"vip\")\n" +
"```\n\n" +
"---\n\n" +
"### 2. 扣点指令\n\n" +
"扣除用户积分。\n\n" +
"#### 指令格式\n\n" +
"```javascript\n" +
"{\n" +
" action: \"deduct_points\",\n" +
" user_id: 123,\n" +
" points: 100,\n" +
" reason: \"使用功能扣点\"\n" +
"}\n" +
"```\n\n" +
"#### 字段说明\n\n" +
"| 字段名 | 类型 | 必填 | 说明 |\n" +
"|--------|------|------|------|\n" +
"| action | string | 是 | 固定值:\"deduct_points\" |\n" +
"| user_id | number | 是 | 用户ID |\n" +
"| points | number | 是 | 扣除点数 |\n" +
"| reason | string | 否 | 扣点原因 |\n\n" +
"#### 示例\n\n" +
"```javascript\n" +
"function autoDeductPoints(userId, usage) {\n" +
" if (!user || user.points < usage) {\n" +
" return { error: \"积分不足\" };\n" +
" }\n" +
" \n" +
" return {\n" +
" action: \"deduct_points\",\n" +
" user_id: userId,\n" +
" points: usage,\n" +
" reason: \"功能使用扣点\"\n" +
" };\n" +
"}\n\n" +
"autoDeductPoints(123, 50)\n" +
"```\n\n" +
"---\n\n" +
"## 完整示例\n\n" +
"### 示例1:动态定价\n\n" +
"#### 场景\n" +
"根据用户等级和应用设置,动态计算价格。\n\n" +
"#### 请求\n\n" +
"```json\n" +
"POST /api/v1/app/your-app-key/cloud-function/calculate-price/execute\n" +
"{\n" +
" \"params\": {\n" +
" \"basePrice\": 100,\n" +
" \"userType\": \"new\"\n" +
" }\n" +
"}\n" +
"```\n\n" +
"#### 云端函数代码\n\n" +
"```javascript\n" +
"function calculatePrice(basePrice, userType) {\n" +
" let finalPrice = basePrice;\n" +
" \n" +
" if (app.enable_trial && userType === \"new\") {\n" +
" finalPrice = finalPrice * 0.5;\n" +
" }\n" +
" \n" +
" if (userType === \"vip\") {\n" +
" finalPrice = finalPrice * 0.8;\n" +
" }\n" +
" \n" +
" return {\n" +
" original_price: basePrice,\n" +
" user_type: userType,\n" +
" enable_trial: app.enable_trial,\n" +
" final_price: finalPrice,\n" +
" discount: basePrice - finalPrice\n" +
" };\n" +
"}\n\n" +
"calculatePrice(basePrice, userType)\n" +
"```\n\n" +
"#### 响应\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": {\n" +
" \"original_price\": 100,\n" +
" \"user_type\": \"new\",\n" +
" \"enable_trial\": true,\n" +
" \"final_price\": 50,\n" +
" \"discount\": 50\n" +
" },\n" +
" \"execution_time\": 1\n" +
" }\n" +
"}\n" +
"```\n\n" +
"---\n\n" +
"### 示例2:权限验证\n\n" +
"#### 场景\n" +
"检查用户设备数量是否超过限制。\n\n" +
"#### 请求\n\n" +
"```json\n" +
"POST /api/v1/app/your-app-key/cloud-function/check-permission/execute\n" +
"{\n" +
" \"params\": {\n" +
" \"deviceCount\": 2\n" +
" },\n" +
" \"user_id\": 123\n" +
"}\n" +
"```\n\n" +
"#### 云端函数代码\n\n" +
"```javascript\n" +
"function checkPermission(deviceCount) {\n" +
" if (!user) {\n" +
" return { allowed: false, reason: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" if (user.status !== \"active\") {\n" +
" return { allowed: false, reason: \"用户状态异常\" };\n" +
" }\n" +
" \n" +
" if (deviceCount > app.max_devices) {\n" +
" return { \n" +
" allowed: false, \n" +
" reason: \"设备数量超过限制\",\n" +
" current: deviceCount,\n" +
" max: app.max_devices\n" +
" };\n" +
" }\n" +
" \n" +
" return { \n" +
" allowed: true, \n" +
" reason: \"权限验证通过\",\n" +
" user: user.username\n" +
" };\n" +
"}\n\n" +
"checkPermission(deviceCount)\n" +
"```\n\n" +
"#### 响应\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": {\n" +
" \"allowed\": true,\n" +
" \"reason\": \"权限验证通过\",\n" +
" \"user\": \"testuser\"\n" +
" },\n" +
" \"execution_time\": 1\n" +
" }\n" +
"}\n" +
"```\n\n" +
"---\n\n" +
"### 示例3:每日登录自动加时\n\n" +
"#### 场景\n" +
"试用用户每日登录自动延长1天。\n\n" +
"#### 请求\n\n" +
"```json\n" +
"POST /api/v1/app/your-app-key/cloud-function/daily-login/execute\n" +
"{\n" +
" \"params\": {\n" +
" \"userId\": 123\n" +
" },\n" +
" \"user_id\": 123\n" +
"}\n" +
"```\n\n" +
"#### 云端函数代码\n\n" +
"```javascript\n" +
"function processDailyLogin(userId) {\n" +
" if (!user) {\n" +
" return { error: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" if (user.status !== \"active\") {\n" +
" return { error: \"用户状态异常\" };\n" +
" }\n" +
" \n" +
" if (user.is_trial_user) {\n" +
" return {\n" +
" action: \"extend_time\",\n" +
" user_id: userId,\n" +
" days: 1,\n" +
" reason: \"试用用户每日登录加时\",\n" +
" timestamp: new Date().toISOString()\n" +
" };\n" +
" }\n" +
" \n" +
" return {\n" +
" message: \"登录成功\",\n" +
" user_points: user.points,\n" +
" app_name: app.name\n" +
" };\n" +
"}\n\n" +
"processDailyLogin(userId)\n" +
"```\n\n" +
"#### 响应\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": {\n" +
" \"action\": \"extend_time\",\n" +
" \"user_id\": 123,\n" +
" \"days\": 1,\n" +
" \"reason\": \"试用用户每日登录加时\",\n" +
" \"timestamp\": \"2026-03-11T00:57:55.123Z\"\n" +
" },\n" +
" \"execution_time\": 2\n" +
" }\n" +
"}\n" +
"```\n\n" +
"---\n\n" +
"### 示例4:功能使用自动扣点\n\n" +
"#### 场景\n" +
"用户使用功能时自动扣除相应积分。\n\n" +
"#### 请求\n\n" +
"```json\n" +
"POST /api/v1/app/your-app-key/cloud-function/use-feature/execute\n" +
"{\n" +
" \"params\": {\n" +
" \"userId\": 123,\n" +
" \"featureType\": \"advanced\"\n" +
" },\n" +
" \"user_id\": 123\n" +
"}\n" +
"```\n\n" +
"#### 云端函数代码\n\n" +
"```javascript\n" +
"function processFeatureUsage(userId, featureType) {\n" +
" if (!user) {\n" +
" return { error: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" if (user.status !== \"active\") {\n" +
" return { error: \"用户状态异常\" };\n" +
" }\n" +
" \n" +
" let cost = app.points_per_cycle;\n" +
" \n" +
" if (featureType === \"advanced\") {\n" +
" cost = cost * 2;\n" +
" } else if (featureType === \"premium\") {\n" +
" cost = cost * 5;\n" +
" }\n" +
" \n" +
" if (user.points < cost) {\n" +
" return { \n" +
" error: \"积分不足\",\n" +
" required: cost,\n" +
" current: user.points\n" +
" };\n" +
" }\n" +
" \n" +
" return {\n" +
" action: \"deduct_points\",\n" +
" user_id: userId,\n" +
" points: cost,\n" +
" reason: \"使用功能扣点: \" + featureType,\n" +
" feature_type: featureType\n" +
" };\n" +
"}\n\n" +
"processFeatureUsage(userId, featureType)\n" +
"```\n\n" +
"#### 响应\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": {\n" +
" \"action\": \"deduct_points\",\n" +
" \"user_id\": 123,\n" +
" \"points\": 2,\n" +
" \"reason\": \"使用功能扣点: advanced\",\n" +
" \"feature_type\": \"advanced\"\n" +
" },\n" +
" \"execution_time\": 1\n" +
" }\n" +
"}\n" +
"```\n\n" +
"---\n\n" +
"### 示例5:业务规则获取\n\n" +
"#### 场景\n" +
"获取应用的所有业务规则,用于前端展示。\n\n" +
"#### 请求\n\n" +
"```json\n" +
"POST /api/v1/app/your-app-key/cloud-function/get-rules/execute\n" +
"{\n" +
" \"params\": {}\n" +
"}\n" +
"```\n\n" +
"#### 云端函数代码\n\n" +
"```javascript\n" +
"function getBusinessRules() {\n" +
" const rules = [];\n" +
" \n" +
" if (app.enable_trial) {\n" +
" rules.push({\n" +
" type: \"trial\",\n" +
" title: \"试用功能\",\n" +
" description: \"支持试用,试用时长:\" + app.trial_duration + \"天\"\n" +
" });\n" +
" }\n" +
" \n" +
" if (app.max_devices > 1) {\n" +
" rules.push({\n" +
" type: \"multi_device\",\n" +
" title: \"多设备支持\",\n" +
" description: \"支持多设备,最多\" + app.max_devices + \"台\"\n" +
" });\n" +
" }\n" +
" \n" +
" if (app.enable_free_period) {\n" +
" rules.push({\n" +
" type: \"free_period\",\n" +
" title: \"免费时段\",\n" +
" description: \"支持免费时段:\" + app.free_period_start + \" - \" + app.free_period_end\n" +
" });\n" +
" }\n" +
" \n" +
" if (app.point_price > 0) {\n" +
" rules.push({\n" +
" type: \"points\",\n" +
" title: \"积分系统\",\n" +
" description: \"点数价格:\" + app.point_price + \",每周期扣点:\" + app.points_per_cycle\n" +
" });\n" +
" }\n" +
" \n" +
" return {\n" +
" app_name: app.name,\n" +
" total_rules: rules.length,\n" +
" rules: rules\n" +
" };\n" +
"}\n\n" +
"getBusinessRules()\n" +
"```\n\n" +
"#### 响应\n\n" +
"```json\n" +
"{\n" +
" \"code\": 200,\n" +
" \"message\": \"操作成功\",\n" +
" \"data\": {\n" +
" \"result\": {\n" +
" \"app_name\": \"我的应用\",\n" +
" \"total_rules\": 4,\n" +
" \"rules\": [\n" +
" {\n" +
" \"type\": \"trial\",\n" +
" \"title\": \"试用功能\",\n" +
" \"description\": \"支持试用,试用时长:7天\"\n" +
" },\n" +
" {\n" +
" \"type\": \"multi_device\",\n" +
" \"title\": \"多设备支持\",\n" +
" \"description\": \"支持多设备,最多3台\"\n" +
" },\n" +
" {\n" +
" \"type\": \"free_period\",\n" +
" \"title\": \"免费时段\",\n" +
" \"description\": \"支持免费时段:20:00 - 08:00\"\n" +
" },\n" +
" {\n" +
" \"type\": \"points\",\n" +
" \"title\": \"积分系统\",\n" +
" \"description\": \"点数价格:0.1,每周期扣点:1\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"execution_time\": 1\n" +
" }\n" +
"}\n" +
"```\n\n" +
"---\n\n" +
"## 错误处理\n\n" +
"### 常见错误\n\n" +
"#### 1. 语法错误\n\n" +
"```json\n" +
"{\n" +
" \"code\": 400,\n" +
" \"message\": \"代码执行错误: SyntaxError: Unexpected token\"\n" +
"}\n" +
"```\n\n" +
"**原因**JavaScript代码语法错误\n\n" +
"**解决**:检查代码语法,确保括号、引号等匹配正确\n\n" +
"---\n\n" +
"#### 2. 运行时错误\n\n" +
"```json\n" +
"{\n" +
" \"code\": 400,\n" +
" \"message\": \"代码执行错误: ReferenceError: x is not defined\"\n" +
"}\n" +
"```\n\n" +
"**原因**:使用了未定义的变量\n\n" +
"**解决**:检查变量名是否正确,或确保已通过参数传入\n\n" +
"---\n\n" +
"#### 3. 全局return错误\n\n" +
"```json\n" +
"{\n" +
" \"code\": 400,\n" +
" \"message\": \"代码执行错误: SyntaxError: Illegal return statement\"\n" +
"}\n" +
"```\n\n" +
"**原因**:在全局作用域使用了return语句\n\n" +
"**解决**:将return语句放在函数内部,或直接返回表达式\n\n" +
"---\n\n" +
"#### 4. 操作执行失败\n\n" +
"```json\n" +
"{\n" +
" \"code\": 500,\n" +
" \"message\": \"执行加时操作失败: user not found\"\n" +
"}\n" +
"```\n\n" +
"**原因**:操作指令中的用户不存在\n\n" +
"**解决**:检查user_id是否正确,或用户是否属于当前应用\n\n" +
"---\n\n" +
"## 性能建议\n\n" +
"1. **避免死循环**:确保循环有明确的退出条件\n" +
"2. **合理使用缓存**:对于重复计算,可以考虑缓存结果\n" +
"3. **控制代码复杂度**:过复杂的代码可能影响执行效率\n" +
"4. **使用内置函数**:优先使用JavaScript内置函数,性能更好\n" +
"5. **避免频繁操作**:操作指令会修改数据库,避免频繁调用\n\n" +
"---\n\n" +
"## 安全建议\n\n" +
"1. **不要执行危险操作**:避免访问文件系统、网络请求等\n" +
"2. **参数验证**:在代码中对参数进行必要的验证\n" +
"3. **避免敏感信息**:不要在代码中硬编码密钥、密码等敏感信息\n" +
"4. **限制执行时间**:设置合理的超时时间,防止长时间运行\n" +
"5. **操作指令谨慎**:操作指令会修改数据库,确保逻辑正确\n" +
"6. **用户数据保护**:不要在代码中泄露用户敏感信息\n\n" +
"---\n\n" +
"## 最佳实践\n\n" +
"### 1. 代码组织\n\n" +
"将复杂逻辑封装成函数,提高代码可读性:\n\n" +
"```javascript\n" +
"function validateUser() {\n" +
" if (!user) {\n" +
" return { valid: false, reason: \"用户不存在\" };\n" +
" }\n" +
" \n" +
" if (user.status !== \"active\") {\n" +
" return { valid: false, reason: \"用户状态异常\" };\n" +
" }\n" +
" \n" +
" return { valid: true };\n" +
"}\n\n" +
"function calculatePrice(basePrice, discount) {\n" +
" return basePrice * (1 - discount / 100);\n" +
"}\n\n" +
"function mainProcess() {\n" +
" const validation = validateUser();\n" +
" if (!validation.valid) {\n" +
" return validation;\n" +
" }\n" +
" \n" +
" const price = calculatePrice(100, 20);\n" +
" return { success: true, price: price };\n" +
"}\n\n" +
"mainProcess()\n" +
"```\n\n" +
"---\n\n" +
"### 2. 错误处理\n\n" +
"在代码中添加适当的错误处理:\n\n" +
"```javascript\n" +
"function safeCalculate(a, b) {\n" +
" if (typeof a !== \"number\" || typeof b !== \"number\") {\n" +
" return { error: \"参数必须是数字\" };\n" +
" }\n" +
" \n" +
" try {\n" +
" return { result: a + b };\n" +
" } catch (e) {\n" +
" return { error: \"计算失败: \" + e.message };\n" +
" }\n" +
"}\n\n" +
"safeCalculate(a, b)\n" +
"```\n\n" +
"---\n\n" +
"### 3. 使用现代JavaScript\n\n" +
"利用ES6+特性提高代码质量:\n\n" +
"```javascript\n" +
"// 使用箭头函数\n" +
"const calculate = (x, y) => x + y;\n\n" +
"// 使用解构赋值\n" +
"const { name, age } = userInfo;\n\n" +
"// 使用模板字符串\n" +
"const message = `用户${name}的年龄是${age}岁`;\n\n" +
"// 使用数组方法\n" +
"const sum = numbers.reduce((a, b) => a + b, 0);\n\n" +
"// 使用对象简写\n" +
"const result = { name, age, status };\n" +
"```\n\n" +
"---\n\n" +
"### 4. 日志记录\n\n" +
"在返回结果中包含调试信息:\n\n" +
"```javascript\n" +
"function processWithLog(userId) {\n" +
" const startTime = new Date();\n" +
" \n" +
" if (!user) {\n" +
" return { \n" +
" error: \"用户不存在\",\n" +
" timestamp: startTime.toISOString(),\n" +
" debug: { userId }\n" +
" };\n" +
" }\n" +
" \n" +
" const result = {\n" +
" user_id: userId,\n" +
" username: user.username,\n" +
" status: user.status\n" +
" };\n" +
" \n" +
" return {\n" +
" success: true,\n" +
" data: result,\n" +
" timestamp: startTime.toISOString(),\n" +
" execution_time: new Date() - startTime\n" +
" };\n" +
"}\n\n" +
"processWithLog(userId)\n" +
"```\n\n" +
"---\n\n" +
"## 常见问题\n\n" +
"### Q1: 云端函数可以访问数据库吗?\n\n" +
"**A**: 不能直接访问数据库。云端函数只能使用注入的数据(app、user)和请求参数。如果需要修改数据,应该返回操作指令(extend_time、deduct_points),由后端安全地执行。\n\n" +
"---\n\n" +
"### Q2: 云端函数可以调用外部API吗?\n\n" +
"**A**: 不能。云端函数在沙箱环境中执行,不支持网络请求。如果需要调用外部API,应该通过后端接口实现。\n\n" +
"---\n\n" +
"### Q3: 云端函数的执行时间有限制吗?\n\n" +
"**A**: 有。云端函数有执行时间限制,超时会被终止。建议避免死循环和长时间运行的操作。\n\n" +
"---\n\n" +
"### Q4: 如何调试云端函数?\n\n" +
"**A**: 可以在返回结果中包含调试信息,或者使用简单的测试参数验证逻辑是否正确。\n\n" +
"---\n\n" +
"### Q5: 云端函数可以修改应用配置吗?\n\n" +
"**A**: 不能。app对象是只读的,不能修改。如果需要修改应用配置,应该使用专门的管理接口。\n\n" +
"---\n\n" +
"## 更新日志\n\n" +
"### v1.0.0 (2026-03-11)\n" +
"- 初始版本发布\n" +
"- 支持应用配置数据注入\n" +
"- 支持用户信息注入\n" +
"- 支持操作指令(加时、扣点)\n" +
"- 支持完整的JavaScript语法\n" +
"- 提供丰富的使用示例\n"
cloudFunctionDoc := model.Doc{
Title: "云端函数接口",
CategoryID: &apiCat.ID,
Slug: "cloud-function",
Content: cloudFunctionContent,
Summary: "云端函数允许开发者为应用创建自定义的业务逻辑,通过JavaScript代码实现灵活的数据处理和业务规则",
Icon: "⚡",
Sort: 1,
Status: "published",
}
if err := database.DB.Create(&cloudFunctionDoc).Error; err != nil {
log.Printf("创建云端函数文档失败: %v", err)
return
}
log.Println("云端函数文档创建成功")
}
+349
View File
@@ -0,0 +1,349 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
docContent := `# 手动扣费 API
## 概述
手动扣费功能允许开发者在余额模式下,通过 API 自行控制扣费时机和金额。这为开发者提供了最大的灵活性,可以根据业务需求实现自定义的计费逻辑。
## 前提条件
1. 应用的运营模式必须设置为**余额模式**
2. 扣费方式必须设置为**手动扣费**
## 扣费方式说明
扣费方式分为两级选择:
### 第一级:扣费方式
| 方式 | 说明 |
|------|------|
| 自动扣费 | 系统自动触发扣费 |
| 手动扣费 | 通过 API 自行控制扣费 |
### 第二级:自动扣费类型(仅自动扣费时显示)
| 类型 | 说明 |
|------|------|
| 登录扣费 | 每次登录验证时扣费 |
| 计时扣费 | 按设定的时间间隔自动扣费 |
### 计时扣费配置
选择计时扣费后,可自定义扣费间隔:
- **间隔值**1-9999 之间的整数
- **时间单位**:分钟、小时、天
例如:设置为 "每 30 分钟扣费一次" 或 "每 2 小时扣费一次"
## 接口详情
### 请求地址
POST /api/v1/dev/applications/:id/deduct
### 请求头
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| Authorization | string | 是 | Bearer {token},开发者登录后获取的令牌 |
| Content-Type | string | 是 | application/json |
### 路径参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | integer | 是 | 应用ID |
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | integer | 是 | 要扣费的用户ID |
| amount | number | 是 | 扣费金额,必须大于0 |
| description | string | 否 | 扣费描述/原因 |
### 请求示例
` + "```json" + `
{
"user_id": 123,
"amount": 10.5,
"description": "使用高级功能扣费"
}
` + "```" + `
### 响应示例
#### 成功响应
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"user_id": 123,
"username": "testuser",
"amount": 10.5,
"balance_before": 100.5,
"balance_after": 90.0,
"description": "使用高级功能扣费",
"message": "扣费成功"
}
}
` + "```" + `
#### 错误响应
**应用不存在**
` + "```json" + `
{
"code": 404,
"message": "应用不存在"
}
` + "```" + `
**非余额模式**
` + "```json" + `
{
"code": 400,
"message": "只有余额模式的应用才支持手动扣费"
}
` + "```" + `
**未开启手动扣费**
` + "```json" + `
{
"code": 400,
"message": "该应用未开启手动扣费模式,请先在设置中修改扣费频率为手动扣费"
}
` + "```" + `
**用户不存在**
` + "```json" + `
{
"code": 404,
"message": "用户不存在"
}
` + "```" + `
**余额不足**
` + "```json" + `
{
"code": 400,
"message": "用户余额不足,当前余额: 5.00,需扣除: 10.50"
}
` + "```" + `
**永久会员**
` + "```json" + `
{
"code": 400,
"message": "该用户为永久会员,无法扣费"
}
` + "```" + `
## 使用场景
### 1. 按次计费
用户每次使用特定功能时扣费:
` + "```javascript" + `
// 用户使用高级分析功能
async function useAdvancedAnalysis(userId) {
const response = await fetch('/api/v1/dev/applications/1/deduct', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: userId,
amount: 5.0,
description: '使用高级分析功能'
})
});
const result = await response.json();
if (result.code === 200) {
// 扣费成功,执行功能
return true;
} else {
// 扣费失败,提示用户
alert(result.message);
return false;
}
}
` + "```" + `
### 2. 阶梯计费
根据使用量阶梯定价:
` + "```javascript" + `
function calculatePrice(usage) {
if (usage <= 100) {
return 1.0; // 前100次,每次1元
} else if (usage <= 500) {
return 0.8; // 101-500次,每次0.8元
} else {
return 0.5; // 500次以上,每次0.5元
}
}
async function chargeUser(userId, currentUsage) {
const price = calculatePrice(currentUsage);
// 调用扣费API...
}
` + "```" + `
### 3. 动态定价
根据时间段或活动动态调整价格:
` + "```javascript" + `
function getDynamicPrice() {
const hour = new Date().getHours();
const dayOfWeek = new Date().getDay();
// 周末打折
if (dayOfWeek === 0 || dayOfWeek === 6) {
return 5.0 * 0.8;
}
// 深夜时段打折
if (hour >= 22 || hour < 6) {
return 5.0 * 0.5;
}
return 5.0;
}
` + "```" + `
### 4. 批量扣费
对多个用户批量扣费:
` + "```javascript" + `
async function batchDeduct(users) {
const results = [];
for (const user of users) {
try {
const response = await fetch('/api/v1/dev/applications/1/deduct', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: user.id,
amount: user.amount,
description: user.description
})
});
const result = await response.json();
results.push({
user_id: user.id,
success: result.code === 200,
message: result.message
});
} catch (error) {
results.push({
user_id: user.id,
success: false,
message: error.message
});
}
}
return results;
}
` + "```" + `
## 注意事项
1. **余额检查**: 调用扣费接口前,建议先检查用户余额是否充足
2. **幂等性**: 如果业务需要保证幂等性,请在业务层实现去重逻辑
3. **并发控制**: 高并发场景下,建议使用锁机制防止余额扣成负数
4. **记录保存**: 每次扣费都会自动创建消费记录,可在后台查看
5. **错误处理**: 请妥善处理各种错误情况,给用户友好的提示
## 与自动扣费的区别
| 特性 | 手动扣费 | 自动扣费 |
|------|----------|----------|
| 扣费时机 | 开发者自行控制 | 系统自动执行 |
| 扣费金额 | 每次可不同 | 固定金额 |
| 灵活性 | 高 | 低 |
| 实现复杂度 | 需要开发者实现 | 无需开发 |
| 适用场景 | 复杂计费逻辑 | 简单按时/按次计费 |
## 相关接口
- [获取用户信息](/docs/app-api-docs#获取账户信息) - 查询用户当前余额
- [用户充值](/docs/app-api-docs#卡密充值) - 用户通过卡密充值
- [消费记录](/docs/finance) - 查看扣费记录`
var devCategory model.DocCategory
if err := database.DB.Where("slug = ?", "dev-docs").First(&devCategory).Error; err != nil {
devCategory = model.DocCategory{
Name: "开发者文档",
Slug: "dev-docs",
Description: "开发者API接口文档",
Sort: 90,
}
if err := database.DB.Create(&devCategory).Error; err != nil {
log.Printf("创建开发者文档分类失败: %v", err)
return
}
}
var existingDoc model.Doc
if err := database.DB.Where("slug = ?", "manual-deduct-api").First(&existingDoc).Error; err == nil {
existingDoc.Content = docContent
if err := database.DB.Save(&existingDoc).Error; err != nil {
log.Printf("更新手动扣费文档失败: %v", err)
} else {
log.Println("手动扣费文档更新成功")
}
return
}
doc := model.Doc{
Title: "手动扣费 API",
CategoryID: &devCategory.ID,
Slug: "manual-deduct-api",
Content: docContent,
Summary: "余额模式下通过API自行控制扣费时机和金额,支持按次计费、阶梯计费、动态定价等场景",
Icon: "💳",
Sort: 10,
Status: "published",
}
if err := database.DB.Create(&doc).Error; err != nil {
log.Printf("创建手动扣费文档失败: %v", err)
return
}
log.Println("手动扣费文档创建成功")
}
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 修复套餐数据 ===")
var pkg model.Package
if err := database.DB.First(&pkg, 1).Error; err != nil {
log.Printf("查询套餐失败: %v\n", err)
return
}
log.Printf("修复前: ID=%d, Name='%s', Price=%.2f, Period='%s', Status='%s'\n",
pkg.ID, pkg.Name, pkg.Price, pkg.Period, pkg.Status)
if pkg.Name == "" {
pkg.Name = "免费版"
pkg.Price = 0
pkg.Period = "permanent"
pkg.Status = "active"
pkg.Description = "免费体验版本,功能有限"
pkg.Sort = 0
if err := database.DB.Save(&pkg).Error; err != nil {
log.Printf("更新套餐失败: %v\n", err)
return
}
log.Printf("修复后: ID=%d, Name='%s', Price=%.2f, Period='%s', Status='%s'\n",
pkg.ID, pkg.Name, pkg.Price, pkg.Period, pkg.Status)
var permission model.PackagePermission
if err := database.DB.Where("package_id = ?", pkg.ID).First(&permission).Error; err != nil {
log.Printf("查询权限失败: %v\n", err)
} else {
permission.MaxApplications = 1
permission.MaxStorage = 100
permission.MaxApiCalls = 1000
permission.AllowAgent = false
permission.AllowCloudData = false
permission.AllowDynamicCode = false
permission.PrioritySupport = false
if err := database.DB.Save(&permission).Error; err != nil {
log.Printf("更新权限失败: %v\n", err)
} else {
log.Printf("权限已更新: MaxApps=%d, MaxStorage=%d, MaxApiCalls=%d\n",
permission.MaxApplications, permission.MaxStorage, permission.MaxApiCalls)
}
}
} else {
log.Println("套餐名称不为空,无需修复")
}
log.Println("套餐数据修复完成")
}
+113
View File
@@ -0,0 +1,113 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 初始化套餐数据 ===")
var count int64
database.DB.Model(&model.Package{}).Count(&count)
log.Printf("当前套餐数量: %d\n", count)
if count > 0 {
log.Println("套餐数据已存在,跳过初始化")
return
}
packages := []model.Package{
{
Name: "基础版",
Description: "适合个人开发者和小型项目",
Price: 9.9,
Period: "monthly",
Status: "active",
Sort: 1,
IsRecommended: false,
AllowUpgrade: true,
},
{
Name: "专业版",
Description: "适合中小型企业和成长型项目",
Price: 29.9,
Period: "monthly",
Status: "active",
Sort: 2,
IsRecommended: true,
AllowUpgrade: true,
},
{
Name: "企业版",
Description: "适合大型企业和高流量项目",
Price: 99.9,
Period: "monthly",
Status: "active",
Sort: 3,
IsRecommended: false,
AllowUpgrade: true,
},
{
Name: "永久版",
Description: "一次性购买,永久使用",
Price: 999,
Period: "permanent",
Status: "active",
Sort: 4,
IsRecommended: false,
AllowUpgrade: false,
},
}
for _, pkg := range packages {
if err := database.DB.Create(&pkg).Error; err != nil {
log.Printf("创建套餐失败: %v\n", err)
continue
}
permission := model.PackagePermission{
PackageID: pkg.ID,
MaxApplications: 5,
MaxStorage: 1024,
MaxApiCalls: 10000,
AllowAgent: false,
AllowCloudData: true,
AllowDynamicCode: false,
PrioritySupport: false,
}
if pkg.Name == "专业版" {
permission.MaxApplications = 20
permission.MaxStorage = 5120
permission.MaxApiCalls = 100000
permission.AllowAgent = true
permission.AllowDynamicCode = true
} else if pkg.Name == "企业版" {
permission.MaxApplications = -1
permission.MaxStorage = -1
permission.MaxApiCalls = -1
permission.AllowAgent = true
permission.AllowDynamicCode = true
permission.PrioritySupport = true
} else if pkg.Name == "永久版" {
permission.MaxApplications = -1
permission.MaxStorage = -1
permission.MaxApiCalls = -1
permission.AllowAgent = true
permission.AllowDynamicCode = true
permission.PrioritySupport = true
}
if err := database.DB.Create(&permission).Error; err != nil {
log.Printf("创建套餐权限失败: %v\n", err)
} else {
log.Printf("创建套餐成功: %s (ID: %d)\n", pkg.Name, pkg.ID)
}
}
log.Println("套餐初始化完成")
}
+23
View File
@@ -0,0 +1,23 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 查看当前所有文档 ===")
var docs []model.Doc
if err := database.DB.Find(&docs).Error; err != nil {
log.Printf("查询文档失败: %v", err)
return
}
for _, doc := range docs {
log.Printf("ID: %d, Slug: %s, Title: %s", doc.ID, doc.Slug, doc.Title)
}
}
+56
View File
@@ -0,0 +1,56 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
db := database.DB
var categories []model.DocCategory
if err := db.Find(&categories).Error; err != nil {
log.Fatal("获取分类失败:", err)
}
for _, cat := range categories {
if cat.NameEn == "" && cat.Name != "" {
cat.NameEn = cat.Name
}
if cat.DescriptionEn == "" && cat.Description != "" {
cat.DescriptionEn = cat.Description
}
if err := db.Save(&cat).Error; err != nil {
log.Printf("更新分类 %d 失败: %v", cat.ID, err)
} else {
fmt.Printf("已更新分类: %s\n", cat.Name)
}
}
var docs []model.Doc
if err := db.Find(&docs).Error; err != nil {
log.Fatal("获取文档失败:", err)
}
for _, doc := range docs {
if doc.TitleEn == "" && doc.Title != "" {
doc.TitleEn = doc.Title
}
if doc.ContentEn == "" && doc.Content != "" {
doc.ContentEn = doc.Content
}
if err := db.Save(&doc).Error; err != nil {
log.Printf("更新文档 %d 失败: %v", doc.ID, err)
} else {
fmt.Printf("已更新文档: %s\n", doc.Title)
}
}
fmt.Println("\n迁移完成!")
fmt.Printf("更新了 %d 个分类\n", len(categories))
fmt.Printf("更新了 %d 个文档\n", len(docs))
}
+178
View File
@@ -0,0 +1,178 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
db := database.DB
categoryTranslations := map[string]struct {
NameEn string
DescriptionEn string
}{
"快速开始": {NameEn: "Quick Start", DescriptionEn: "Complete integration in 5 minutes"},
"API文档": {NameEn: "API Documentation", DescriptionEn: "Complete API reference"},
"示例代码": {NameEn: "Code Examples", DescriptionEn: "Common use case examples"},
"常见问题": {NameEn: "FAQ", DescriptionEn: "Frequently asked questions"},
}
var categories []model.DocCategory
if err := db.Find(&categories).Error; err != nil {
log.Fatal("Failed to get categories:", err)
}
for _, cat := range categories {
if trans, ok := categoryTranslations[cat.Name]; ok {
cat.NameEn = trans.NameEn
cat.DescriptionEn = trans.DescriptionEn
if err := db.Save(&cat).Error; err != nil {
log.Printf("Failed to update category %d: %v", cat.ID, err)
} else {
fmt.Printf("Translated category: %s -> %s\n", cat.Name, cat.NameEn)
}
}
}
docTranslations := map[string]struct {
TitleEn string
ContentEn string
}{
"用户注册": {
TitleEn: "User Registration",
ContentEn: "# User Registration\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/register\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| username | string | Yes | Username |\n| password | string | Yes | Password |\n| device_id | string | Yes | Device fingerprint, unique device identifier |\n| device_name | string | No | Device name, e.g. \"My Computer\", defaults to device_id |\n| email | string | Conditional | Email address, required when email verification is enabled |\n| email_code | string | Conditional | Email verification code, required when mandatory email verification is enabled |\n| device_type | string | No | Device type: android/ios/windows/mac/linux/web, auto-detected if not provided |\n| instance_id | string | No | Instance ID for multi-instance detection, defaults to device_id |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"password\": \"password123\",\n \"device_id\": \"abc123def456\",\n \"device_name\": \"My Computer\",\n \"email\": \"user@example.com\",\n \"email_code\": \"123456\",\n \"device_type\": \"windows\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Registration successful\",\n \"data\": {\n \"user_id\": 123\n }\n}\n```\n\n### Notes\n- device_id is the device fingerprint used to uniquely identify a device\n- device_name is a friendly name for the device for display purposes\n- device_type is auto-detected based on User-Agent if not provided\n- If mandatory email verification is enabled for the application, email and email_code are required",
},
"发送邮箱验证码": {
TitleEn: "Send Email Verification Code",
ContentEn: "# Send Email Verification Code\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/send-email-code\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| email | string | Yes | Email address |\n| purpose | string | No | Purpose: register, reset_password, change_email, defaults to register |\n\n### Request Example\n```json\n{\n \"email\": \"user@example.com\",\n \"purpose\": \"register\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": {\n \"message\": \"Verification code sent\"\n }\n}\n```\n\n### Notes\n- Verification code is valid for 15 minutes\n- Returns an error if email verification is not enabled for the application",
},
"用户登录": {
TitleEn: "User Login",
ContentEn: "# User Login\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/login\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| username | string | Yes | Username |\n| password | string | Yes | Password |\n| device_id | string | Yes | Device fingerprint, unique device identifier |\n| device_name | string | No | Device name, e.g. \"My Computer\", defaults to device_id |\n| device_type | string | No | Device type: android/ios/windows/mac/linux/web, auto-detected if not provided |\n| instance_id | string | No | Instance ID for multi-instance detection, defaults to device_id |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"password\": \"password123\",\n \"device_id\": \"abc123def456\",\n \"device_name\": \"My Computer\",\n \"device_type\": \"windows\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Login successful\",\n \"data\": {\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n \"user_id\": 123\n }\n}\n```\n\n### Notes\n- device_id is the device fingerprint used to uniquely identify a device\n- device_name is a friendly name for the device for display purposes\n- device_type is auto-detected based on User-Agent if not provided\n- Returns an error with bound device list if device binding limit is reached",
},
"心跳验证": {
TitleEn: "Heartbeat Verification",
ContentEn: "# Heartbeat Verification\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/heartbeat\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"success\"\n}\n```\n\n### Notes\n- Keeps user online status\n- Recommended to call every 30 seconds\n- User will be considered offline if heartbeat is not called for a certain period\n- JWT token must be included in the request header",
},
"卡密充值": {
TitleEn: "Card Key Recharge",
ContentEn: "# Card Key Recharge\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/recharge\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| username | string | Yes | User account |\n| card_key | string | Yes | Card key |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"card_key\": \"VIP123456\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Recharge successful\",\n \"data\": {\n \"value\": 30\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|-------|------|-------------|\n| value | number | Recharge value (meaning varies by card type) |",
},
"获取绑定设备列表": {
TitleEn: "Get Bound Devices List",
ContentEn: "# Get Bound Devices List\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/devices\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"devices\": [\n {\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"bind_time\": \"2024-03-04T12:00:00Z\",\n \"last_active\": \"2024-03-04T13:00:00Z\"\n }\n ]\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|-------|------|-------------|\n| device_id | string | Device ID |\n| device_name | string | Device name |\n| bind_time | string | Binding time |\n| last_active | string | Last active time |",
},
"解绑设备": {
TitleEn: "Unbind Device",
ContentEn: "# Unbind Device\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Device unbound successfully\"\n}\n```",
},
"通过认证解绑设备": {
TitleEn: "Unbind Device with Authentication",
ContentEn: "# Unbind Device with Authentication\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device-with-auth\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| device_id | string | Yes | Device ID to unbind |\n| password | string | Yes | User password for verification |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\",\n \"password\": \"password123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Device unbound successfully\"\n}\n```",
},
"重置密码(邮箱验证码)": {
TitleEn: "Reset Password (Email Verification)",
ContentEn: "# Reset Password (Email Verification)\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/reset-password\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| email | string | Yes | Email address |\n| email_code | string | Yes | Email verification code |\n| new_password | string | Yes | New password |\n\n### Request Example\n```json\n{\n \"email\": \"user@example.com\",\n \"email_code\": \"123456\",\n \"new_password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Password reset successful\"\n}\n```",
},
"修改密码": {
TitleEn: "Change Password",
ContentEn: "# Change Password\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/change-password\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| old_password | string | Yes | Current password |\n| new_password | string | Yes | New password |\n\n### Request Example\n```json\n{\n \"old_password\": \"oldpassword123\",\n \"new_password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Password changed successfully\"\n}\n```",
},
"获取设备数量": {
TitleEn: "Get Device Count",
ContentEn: "# Get Device Count\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/device-count\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"count\": 2,\n \"max_devices\": 3\n }\n}\n```",
},
"获取应用信息": {
TitleEn: "Get Application Info",
ContentEn: "# Get Application Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/info\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"name\": \"My Application\",\n \"description\": \"Application description\",\n \"version\": \"1.0.0\",\n \"status\": \"active\"\n }\n}\n```",
},
"获取用户账户信息": {
TitleEn: "Get User Account Info",
ContentEn: "# Get User Account Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/user/info\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"user_id\": 123,\n \"username\": \"user123\",\n \"email\": \"user@example.com\",\n \"balance\": 100,\n \"expiry_at\": \"2024-12-31T23:59:59Z\"\n }\n}\n```",
},
"获取应用常量": {
TitleEn: "Get Application Constants",
ContentEn: "# Get Application Constants\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"constants\": [\n {\n \"key\": \"APP_VERSION\",\n \"value\": \"1.0.0\"\n }\n ]\n }\n}\n```",
},
"获取指定常量": {
TitleEn: "Get Specific Constant",
ContentEn: "# Get Specific Constant\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"key\": \"APP_VERSION\",\n \"value\": \"1.0.0\"\n }\n}\n```",
},
"获取应用变量": {
TitleEn: "Get Application Variables",
ContentEn: "# Get Application Variables\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"variables\": [\n {\n \"key\": \"user_setting\",\n \"value\": \"default\"\n }\n ]\n }\n}\n```",
},
"获取指定变量": {
TitleEn: "Get Specific Variable",
ContentEn: "# Get Specific Variable\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"key\": \"user_setting\",\n \"value\": \"default\"\n }\n}\n```",
},
"更新用户变量": {
TitleEn: "Update User Variable",
ContentEn: "# Update User Variable\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/variables/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| value | string | Yes | Variable value |\n\n### Request Example\n```json\n{\n \"value\": \"new_value\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Variable updated successfully\"\n}\n```",
},
"调用云端函数": {
TitleEn: "Call Cloud Function",
ContentEn: "# Call Cloud Function\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/functions/:name\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Example\n```json\n{\n \"param1\": \"value1\",\n \"param2\": \"value2\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"result\": \"function_result\"\n }\n}\n```",
},
"检查更新": {
TitleEn: "Check for Updates",
ContentEn: "# Check for Updates\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/check-update\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| current_version | string | Yes | Current application version |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"has_update\": true,\n \"latest_version\": \"2.0.0\",\n \"download_url\": \"https://example.com/download\",\n \"update_log\": \"Bug fixes and improvements\"\n }\n}\n```",
},
"获取公告": {
TitleEn: "Get Announcements",
ContentEn: "# Get Announcements\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/announcements\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"announcements\": [\n {\n \"id\": 1,\n \"title\": \"System Maintenance\",\n \"content\": \"System maintenance scheduled for...\",\n \"type\": \"warning\",\n \"created_at\": \"2024-03-04T12:00:00Z\"\n }\n ]\n }\n}\n```",
},
"获取在线实例列表": {
TitleEn: "Get Online Instances",
ContentEn: "# Get Online Instances\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/online-instances\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"instances\": [\n {\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"login_time\": \"2024-03-04T12:00:00Z\",\n \"last_heartbeat\": \"2024-03-04T13:00:00Z\"\n }\n ]\n }\n}\n```",
},
"强制离线实例": {
TitleEn: "Force Offline Instance",
ContentEn: "# Force Offline Instance\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/force-offline\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| device_id | string | Yes | Device ID to force offline |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Instance forced offline successfully\"\n}\n```",
},
"软件验证示例": {
TitleEn: "Software Verification Example",
ContentEn: "# Software Verification Example\n\n## Basic Verification Flow\n\n### 1. User Login\n```bash\ncurl -X POST https://api.example.com/api/v1/app/YOUR_APP_KEY/login \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"username\": \"user123\",\n \"password\": \"password123\",\n \"device_id\": \"unique_device_id\"\n }'\n```\n\n### 2. Save Token\nSave the returned token for subsequent API calls.\n\n### 3. Heartbeat\nCall heartbeat API periodically to maintain online status.\n\n## Complete Example (Python)\n```python\nimport requests\nimport time\n\nclass AppClient:\n def __init__(self, app_key):\n self.app_key = app_key\n self.base_url = \"https://api.example.com/api/v1/app\"\n self.token = None\n \n def login(self, username, password, device_id):\n url = f\"{self.base_url}/{self.app_key}/login\"\n response = requests.post(url, json={\n \"username\": username,\n \"password\": password,\n \"device_id\": device_id\n })\n data = response.json()\n if data[\"code\"] == 200:\n self.token = data[\"data\"][\"token\"]\n return True\n return False\n\n# Usage\nclient = AppClient(\"your_app_key\")\nif client.login(\"user123\", \"password\", \"device_001\"):\n while True:\n client.heartbeat(\"device_001\")\n time.sleep(30)\n```",
},
"游戏验证示例": {
TitleEn: "Game Verification Example",
ContentEn: "# Game Verification Example\n\n## Unity Integration\n\n### 1. Create API Client\n```csharp\nusing UnityEngine;\nusing UnityEngine.Networking;\nusing System.Collections;\n\npublic class VerificationClient : MonoBehaviour\n{\n private string appKey = \"your_app_key\";\n private string baseUrl = \"https://api.example.com/api/v1/app\";\n private string token;\n \n public IEnumerator Login(string username, string password, string deviceId)\n {\n string url = $\"{baseUrl}/{appKey}/login\";\n // ... implementation\n }\n}\n```\n\n### 2. Usage in Game\n```csharp\npublic class GameManager : MonoBehaviour\n{\n private VerificationClient client;\n private string deviceId;\n \n void Start()\n {\n deviceId = SystemInfo.deviceUniqueIdentifier;\n client = GetComponent<VerificationClient>();\n StartCoroutine(client.Login(\"user123\", \"password\", deviceId));\n }\n}\n```",
},
"如何获取API密钥?": {
TitleEn: "How to Get API Key?",
ContentEn: "# How to Get API Key?\n\n## Steps\n\n### 1. Register Account\nFirst, register a developer account on the platform.\n\n### 2. Create Application\n1. Go to Console > Applications\n2. Click \"Create Application\" button\n3. Fill in application name and description\n4. Click \"Create\"\n\n### 3. Get App Key\nAfter creating the application, you can find the App Key on the application details page.\n\n## App Key Format\nApp Key is a unique identifier in the format:\n```\napp_xxxxxxxxxxxxxxxx\n```\n\n## Security Notes\n- Do not share your App Key publicly\n- Regenerate App Key if it's compromised\n- Use environment variables to store App Key in production",
},
"卡密验证失败怎么办?": {
TitleEn: "What to Do When Card Key Verification Fails?",
ContentEn: "# What to Do When Card Key Verification Fails?\n\n## Common Issues\n\n### 1. Invalid Card Key\n**Symptom**: Returns \"Invalid or expired card key\"\n\n**Solutions**:\n- Check if the card key is entered correctly\n- Verify the card key hasn't been used\n- Confirm the card key hasn't expired\n\n### 2. Card Key Already Used\n**Symptom**: Returns \"Card key already used\"\n\n**Solutions**:\n- Each card key can only be used once\n- Purchase a new card key\n\n### 3. Card Key Expired\n**Symptom**: Returns \"Card key expired\"\n\n**Solutions**:\n- Check card key validity period\n- Contact support for assistance",
},
"如何实现代理授权?": {
TitleEn: "How to Implement Agent Authorization?",
ContentEn: "# How to Implement Agent Authorization?\n\n## Overview\nAgent authorization allows you to delegate application management to agents.\n\n## Setup Steps\n\n### 1. Create Agent Application\n1. Go to Console > Agent Applications\n2. Click \"Create Agent Application\"\n3. Select the application to authorize\n4. Set commission rate\n\n### 2. Generate Agent Link\nAfter creation, you'll get an agent link:\n```\nhttps://platform.com/agent/AGENT_CODE\n```\n\n### 3. Agent Dashboard\nAgents can access their dashboard to:\n- View sales statistics\n- Generate card keys\n- Manage users",
},
}
var docs []model.Doc
if err := db.Find(&docs).Error; err != nil {
log.Fatal("Failed to get docs:", err)
}
for _, doc := range docs {
if trans, ok := docTranslations[doc.Title]; ok {
doc.TitleEn = trans.TitleEn
doc.ContentEn = trans.ContentEn
if err := db.Save(&doc).Error; err != nil {
log.Printf("Failed to update doc %d: %v", doc.ID, err)
} else {
fmt.Printf("Translated doc: %s -> %s\n", doc.Title, doc.TitleEn)
}
}
}
fmt.Println("\nTranslation completed!")
}
@@ -0,0 +1,93 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
db := database.DB
packageTranslations := map[string]struct {
NameEn string
DescriptionEn string
}{
"免费版": {
NameEn: "Free",
DescriptionEn: "Basic features for individual developers",
},
"公益版": {
NameEn: "Community",
DescriptionEn: "Free for individual developers",
},
"专业版": {
NameEn: "Professional",
DescriptionEn: "Advanced features for professional developers",
},
"企业版": {
NameEn: "Enterprise",
DescriptionEn: "Full features for enterprise teams",
},
"基础版": {
NameEn: "Basic",
DescriptionEn: "Essential features for small projects",
},
"高级版": {
NameEn: "Premium",
DescriptionEn: "Premium features for growing businesses",
},
"旗舰版": {
NameEn: "Flagship",
DescriptionEn: "Top-tier features for large organizations",
},
"开发者版": {
NameEn: "Developer",
DescriptionEn: "Perfect for individual developers",
},
"团队版": {
NameEn: "Team",
DescriptionEn: "Collaborative features for teams",
},
"个人版": {
NameEn: "Personal",
DescriptionEn: "Personal use with essential features",
},
"标准版": {
NameEn: "Standard",
DescriptionEn: "Standard features for regular users",
},
}
var packages []model.Package
if err := db.Find(&packages).Error; err != nil {
log.Fatal("Failed to get packages:", err)
}
for _, pkg := range packages {
if trans, ok := packageTranslations[pkg.Name]; ok {
pkg.NameEn = trans.NameEn
pkg.DescriptionEn = trans.DescriptionEn
if err := db.Save(&pkg).Error; err != nil {
log.Printf("Failed to update package %d: %v", pkg.ID, err)
} else {
fmt.Printf("Translated package: %s -> %s\n", pkg.Name, pkg.NameEn)
}
} else {
pkg.NameEn = pkg.Name
if pkg.Description != "" {
pkg.DescriptionEn = pkg.Description
}
if err := db.Save(&pkg).Error; err != nil {
log.Printf("Failed to update package %d: %v", pkg.ID, err)
} else {
fmt.Printf("Copied package: %s (no translation found)\n", pkg.Name)
}
}
}
fmt.Println("\nPackage translation completed!")
}
+794
View File
@@ -0,0 +1,794 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
apiDocContent := `# 应用API
## 概述
本文档描述了应用对接平台所需的所有API。每个应用都有独立的API地址,通过应用的密钥(appKey)进行访问。
## 基础信息
- **基础URL**: http://your-domain.com/api/v1/app/{appKey}
- **请求方式**: GET/POST
- **数据格式**: JSON
- **字符编码**: UTF-8
## 通信加密
所有API支持加密通信,根据应用的安全设置进行加密解密。
### 加密方式
| 类型 | 说明 |
|------|------|
| none | 不加密,明文传输 |
| aes | AES-GCM加密 |
| rc4 | RC4流加密 |
### 加密请求格式
` + "```json" + `
{
"data": "加密后的数据(Base64编码)"
}
` + "```" + `
### 加密响应格式
` + "```json" + `
{
"data": "加密后的数据(Base64编码)"
}
` + "```" + `
---
## 公告管理
### 获取公告列表
获取应用的公告列表。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/announcements
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"announcements": [
{
"id": 1,
"title": "系统维护通知",
"content": "系统将于今晚进行维护",
"type": "warning",
"priority": "high",
"status": "active",
"created_at": "2024-01-01T00:00:00Z"
}
]
}
}
` + "```" + `
**字段说明**
| 字段 | 类型 | 说明 |
|------|------|------|
| type | string | 公告类型(info、warning、urgent |
| priority | string | 优先级(normal、medium、high |
| status | string | 状态(draft、active |
---
## 版本更新
### 检测更新
检查应用是否有新版本可用。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/check-update?version=1.0.0
` + "```" + `
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| version | string | 是 | 当前版本号 |
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"has_update": true,
"force_update": false,
"latest_version": "2.0.0",
"download_url": "http://example.com/download/app-v2.0.0.zip",
"file_size": 10240000,
"description": "新版本修复了若干bug"
}
}
` + "```" + `
---
## 应用信息
### 获取应用配置
获取应用的基本配置信息。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/info
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"app_id": 1,
"app_name": "示例应用",
"description": "这是一个示例应用",
"billing_type": "subscription",
"encrypt_type": "aes",
"bind_type": "device",
"max_devices": 1,
"multi_open": false,
"enable_trial": true,
"trial_duration": 7
}
}
` + "```" + `
**字段说明**
| 字段 | 类型 | 说明 |
|------|------|------|
| billing_type | string | 计费类型(subscription订阅、time计时、point点数) |
| encrypt_type | string | 加密类型(none、aes、rc4 |
| bind_type | string | 绑定类型(none、device、ip |
| enable_trial | bool | 是否启用试用 |
| trial_duration | int | 试用天数 |
---
## 用户认证
### 用户注册
注册新用户账号。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/register
` + "```" + `
**请求体**
` + "```json" + `
{
"username": "testuser",
"password": "password123",
"email": "test@example.com",
"device_id": "DEVICE-001"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"user_id": 1,
"username": "testuser",
"message": "注册成功"
}
}
` + "```" + `
---
### 用户登录
用户登录获取账号信息。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/login
` + "```" + `
**请求体**
` + "```json" + `
{
"username": "testuser",
"password": "password123",
"device_id": "DEVICE-001"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"user_id": 1,
"username": "testuser",
"balance": 100,
"is_trial": false,
"expiry_at": "2024-12-31T23:59:59Z",
"message": "登录成功"
}
}
` + "```" + `
**错误响应**
| 错误信息 | 说明 |
|----------|------|
| 用户不存在 | 用户名未注册 |
| 密码错误 | 密码不正确 |
| 余额不足 | 账户余额为0且已过期 |
| 设备绑定数量已达上限 | 超过最大设备数限制 |
| 多开数量已达上限 | 同一设备登录用户数超限 |
---
## 卡密充值
### 使用卡密充值
使用卡密为当前用户充值。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/recharge
` + "```" + `
**请求体**
` + "```json" + `
{
"user_id": 1,
"card_key": "CARD-1234567890"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"balance": 200,
"expiry_at": "2024-12-31T23:59:59Z",
"message": "充值成功"
}
}
` + "```" + `
---
## 试用功能
### 申请试用
申请新用户试用。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/trial
` + "```" + `
**请求体**
` + "```json" + `
{
"user_id": 1,
"device_id": "DEVICE-001"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"trial_start": "2024-01-01T00:00:00Z",
"trial_end": "2024-01-08T00:00:00Z",
"expiry_at": "2024-01-08T00:00:00Z",
"message": "试用成功"
}
}
` + "```" + `
---
## 设备管理
### 获取设备列表
获取用户绑定的设备列表。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/devices?user_id=1
` + "```" + `
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"devices": [
{
"device_id": "DEVICE-001",
"device_name": "默认设备",
"last_active": "2024-01-01T12:00:00Z"
}
]
}
}
` + "```" + `
---
### 解绑设备
解绑用户的设备。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/unbind-device
` + "```" + `
**请求体**
` + "```json" + `
{
"user_id": 1,
"device_id": "DEVICE-001"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"message": "解绑成功"
}
}
` + "```" + `
---
## 账户管理
### 获取账户信息
获取用户的详细账户信息。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/account?user_id=1
` + "```" + `
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"user_id": 1,
"username": "testuser",
"email": "test@example.com",
"balance": 100,
"is_trial": false,
"trial_start": null,
"trial_end": null,
"expiry_at": "2024-12-31T23:59:59Z",
"device_id": "DEVICE-001",
"created_at": "2024-01-01T00:00:00Z"
}
}
` + "```" + `
---
### 心跳检测
发送心跳以保持在线状态。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/heartbeat
` + "```" + `
**请求体**
` + "```json" + `
{
"user_id": 1,
"device_id": "DEVICE-001"
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"status": "active",
"message": "心跳成功"
}
}
` + "```" + `
---
## 云端数据
### 获取云端常量
获取全局的云端常量配置。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/constants
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"constants": {
"max_connections": "10",
"timeout": "30",
"retry_times": "3"
}
}
}
` + "```" + `
---
### 获取云端变量
获取用户的云端变量。
**请求**
` + "```" + `
GET /api/v1/app/{appKey}/variables?user_id=1
` + "```" + `
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| user_id | int | 是 | 用户ID |
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"variables": {
"setting1": "value1",
"setting2": "value2"
}
}
}
` + "```" + `
---
### 修改云端变量
更新用户的云端变量。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/variables
` + "```" + `
**请求体**
` + "```json" + `
{
"user_id": 1,
"variables": {
"setting1": "new_value1",
"setting2": "new_value2"
}
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"message": "更新成功"
}
}
` + "```" + `
---
### 调用云端函数
调用云端定义的动态函数。
**请求**
` + "```" + `
POST /api/v1/app/{appKey}/call-function
` + "```" + `
**请求体**
` + "```json" + `
{
"function_name": "custom_function",
"parameters": {
"param1": "value1",
"param2": "value2"
}
}
` + "```" + `
**响应示例**
` + "```json" + `
{
"code": 200,
"message": "success",
"data": {
"result": "动态函数调用成功",
"data": {
"param1": "value1",
"param2": "value2"
}
}
}
` + "```" + `
---
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 请求成功 |
| 400 | 请求参数错误 |
| 401 | 未授权或认证失败 |
| 403 | 禁止访问(账号被禁用、已过期等) |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
---
## 试用和免费时段
### 试用功能
- 订阅模式应用可以启用试用功能
- 新用户可以申请试用,试用时长由应用配置决定
- 试用用户只能试用一次
- 试用期间可以正常使用应用的所有功能
### 免费时段功能
- 订阅模式应用可以设置免费时间段
- 在免费时间段内,所有用户可以免费使用应用
- 免费时间段按每天设置,例如:00:00-06:00
---
## 安全建议
1. **使用HTTPS**: 生产环境建议使用HTTPS协议
2. **启用加密**: 建议启用AES加密保护通信安全
3. **定期更换密钥**: 定期更换应用的通信密钥
4. **验证设备**: 启用设备绑定功能防止账号共享
5. **限制并发**: 根据需要限制最大设备数和并发数
---
## 代码示例
### JavaScript
` + "```javascript" + `
const appKey = 'your-app-key';
const baseUrl = 'http://your-domain.com/api/v1/app/' + appKey;
// 获取公告
async function getAnnouncements() {
const response = await fetch(baseUrl + '/announcements');
const result = await response.json();
if (result.code === 200) {
console.log('公告列表:', result.data.announcements);
}
}
// 用户登录
async function login(username, password, deviceId) {
const response = await fetch(baseUrl + '/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password, device_id: deviceId })
});
const result = await response.json();
if (result.code === 200) {
console.log('登录成功:', result.data);
return result.data;
}
}
// 心跳检测
async function heartbeat(userId, deviceId) {
const response = await fetch(baseUrl + '/heartbeat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: userId, device_id: deviceId })
});
const result = await response.json();
return result.code === 200;
}
` + "```" + `
### Python
` + "```python" + `
import requests
class AppClient:
def __init__(self, app_key, base_url='http://your-domain.com/api/v1/app'):
self.app_key = app_key
self.base_url = base_url + '/' + app_key
def get_announcements(self):
response = requests.get(self.base_url + '/announcements')
result = response.json()
if result['code'] == 200:
return result['data']['announcements']
return None
def login(self, username, password, device_id):
data = {'username': username, 'password': password, 'device_id': device_id}
response = requests.post(self.base_url + '/login', json=data)
result = response.json()
if result['code'] == 200:
return result['data']
return None
def heartbeat(self, user_id, device_id):
data = {'user_id': user_id, 'device_id': device_id}
response = requests.post(self.base_url + '/heartbeat', json=data)
result = response.json()
return result['code'] == 200
# 使用示例
client = AppClient('your-app-key')
announcements = client.get_announcements()
print('公告列表:', announcements)
` + "```" + `
`
var apiCategory model.DocCategory
if err := database.DB.Where("slug = ?", "api-docs").First(&apiCategory).Error; err != nil {
apiCategory = model.DocCategory{
Name: "API文档",
Slug: "api-docs",
Description: "应用对接API文档",
Sort: 100,
}
if err := database.DB.Create(&apiCategory).Error; err != nil {
log.Printf("创建API文档分类失败: %v", err)
return
}
}
var existingDoc model.Doc
if err := database.DB.Where("slug = ?", "app-api-docs").First(&existingDoc).Error; err == nil {
existingDoc.Title = "应用API"
existingDoc.Content = apiDocContent
existingDoc.Summary = "应用对接平台所需的所有API文档,包含用户认证、设备管理、云端数据等核心功能"
if err := database.DB.Save(&existingDoc).Error; err != nil {
log.Printf("更新API文档失败: %v", err)
return
}
log.Println("API文档更新成功")
return
}
apiDoc := model.Doc{
Title: "应用API",
CategoryID: &apiCategory.ID,
Slug: "app-api-docs",
Content: apiDocContent,
Summary: "应用对接平台所需的所有API文档,包含用户认证、设备管理、云端数据等核心功能",
Icon: "code",
Sort: 1,
Status: "published",
}
if err := database.DB.Create(&apiDoc).Error; err != nil {
log.Printf("创建API文档失败: %v", err)
return
}
log.Println("API文档创建成功")
}
+187
View File
@@ -0,0 +1,187 @@
package main
import (
"fmt"
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"gorm.io/gorm/clause"
)
func main() {
database.Init()
db := database.DB
docTranslations := map[string]struct {
TitleEn string
ContentEn string
SummaryEn string
}{
"api-heartbeat": {
TitleEn: "Heartbeat Verification",
ContentEn: "# Heartbeat Verification\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/heartbeat\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"success\"\n}\n```\n\n### Notes\n- Keeps user online status\n- Recommended to call every 30 seconds\n- User will be considered offline if heartbeat is not called for a certain period\n- JWT token must be included in the request header",
SummaryEn: "Heartbeat verification instructions",
},
"api-recharge": {
TitleEn: "Card Key Recharge",
ContentEn: "# Card Key Recharge\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/recharge\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | User account |\n| card_key | string | Yes | Card key |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"card_key\": \"VIP123456\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Recharge successful\",\n \"data\": {\n \"value\": 30\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| value | number | Recharge value (meaning varies by card type) |\n\n### Error Response\n```json\n{\n \"code\": 400,\n \"message\": \"Invalid or expired card key\"\n}\n```",
SummaryEn: "Card key recharge instructions",
},
"api-devices": {
TitleEn: "Get Bound Device List",
ContentEn: "# Get Bound Device List\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/devices\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"devices\": [\n {\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"bind_time\": \"2024-03-04T12:00:00Z\",\n \"last_active\": \"2024-03-04T13:00:00Z\"\n }\n ]\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| device_id | string | Device ID |\n| device_name | string | Device name |\n| bind_time | string | Bind time |\n| last_active | string | Last active time |",
SummaryEn: "Get bound device list instructions",
},
"api-unbind-device": {
TitleEn: "Unbind Device",
ContentEn: "# Unbind Device\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Unbind successful\"\n}\n```\n\n### Error Response\n```json\n{\n \"code\": 400,\n \"message\": \"Device not found or not bound\"\n}\n```",
SummaryEn: "Unbind device instructions",
},
"api-unbind-device-with-auth": {
TitleEn: "Unbind Device with Authentication",
ContentEn: "# Unbind Device with Authentication\n\nWhen users cannot login due to device limit, they can use this endpoint to unbind a device with username and password.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device-with-auth\n```\n\n### Note\nThis endpoint does not require Authorization header, uses username and password for authentication.\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | Username |\n| password | string | Yes | Password |\n| device_id | string | Yes | Device ID to unbind |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"password\": \"password123\",\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Unbind successful\"\n }\n}\n```",
SummaryEn: "Unbind device with authentication instructions",
},
"api-reset-password": {
TitleEn: "Reset Password (Email Verification)",
ContentEn: "# Reset Password (Email Verification)\n\nWhen users forget their password, they can reset it via email verification code.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/reset-password\n```\n\n### Note\nThis endpoint does not require Authorization header, must call send email verification code endpoint first.\n\n### Prerequisites\n- Application has password reset enabled\n- User's package supports password reset\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| email | string | Yes | Email address |\n| code | string | Yes | Email verification code |\n| password | string | Yes | New password (at least 6 characters) |\n\n### Request Example\n```json\n{\n \"email\": \"user@example.com\",\n \"code\": \"123456\",\n \"password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Password reset successful\"\n }\n}\n```",
SummaryEn: "Reset password via email verification code",
},
"api-change-password": {
TitleEn: "Change Password",
ContentEn: "# Change Password\n\nChange password by verifying username and original password.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/change-password\n```\n\n### Note\nThis endpoint does not require Authorization header, uses username and original password for authentication.\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | Username |\n| old_password | string | Yes | Original password |\n| new_password | string | Yes | New password (at least 6 characters) |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"old_password\": \"oldpassword123\",\n \"new_password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Password changed successfully\"\n }\n}\n```",
SummaryEn: "Change password",
},
"api-device-count": {
TitleEn: "Get Device Count",
ContentEn: "# Get Device Count\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/device-count\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"count\": 3,\n \"max_devices\": 5\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| count | number | Current bound device count |\n| max_devices | number | Maximum allowed devices |",
SummaryEn: "Get device count instructions",
},
"api-app-info": {
TitleEn: "Get Application Info",
ContentEn: "# Get Application Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/info\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"app_id\": 1,\n \"name\": \"My App\",\n \"description\": \"Game verification app\",\n \"status\": \"active\"\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| app_id | number | Application ID |\n| name | string | Application name |\n| description | string | Application description |\n| status | string | Application status |",
SummaryEn: "Get application info instructions",
},
"api-account": {
TitleEn: "Get User Account Info",
ContentEn: "# Get User Account Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/account\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"user_id\": 123,\n \"username\": \"user123\",\n \"expire_time\": \"2024-04-04T00:00:00Z\",\n \"remaining_days\": 30\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| user_id | number | User ID |\n| username | string | Username |\n| expire_time | string | Expiration time |\n| remaining_days | number | Remaining days |",
SummaryEn: "Get user account info instructions",
},
"api-constants": {
TitleEn: "Get Application Constants",
ContentEn: "# Get Application Constants\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"name\": \"Constant Name\",\n \"key\": \"CONSTANT_KEY\",\n \"value\": \"Constant Value\",\n \"var_type\": \"string\",\n \"description\": \"Constant description\"\n }\n ]\n}\n```",
SummaryEn: "Get application constants instructions",
},
"api-constant": {
TitleEn: "Get Specific Constant",
ContentEn: "# Get Specific Constant\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| key | string | Yes | Constant key |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"id\": 1,\n \"name\": \"Constant Name\",\n \"key\": \"CONSTANT_KEY\",\n \"value\": \"Constant Value\",\n \"var_type\": \"string\",\n \"description\": \"Constant description\"\n }\n}\n```",
SummaryEn: "Get specific constant instructions",
},
"api-variables": {
TitleEn: "Get Application Variables",
ContentEn: "# Get Application Variables\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"name\": \"Variable Name\",\n \"key\": \"VARIABLE_KEY\",\n \"default_value\": \"Default Value\",\n \"var_type\": \"string\",\n \"scope\": \"user\",\n \"description\": \"Variable description\"\n }\n ]\n}\n```",
SummaryEn: "Get application variables instructions",
},
"api-variable": {
TitleEn: "Get Specific Variable",
ContentEn: "# Get Specific Variable\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| key | string | Yes | Variable key |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"id\": 1,\n \"name\": \"Variable Name\",\n \"key\": \"VARIABLE_KEY\",\n \"default_value\": \"Default Value\",\n \"var_type\": \"string\",\n \"scope\": \"user\",\n \"description\": \"Variable description\"\n }\n}\n```",
SummaryEn: "Get specific variable instructions",
},
"api-update-variables": {
TitleEn: "Update User Variables",
ContentEn: "# Update User Variables\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/variables\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| variables | object | Yes | Variable key-value pairs |\n\n### Request Example\n```json\n{\n \"variables\": {\n \"nickname\": \"New Nickname\",\n \"level\": 10\n }\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Update successful\"\n}\n```\n\n### Notes\n- Only user-scoped variables can be updated\n- Variable value types must match the defined types",
SummaryEn: "Update user variables instructions",
},
"api-call-function": {
TitleEn: "Call Cloud Function",
ContentEn: "# Call Cloud Function\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/call-function\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| function_name | string | Yes | Cloud function name |\n| params | object | No | Parameters to pass to the function |\n\n### Request Example\n```json\n{\n \"function_name\": \"calculatePrice\",\n \"params\": {\n \"x\": 10,\n \"y\": 20\n }\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Success\",\n \"data\": {\n \"result\": 30,\n \"execution_time\": 0.001234\n }\n}\n```",
SummaryEn: "Call cloud function instructions",
},
"api-check-update": {
TitleEn: "Check for Updates",
ContentEn: "# Check for Updates\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/check-update\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| version | string | No | Current client version |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"has_update\": true,\n \"latest_version\": \"2.0.0\",\n \"download_url\": \"https://example.com/download/v2.0.0\",\n \"update_notes\": \"Fixed several bugs\",\n \"update_strategy\": \"optional\",\n \"update_method\": \"manual\"\n }\n}\n```",
SummaryEn: "Check for updates instructions",
},
"api-announcements": {
TitleEn: "Get Announcements",
ContentEn: "# Get Announcements\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/announcements\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"title\": \"System Maintenance Notice\",\n \"content\": \"System will be under maintenance tonight...\",\n \"created_at\": \"2024-03-04T10:00:00Z\"\n }\n ]\n}\n```",
SummaryEn: "Get announcements instructions",
},
"api-instances": {
TitleEn: "Get Online Instance List",
ContentEn: "# Get Online Instance List\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/instances\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| user_id | number | Yes | User ID |\n\n### Request Example\n```json\n{\n \"user_id\": 123\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"instance_id\": \"instance_001\",\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"is_online\": true,\n \"last_heartbeat\": \"2024-03-04T13:00:00Z\",\n \"created_at\": \"2024-03-04T12:00:00Z\"\n }\n ]\n}\n```",
SummaryEn: "Get online instance list instructions",
},
"api-force-offline": {
TitleEn: "Force Instance Offline",
ContentEn: "# Force Instance Offline\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/instances/:instance_id/offline\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| instance_id | string | Yes | Instance identifier |\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| user_id | number | Yes | User ID |\n\n### Request Example\n```json\n{\n \"user_id\": 123\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Forced offline successfully\"\n}\n```",
SummaryEn: "Force instance offline instructions",
},
"example-software": {
TitleEn: "Software Verification Example",
ContentEn: "# Software Verification Example\n\n## Complete Verification Flow\n\n### 1. User Input Username and Password\n\n```javascript\nconst username = document.getElementById('username').value;\nconst password = document.getElementById('password').value;\n```\n\n### 2. Get Device ID\n\n```javascript\nfunction getDeviceId() {\n let deviceId = localStorage.getItem('device_id');\n if (!deviceId) {\n deviceId = 'device_' + Math.random().toString(36).substr(2, 9);\n localStorage.setItem('device_id', deviceId);\n }\n return deviceId;\n}\n\nconst deviceId = getDeviceId();\n```\n\n### 3. Call Login API\n\n```javascript\nconst response = await fetch(`/api/v1/app/${appKey}/login`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n username,\n password,\n device_id: deviceId\n })\n});\n\nconst result = await response.json();\n\nif (result.code !== 200) {\n alert('Login failed: ' + result.message);\n return;\n}\n```\n\n### 4. Save Token\n\n```javascript\nlocalStorage.setItem('token', result.data.token);\nlocalStorage.setItem('expire_time', result.data.expire_time);\n```\n\n### 5. Start Heartbeat\n\n```javascript\nsetInterval(async () => {\n await fetch(`/api/v1/app/${appKey}/heartbeat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${localStorage.getItem('token')}`\n },\n body: JSON.stringify({\n device_id: deviceId\n })\n });\n}, 30000);\n```\n\n### 6. Enter Main Program\n\n```javascript\nstartApplication();\n```",
SummaryEn: "Complete software login verification example",
},
"example-game": {
TitleEn: "Game Verification Example",
ContentEn: "# Game Verification Example\n\n## Unity Integration\n\n### 1. Call Login API\n\n```csharp\nusing UnityEngine;\nusing UnityEngine.Networking;\n\npublic class AuthManager : MonoBehaviour {\n private string appKey = \"your_app_key\";\n private string token;\n private string deviceId;\n \n void Start() {\n deviceId = SystemInfo.deviceUniqueIdentifier;\n }\n \n public async void Login(string username, string password) {\n string url = $\"https://api.example.com/api/v1/app/{appKey}/login\";\n \n string jsonData = $\"{{\\\"username\\\": \\\"{username}\\\", \\\"password\\\": \\\"{password}\\\", \\\"device_id\\\": \\\"{deviceId}\\\"}}\";\n \n using (UnityWebRequest request = UnityWebRequest.Post(url, jsonData)) {\n request.SetRequestHeader(\"Content-Type\", \"application/json\");\n \n yield return request.SendWebRequest();\n \n if (request.result == UnityWebRequest.Result.Success) {\n string response = request.downloadHandler.text;\n var result = JsonUtility.FromJson<LoginResponse>(response);\n \n if (result.code == 200) {\n token = result.data.token;\n PlayerPrefs.SetString(\"token\", token);\n \n // Start heartbeat\n StartCoroutine(HeartbeatCoroutine());\n \n // Enter game\n LoadMainScene();\n } else {\n Debug.LogError(\"Login failed: \" + result.message);\n }\n } else {\n Debug.LogError(\"Request failed: \" + request.error);\n }\n }\n }\n}\n```",
SummaryEn: "Unity game integration example",
},
"faq-api-key": {
TitleEn: "How to Get API Key?",
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to developer dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
SummaryEn: "Detailed steps to get API key",
},
"faq-card-fail": {
TitleEn: "What to Do if Card Key Verification Fails?",
ContentEn: "# What to Do if Card Key Verification Fails?\n\n## Common Reasons\n\n1. **Card Key Expired**\n - Check the validity period of the card key\n - Expired card keys cannot be used\n\n2. **Card Key Already Used**\n - Single-use card keys can only be verified once\n - Used card keys cannot be verified again\n\n3. **Device Binding Error**\n - Check if device ID is correct\n - Confirm if application has device binding enabled\n\n4. **Application Configuration Error**\n - Check if AppID and AppKey are correct\n - Confirm if application status is normal\n\n## Solutions\n\n1. Contact application customer service\n2. Get a new card key\n3. Check device network connection\n4. View application logs for detailed error information",
SummaryEn: "Common reasons and solutions for card key verification failure",
},
"faq-agent": {
TitleEn: "How to Implement Agent Authorization?",
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows developers to authorize their applications to other developers, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent commission and discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Commission**\n - Sales revenue is settled proportionally\n - Authorized party receives commission income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
SummaryEn: "Implementation process and permission management for agent authorization",
},
}
var categories []model.DocCategory
if err := db.Find(&categories).Error; err != nil {
log.Fatal("Failed to get categories:", err)
}
for i := range categories {
translation, ok := docTranslations[categories[i].Slug]
if ok {
categories[i].NameEn = translation.TitleEn
categories[i].DescriptionEn = translation.ContentEn
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name_en", "description_en"}),
}).Create(&categories[i])
fmt.Printf("Updated category: %s -> %s\n", categories[i].Name, categories[i].NameEn)
}
}
var docs []model.Doc
if err := db.Find(&docs).Error; err != nil {
log.Fatal("Failed to get docs:", err)
}
for i := range docs {
translation, ok := docTranslations[docs[i].Slug]
if ok {
docs[i].TitleEn = translation.TitleEn
docs[i].ContentEn = translation.ContentEn
docs[i].SummaryEn = translation.SummaryEn
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"title_en", "content_en", "summary_en"}),
}).Create(&docs[i])
fmt.Printf("Updated doc: %s -> %s\n", docs[i].Title, docs[i].TitleEn)
}
}
fmt.Println("\nDocument translations updated successfully!")
}
+302
View File
@@ -0,0 +1,302 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
var doc model.Doc
if err := database.DB.Where("slug = ?", "api-get-dynamic-code").First(&doc).Error; err != nil {
log.Printf("未找到文档: %v", err)
return
}
newContent := `# 获取动态代码接口
**注意**:动态代码接口需要用户登录认证,需要在请求头中携带JWT token:
Authorization: Bearer {token}
### 接口地址
GET /api/v1/app/:appKey/dynamic-code
### 请求头
Authorization: Bearer {token}
### 路径参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| appKey | string | 是 | 应用密钥 |
### 响应示例
{
"code": 200,
"message": "操作成功",
"data": [
{
"id": 1,
"name": "加法函数",
"key": "add",
"description": "实现数字加法运算",
"status": "active",
"created_at": "2024-01-01T00:00:00Z"
}
]
}
### 字段说明
- id: 动态代码ID
- name: 动态代码名称
- key: 动态代码唯一标识
- description: 描述
- status: 状态(active、inactive
---
## 执行动态代码接口
### 接口地址
POST /api/v1/app/:appKey/dynamic-code/:key/execute
### 请求头
Authorization: Bearer {token}
Content-Type: application/json
### 路径参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| appKey | string | 是 | 应用密钥 |
| key | string | 是 | 动态代码标识 |
### 请求体
{
"params": {
"param1": "value1",
"param2": "value2"
}
}
### 参数说明
- params: 参数对象,键值对形式传递,键名即为JavaScript变量名
### 响应示例
{
"code": 200,
"message": "操作成功",
"data": {
"result": "执行结果",
"execution_time": 1
}
}
### 字段说明
- result: 代码执行结果
- execution_time: 执行时间(毫秒)
---
## JavaScript代码编写规范
### 1. 参数使用
动态代码可以直接使用请求参数中传递的变量,无需额外定义:
a + b
请求参数:{"params": {"a": 10, "b": 20}}
返回结果:30
### 2. 函数定义与调用
可以定义函数并在最后一行调用:
function calculate(x, y) {
return Math.sqrt(x * x + y * y);
}
calculate(x, y)
请求参数:{"params": {"x": 3, "y": 4}}
返回结果:5
### 3. 复杂逻辑
支持完整的JavaScript语法,包括控制流、循环、对象操作等:
function processScore(score) {
if (score >= 90) {
return "优秀";
} else if (score >= 60) {
return "及格";
} else {
return "不及格";
}
}
processScore(score)
### 4. 返回值规则
- **最后一个表达式的值即为返回值**
- **不要在全局作用域使用return语句**
- 可以返回任意类型:数字、字符串、布尔值、对象、数组等
### 5. 常见示例
#### 简单计算
price * quantity
#### 字符串拼接
name + "的年龄是" + age + "岁"
#### 条件判断
score >= 60 ? "及格" : "不及格"
#### 对象返回
{
sum: a + b,
product: a * b,
difference: a - b
}
#### 数组操作
function sumArray(numbers) {
let total = 0;
for (let i = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
sumArray([1, 2, 3, 4, 5])
### 6. 注意事项
1. **全局作用域不能使用return**:return只能在函数内部使用
2. **参数变量直接使用**:无需重新定义
3. **最后一行是返回值**:确保最后一行是需要返回的表达式
4. **支持ES6语法**:可以使用let、const、箭头函数等
5. **内置对象可用**Math、Date、JSON等JavaScript内置对象都可以使用
---
## 错误处理
### 常见错误
#### 1. 语法错误
{
"code": 400,
"message": "代码执行错误: SyntaxError: Unexpected token"
}
#### 2. 运行时错误
{
"code": 400,
"message": "代码执行错误: ReferenceError: x is not defined"
}
#### 3. 全局return错误
{
"code": 400,
"message": "代码执行错误: SyntaxError: Illegal return statement"
}
---
## 完整示例
### 示例1:计算折扣价格
**请求**
POST /api/v1/app/your-app-key/dynamic-code/calculate-discount/execute
{
"params": {
"price": 100,
"discount": 20,
"tax": 10
}
}
**动态代码**
function calculateFinalPrice(price, discount, tax) {
const subtotal = price;
const discountAmount = subtotal * (discount / 100);
const afterDiscount = subtotal - discountAmount;
const taxAmount = afterDiscount * (tax / 100);
return afterDiscount + taxAmount;
}
calculateFinalPrice(price, discount, tax)
**响应**
{
"code": 200,
"message": "操作成功",
"data": {
"result": 88,
"execution_time": 1
}
}
### 示例2:用户信息格式化
**请求**
POST /api/v1/app/your-app-key/dynamic-code/format-user/execute
{
"params": {
"name": "张三",
"age": 25,
"city": "北京"
}
}
**动态代码**
function formatUserInfo(name, age, city) {
const greeting = "你好";
return greeting + ",我是" + name + ",今年" + age + "岁,来自" + city;
}
formatUserInfo(name, age, city)
**响应**
{
"code": 200,
"message": "操作成功",
"data": {
"result": "你好,我是张三,今年25岁,来自北京",
"execution_time": 1
}
}
---
## 性能建议
1. **避免死循环**:确保循环有明确的退出条件
2. **合理使用缓存**:对于重复计算,可以考虑缓存结果
3. **控制代码复杂度**:过复杂的代码可能影响执行效率
4. **使用内置函数**:优先使用JavaScript内置函数,性能更好
---
## 安全建议
1. **不要执行危险操作**:避免访问文件系统、网络请求等
2. **参数验证**:在代码中对参数进行必要的验证
3. **避免敏感信息**:不要在代码中硬编码密钥、密码等敏感信息
4. **限制执行时间**:设置合理的超时时间,防止长时间运行
`
doc.Content = newContent
if err := database.DB.Save(&doc).Error; err != nil {
log.Printf("更新文档失败: %v", err)
return
}
log.Println("动态代码文档更新成功")
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
package main
import (
"log"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
)
func main() {
database.Init()
log.Println("=== 更新云端函数文档 ===")
var doc model.Doc
if err := database.DB.Where("slug = ?", "dynamic-code").First(&doc).Error; err != nil {
log.Printf("未找到文档: %v", err)
return
}
doc.Title = "云端函数接口"
doc.Slug = "cloud-function"
doc.Summary = "云端函数允许开发者为应用创建自定义的业务逻辑,通过JavaScript代码实现灵活的数据处理和业务规则"
if err := database.DB.Save(&doc).Error; err != nil {
log.Printf("更新文档失败: %v", err)
return
}
log.Println("云端函数文档更新成功")
}