优化表结构,重构模型,重新实现基于白银网关的提取节点流程

This commit is contained in:
2025-11-24 18:44:06 +08:00
parent 9a574f55cb
commit cb2a963a37
142 changed files with 6528 additions and 5808 deletions

View File

@@ -1,32 +1,85 @@
## TODO ## TODO
日志记录,使用任务来实现 限制提取单次最大量
支付后事件,需要检测是否已完成操作 支付后异步任务,到时间后需要尝试完成订单,如果无法完成再关闭
重新实现接口 proxy 注册与注销接口:
- 注册时向 redis ports 可用池中加入端口
- 注销时需要同时移除可用池与租约池中的端口(需要考虑具体实现,考虑正在使用的节点归还问题)
otel 检查接口性能
trade/create 性能问题,缩短事务时间,考虑其他方式实现可靠分布式事务
需要确认以下 ID.GenSerial 的分布式并发安全性
### 长期 ### 长期
模型字段修改,特定枚举字段使用自定义类型代替通用 int32 分离 task 的客户端支持多进程prefork 必要!)
调整目录结构:
```
- /core 核心概念
- /util 工具函数
- /models 模型
- /queries 数据库层
- /clients 三方依赖的客户端实例
- /services 服务层
- /auth 认证相关,特化服务
- /app 应用相关,初始化日志,环境变量等
- /http 协议层http 服务
- /cmd 主函数
逐层向上依赖
cmd 调用 app, http 的初始化函数
http 调用 clients 的初始化函数
```
考虑一个方案限制接口请求速率,无侵入更好
冷数据迁移方案
proxy 网关更新接口可以传输更结构化的数据,直接区分不同类型以加快更新速度 proxy 网关更新接口可以传输更结构化的数据,直接区分不同类型以加快更新速度
## 业务逻辑 ## 业务逻辑
### 订单关闭的几种方式 ### 订单关闭的几种方式
1. 创建订单后推送异步任务,到时间后尝试关闭订单 1. 创建订单后推送异步任务,到时间后尝试完成订单或关闭订单
2. sse 接口推送订单状态,轮询尝试完成订单 2. sse 接口推送订单状态,轮询尝试完成订单
3. 异步回调事件,收到支付成功事件后自动完成订单 3. 异步回调事件,收到支付成功事件后自动完成订单
4. 用户退出支付界面,客户端主动发起关闭订单
### 产品字典表 ### 产品字典表
| 代码 | 产品 | | 代码 | 产品 |
|-------|------| | ----- | ------------ |
| short | 短效代理 | | short | 短效动态代理 |
| long | 长效代理 | | long | 长效动态代理 |
## 问题备忘录 ### 节点分配与存储逻辑
### 商福通支付接口的同步跳转参数 添加:
- 检查用户 ip 是否在白名单内
- 取用端口,不够则返回失败
- 将分配结果转写成配置发送到网关
- 保存通道信息和分配记录,其中通道信息以网关为主体,分配记录以用户为主体
- 添加异步任务,当时间结束后释放取用的端口并清空网关配置
部分通道支持这个参数,银盛和汇付不支持这个参数 删除:
- 如果传入用户信息,检查要删除的连接是否属于该用户
- 释放可用端口
- redis 脚本中检查,如果端口所属节点已下线则直接忽略
- 提交清空配置到网关
缩扩容:
- 通过调度任务实现缩扩容
- 每 n 秒检查一次全部配置,按代理分组
- 获取所有代理后备配置
- 后备配置/当前配置
- 当比例 < 1.5 或 > 3 时,重新更新为 2 倍

View File

@@ -1,11 +1,10 @@
package main package main
import ( import (
"strings" m "platform/web/models"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gen" "gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
) )
@@ -26,149 +25,42 @@ func main() {
g = gen.NewGenerator(gen.Config{ g = gen.NewGenerator(gen.Config{
OutPath: "web/queries", OutPath: "web/queries",
ModelPkgPath: "models",
FieldNullable: true, FieldNullable: true,
FieldSignable: true, FieldSignable: true,
FieldWithTypeTag: true, FieldWithTypeTag: true,
Mode: gen.WithDefaultQuery | gen.WithoutContext, Mode: gen.WithDefaultQuery | gen.WithoutContext,
}) })
g.UseDB(db) g.UseDB(db)
g.ApplyBasic(
// 公共参数 m.Admin{},
common := []gen.ModelOpt{ m.AdminRole{},
gen.FieldModify(func(field gen.Field) gen.Field { m.Announcement{},
switch { m.Bill{},
case field.Type == "*time.Time": m.Channel{},
field.Type = "*orm.LocalDateTime" m.Client{},
case field.Type == "time.Time": m.Coupon{},
field.Type = "orm.LocalDateTime" m.Edge{},
case strings.Contains(field.Tags(), "numeric"): m.LinkAdminRole{},
field.Type = "decimal.Decimal" m.LinkAdminRolePermission{},
} m.LinkClientPermission{},
return field m.LinkUserRole{},
}), m.LinkUserRolePermission{},
gen.FieldRename("contact_qq", "ContactQQ"), m.LogsLogin{},
gen.FieldRename("ua", "UA"), m.LogsRequest{},
} m.LogsUserBandwidth{},
m.LogsUserUsage{},
// 生成模型 m.Permission{},
customs := make(map[string]any) m.Product{},
m.Proxy{},
// resource m.Refund{},
resourceShort := g.GenerateModel("resource_short", common...) m.Resource{},
customs["resource_short"] = resourceShort m.ResourceLong{},
m.ResourceShort{},
resourceLong := g.GenerateModel("resource_long", common...) m.Session{},
customs["resource_long"] = resourceLong m.Trade{},
m.User{},
resource := g.GenerateModel("resource", append(common, m.UserRole{},
gen.FieldRelate(field.HasOne, "Short", resourceShort, &field.RelateConfig{ m.Whitelist{},
RelatePointer: true, )
GORMTag: field.GormTag{
"foreignKey": []string{"ResourceID"},
"references": []string{"ID"},
},
}),
gen.FieldRelate(field.HasOne, "Long", resourceLong, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"ResourceID"},
"references": []string{"ID"},
},
}),
)...)
customs["resource"] = resource
// trade
trade := g.GenerateModel("trade", common...)
customs["trade"] = trade
refund := g.GenerateModel("refund", common...)
customs["refund"] = refund
bill := g.GenerateModel("bill", append(common,
gen.FieldRelate(field.BelongsTo, "Trade", trade, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"TradeID"},
},
}),
gen.FieldRelate(field.BelongsTo, "Refund", refund, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"RefundID"},
},
}),
gen.FieldRelate(field.BelongsTo, "Resource", resource, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"ResourceID"},
},
}),
)...)
customs["bill"] = bill
// proxy
edge := g.GenerateModel("edge", common...)
customs["edge"] = edge
proxy := g.GenerateModel("proxy", append(common,
gen.FieldRelate(field.HasMany, "Edges", edge, &field.RelateConfig{
GORMTag: field.GormTag{
"foreignKey": []string{"ProxyID"},
"references": []string{"ID"},
},
}),
)...)
customs["proxy"] = proxy
// session
user := g.GenerateModel("user", common...)
customs["user"] = user
admin := g.GenerateModel("admin", common...)
customs["admin"] = admin
client := g.GenerateModel("client", common...)
customs["client"] = client
session := g.GenerateModel("session", append(common,
gen.FieldRelate(field.BelongsTo, "User", user, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"UserID"},
},
}),
gen.FieldRelate(field.BelongsTo, "Admin", admin, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"AdminID"},
},
}),
gen.FieldRelate(field.BelongsTo, "Client", client, &field.RelateConfig{
RelatePointer: true,
GORMTag: field.GormTag{
"foreignKey": []string{"ClientID"},
"belongsTo": []string{"ID"},
},
}),
)...)
customs["session"] = session
// 生成表结构
tables, _ := db.Migrator().GetTables()
models := make([]any, len(tables))
for i, name := range tables {
if customs[name] != nil {
models[i] = customs[name]
} else {
models[i] = g.GenerateModel(name, common...)
}
}
g.ApplyBasic(models...)
// 生成查询
g.Execute() g.Execute()
} }

View File

@@ -1,4 +1,4 @@
name: server-dev name: lanhu
services: services:
postgres: postgres:
@@ -11,6 +11,7 @@ services:
- "${DB_PORT}:5432" - "${DB_PORT}:5432"
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
- ./scripts/sql/init.sql:/docker-entrypoint-initdb.d/init.sql
restart: unless-stopped restart: unless-stopped
redis: redis:

View File

@@ -5,11 +5,36 @@ import (
"time" "time"
) )
// P 是一个工具函数,用于在表达式内原地创建一个指针 // ====================
// 逻辑
// ====================
func Else[T any](v *T, or T) T {
if v == nil {
return or
} else {
return *v
}
}
func ElseTo[A any, B any](a *A, f func(A) B) *B {
if a == nil {
return nil
} else {
return P(f(*a))
}
}
// ====================
// 指针
// ====================
// P 原地创建值的指针
func P[T any](v T) *T { func P[T any](v T) *T {
return &v return &v
} }
// Z 转换值为不可空,如果值为 nil则返回其零值
func Z[T any](v *T) T { func Z[T any](v *T) T {
if v == nil { if v == nil {
var zero T var zero T
@@ -18,6 +43,7 @@ func Z[T any](v *T) T {
return *v return *v
} }
// X 转换值为可空,如果值为零值,则返回 nil
func X[T comparable](v T) *T { func X[T comparable](v T) *T {
var zero T var zero T
if v == zero { if v == zero {
@@ -26,28 +52,50 @@ func X[T comparable](v T) *T {
return &v return &v
} }
func Today() time.Time { // ====================
var now = time.Now() // 数组
return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) // ====================
}
func Date(date time.Time) time.Time { func Map[T any, R any](src []T, convert func(T) R) []R {
return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) dst := make([]R, len(src))
} for i, item := range src {
dst[i] = convert(item)
func SameDate(date time.Time) bool {
var now = time.Now()
return date.Year() == now.Year() && date.Month() == now.Month() && date.Day() == now.Day()
}
func Or[T any](v *T, or T) T {
if v == nil {
return or
} else {
return *v
} }
return dst
} }
// ====================
// 时间
// ====================
func DateHead(date time.Time) time.Time {
var y, m, d = date.Date()
return time.Date(y, m, d, 0, 0, 0, 0, date.Location())
}
func DateFoot(date time.Time) time.Time {
var y, m, d = date.Date()
return time.Date(y, m, d, 23, 59, 59, 999999999, date.Location())
}
func Today() time.Time {
return DateHead(time.Now())
}
func IsSameDate(date1, date2 time.Time) bool {
var y1, m1, d1 = date1.Date()
var y2, m2, d2 = date2.Date()
return y1 == y2 && m1 == m2 && d1 == d2
}
func IsToday(date time.Time) bool {
return IsSameDate(date, time.Now())
}
// ====================
// 错误
// ====================
func CombineErrors(errs []error) error { func CombineErrors(errs []error) error {
var combinedErr error = nil var combinedErr error = nil
for _, err := range errs { for _, err := range errs {

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"platform/web/core" "platform/web/core"
client2 "platform/web/domains/client"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
@@ -86,8 +85,8 @@ func authBearer(_ context.Context, token string) (*AuthCtx, error) {
} }
scopes := []string{} scopes := []string{}
if session.Scopes_ != nil { if session.Scopes != nil {
scopes = strings.Split(*session.Scopes_, " ") scopes = strings.Split(*session.Scopes, " ")
} }
return &AuthCtx{ return &AuthCtx{
User: session.User, User: session.User,
@@ -138,8 +137,7 @@ func authClient(clientId, clientSecret string) (*m.Client, error) {
} }
// 检查客户端密钥 // 检查客户端密钥
spec := client2.Spec(client.Spec) if client.Spec == m.ClientSpecWeb || client.Spec == m.ClientSpecAPI {
if spec == client2.SpecWeb || spec == client2.SpecApi {
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil { if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
return nil, errors.New("客户端密钥错误") return nil, errors.New("客户端密钥错误")
} }

View File

@@ -10,7 +10,6 @@ import (
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u" "platform/pkg/u"
"platform/web/core" "platform/web/core"
user2 "platform/web/domains/user"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm" "platform/web/globals/orm"
m "platform/web/models" m "platform/web/models"
@@ -162,7 +161,7 @@ func Token(c *fiber.Ctx) error {
AccessToken: session.AccessToken, AccessToken: session.AccessToken,
RefreshToken: u.Z(session.RefreshToken), RefreshToken: u.Z(session.RefreshToken),
ExpiresIn: int(time.Time(session.AccessTokenExpires).Sub(now).Seconds()), ExpiresIn: int(time.Time(session.AccessTokenExpires).Sub(now).Seconds()),
Scope: u.Z(session.Scopes_), Scope: u.Z(session.Scopes),
}) })
} }
@@ -202,7 +201,7 @@ func authAuthorizationCode(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now tim
user, err := q.User.Where( user, err := q.User.Where(
q.User.ID.Eq(codeCtx.UserID), q.User.ID.Eq(codeCtx.UserID),
q.User.Status.Eq(int32(user2.StatusEnabled)), q.User.Status.Eq(int(m.UserStatusEnabled)),
).First() ).First()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -211,18 +210,20 @@ func authAuthorizationCode(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now tim
// todo 检查 scope // todo 检查 scope
// 生成会话 // 生成会话
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
session := &m.Session{ session := &m.Session{
IP: u.X(ctx.IP()), IP: ip,
UA: u.X(ctx.Get(fiber.HeaderUserAgent)), UA: u.X(ua),
UserID: &user.ID, UserID: &user.ID,
ClientID: &auth.Client.ID, ClientID: &auth.Client.ID,
Scopes_: u.P(strings.Join(codeCtx.Scopes, " ")), Scopes: u.P(strings.Join(codeCtx.Scopes, " ")),
AccessToken: uuid.NewString(), AccessToken: uuid.NewString(),
AccessTokenExpires: orm.LocalDateTime(now.Add(time.Duration(env.SessionAccessExpire) * time.Second)), AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
} }
if codeCtx.Remember { if codeCtx.Remember {
session.RefreshToken = u.P(uuid.NewString()) session.RefreshToken = u.P(uuid.NewString())
session.RefreshTokenExpires = u.P(orm.LocalDateTime(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))) session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
} }
err = SaveSession(session) err = SaveSession(session)
@@ -237,12 +238,14 @@ func authClientCredential(ctx *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.T
// todo 检查 scope // todo 检查 scope
// 生成会话 // 生成会话
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
session := &m.Session{ session := &m.Session{
IP: u.X(ctx.IP()), IP: ip,
UA: u.X(ctx.Get(fiber.HeaderUserAgent)), UA: u.X(ua),
ClientID: &auth.Client.ID, ClientID: &auth.Client.ID,
AccessToken: uuid.NewString(), AccessToken: uuid.NewString(),
AccessTokenExpires: orm.LocalDateTime(now.Add(time.Duration(env.SessionAccessExpire) * time.Second)), AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
} }
// 保存会话 // 保存会话
@@ -255,6 +258,9 @@ func authClientCredential(ctx *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.T
} }
func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) { func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) {
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
var user *m.User var user *m.User
err := q.Q.Transaction(func(tx *q.Query) (err error) { err := q.Q.Transaction(func(tx *q.Query) (err error) {
switch req.LoginType { switch req.LoginType {
@@ -267,7 +273,7 @@ func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (
user = &m.User{ user = &m.User{
Phone: req.Username, Phone: req.Username,
Username: u.P(req.Username), Username: u.P(req.Username),
Status: int32(user2.StatusEnabled), Status: m.UserStatusEnabled,
} }
} }
case GrantPasswordEmail: case GrantPasswordEmail:
@@ -285,15 +291,15 @@ func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (
} }
// 账户状态 // 账户状态
if user2.Status(user.Status) == user2.StatusDisabled { if user.Status == m.UserStatusDisabled {
slog.Debug("账户状态异常", "username", req.Username, "status", user.Status) slog.Debug("账户状态异常", "username", req.Username, "status", user.Status)
return core.NewBizErr("账号无法登录") return core.NewBizErr("账号无法登录")
} }
// 更新用户的登录时间 // 更新用户的登录时间
user.LastLogin = u.P(orm.LocalDateTime(time.Now())) user.LastLogin = u.P(time.Now())
user.LastLoginHost = u.X(ctx.IP()) user.LastLoginIP = ip
user.LastLoginAgent = u.X(ctx.Get(fiber.HeaderUserAgent)) user.LastLoginUA = u.X(ua)
if err := tx.User.Save(user); err != nil { if err := tx.User.Save(user); err != nil {
return err return err
} }
@@ -306,17 +312,17 @@ func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (
// 生成会话 // 生成会话
session := &m.Session{ session := &m.Session{
IP: u.X(ctx.IP()), IP: ip,
UA: u.X(ctx.Get(fiber.HeaderUserAgent)), UA: u.X(ua),
UserID: &user.ID, UserID: &user.ID,
ClientID: &auth.Client.ID, ClientID: &auth.Client.ID,
Scopes_: u.X(req.Scope), Scopes: u.X(req.Scope),
AccessToken: uuid.NewString(), AccessToken: uuid.NewString(),
AccessTokenExpires: orm.LocalDateTime(now.Add(time.Duration(env.SessionAccessExpire) * time.Second)), AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
} }
if req.Remember { if req.Remember {
session.RefreshToken = u.P(uuid.NewString()) session.RefreshToken = u.P(uuid.NewString())
session.RefreshTokenExpires = u.P(orm.LocalDateTime(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))) session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
} }
err = SaveSession(session) err = SaveSession(session)
@@ -340,10 +346,10 @@ func authRefreshToken(_ *fiber.Ctx, _ *AuthCtx, req *TokenReq, now time.Time) (*
// 生成令牌 // 生成令牌
session.AccessToken = uuid.NewString() session.AccessToken = uuid.NewString()
session.AccessTokenExpires = orm.LocalDateTime(now.Add(time.Duration(env.SessionAccessExpire) * time.Second)) session.AccessTokenExpires = now.Add(time.Duration(env.SessionAccessExpire) * time.Second)
if session.RefreshToken != nil { if session.RefreshToken != nil {
session.RefreshToken = u.P(uuid.NewString()) session.RefreshToken = u.P(uuid.NewString())
session.RefreshTokenExpires = u.P(orm.LocalDateTime(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))) session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
} }
// 保存令牌 // 保存令牌

View File

@@ -1,7 +1,6 @@
package auth package auth
import ( import (
"platform/web/domains/client"
m "platform/web/models" m "platform/web/models"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@@ -40,8 +39,7 @@ func (a *AuthCtx) PermitSecretClient(scopes ...string) (*AuthCtx, error) {
if a.Client == nil { if a.Client == nil {
return a, ErrAuthenticateForbidden return a, ErrAuthenticateForbidden
} }
spec := client.Spec(a.Client.Spec) if a.Client.Spec != m.ClientSpecAPI && a.Client.Spec != m.ClientSpecWeb {
if spec != client.SpecApi && spec != client.SpecWeb {
return a, ErrAuthenticateForbidden return a, ErrAuthenticateForbidden
} }
if !a.checkScopes(scopes...) { if !a.checkScopes(scopes...) {
@@ -50,16 +48,14 @@ func (a *AuthCtx) PermitSecretClient(scopes ...string) (*AuthCtx, error) {
return a, nil return a, nil
} }
func (a *AuthCtx) PermitInternalClient(scopes ...string) (*AuthCtx, error) { func (a *AuthCtx) PermitOfficialClient(scopes ...string) (*AuthCtx, error) {
if a.Client == nil { if a.Client == nil {
return a, ErrAuthenticateForbidden return a, ErrAuthenticateForbidden
} }
spec := client.Spec(a.Client.Spec) if a.Client.Spec != m.ClientSpecAPI && a.Client.Spec != m.ClientSpecWeb {
if spec != client.SpecApi && spec != client.SpecWeb {
return a, ErrAuthenticateForbidden return a, ErrAuthenticateForbidden
} }
cType := client.Type(a.Client.Type) if a.Client.Type != m.ClientTypeOfficial {
if cType != client.TypeInternal {
return a, ErrAuthenticateForbidden return a, ErrAuthenticateForbidden
} }
if !a.checkScopes(scopes...) { if !a.checkScopes(scopes...) {

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
"time" "time"
@@ -17,7 +16,7 @@ func FindSession(accessToken string, now time.Time) (*m.Session, error) {
Preload(field.Associations). Preload(field.Associations).
Where( Where(
q.Session.AccessToken.Eq(accessToken), q.Session.AccessToken.Eq(accessToken),
q.Session.AccessTokenExpires.Gt(orm.LocalDateTime(now)), q.Session.AccessTokenExpires.Gt(now),
).First() ).First()
} }
@@ -26,7 +25,7 @@ func FindSessionByRefresh(refreshToken string, now time.Time) (*m.Session, error
Preload(field.Associations). Preload(field.Associations).
Where( Where(
q.Session.RefreshToken.Eq(refreshToken), q.Session.RefreshToken.Eq(refreshToken),
q.Session.RefreshTokenExpires.Gt(orm.LocalDateTime(now)), q.Session.RefreshTokenExpires.Gt(now),
).First() ).First()
} }

View File

@@ -6,49 +6,6 @@ import (
"runtime" "runtime"
) )
// region page
type PageReq struct {
RawPage int `json:"page"`
RawSize int `json:"size"`
}
func (p *PageReq) GetPage() int {
if p.RawPage < 1 {
return 1
}
return p.RawPage
}
func (p *PageReq) GetSize() int {
if p.RawSize < 1 {
return 10
}
if p.RawSize > 100 {
return 100
}
return p.RawSize
}
func (p *PageReq) GetOffset() int {
return (p.GetPage() - 1) * p.GetSize()
}
func (p *PageReq) GetLimit() int {
return p.GetSize()
}
type PageResp struct {
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
List any `json:"list"`
}
// endregion
// region error
type Err struct { type Err struct {
msg string msg string
err error err error
@@ -109,5 +66,3 @@ type ServErr struct{ Err }
func NewServErr(msg string, err ...error) *ServErr { func NewServErr(msg string, err ...error) *ServErr {
return &ServErr{newErr(msg, err...)} return &ServErr{newErr(msg, err...)}
} }
// endregion

43
web/core/http.go Normal file
View File

@@ -0,0 +1,43 @@
package core
const HeaderUserIP = "X-Data-IP"
const HeaderUserUA = "X-Data-UA"
// PageReq 分页请求参数
type PageReq struct {
RawPage int `json:"page"`
RawSize int `json:"size"`
}
func (p *PageReq) GetPage() int {
if p.RawPage < 1 {
return 1
}
return p.RawPage
}
func (p *PageReq) GetSize() int {
if p.RawSize < 1 {
return 10
}
if p.RawSize > 100 {
return 100
}
return p.RawSize
}
func (p *PageReq) GetOffset() int {
return (p.GetPage() - 1) * p.GetSize()
}
func (p *PageReq) GetLimit() int {
return p.GetSize()
}
// PageResp 分页响应参数
type PageResp struct {
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
List any `json:"list"`
}

29
web/core/model.go Normal file
View File

@@ -0,0 +1,29 @@
package core
import (
"platform/pkg/u"
"time"
"gorm.io/gorm"
)
type IModel interface {
GetID() int32
}
type Model struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at"`
}
func (m *Model) GetID() int32 {
return m.ID
}
func GetIDs[T IModel](models []T) []int32 {
return u.Map(models, func(m T) int32 {
return m.GetID()
})
}

View File

@@ -1,7 +0,0 @@
package announcement
type Type int32
const (
TypeNormal Type = iota + 1 // 普通公告
)

View File

@@ -1,34 +0,0 @@
package bill
import (
"github.com/shopspring/decimal"
m "platform/web/models"
)
func NewForRecharge(uid int32, billNo string, info string, amount decimal.Decimal, trade *m.Trade) *m.Bill {
return &m.Bill{
UserID: uid,
BillNo: billNo,
TradeID: &trade.ID,
Type: int32(TypeRecharge),
Info: &info,
Amount: amount,
}
}
func NewForConsume(uid int32, billNo string, info string, amount decimal.Decimal, resource *m.Resource, trade ...*m.Trade) *m.Bill {
var bill = &m.Bill{
UserID: uid,
BillNo: billNo,
ResourceID: &resource.ID,
Type: int32(TypeConsume),
Info: &info,
Amount: amount,
}
if len(trade) > 0 {
bill.TradeID = &trade[0].ID
}
return bill
}

View File

@@ -1,9 +0,0 @@
package bill
type Type int32
const (
TypeConsume Type = iota + 1 // 消费
TypeRefund // 退款
TypeRecharge // 充值
)

View File

@@ -1,9 +0,0 @@
package channel
type Protocol int32
const (
ProtocolHttp Protocol = iota + 1
ProtocolHttps
ProtocolSocks5
)

View File

@@ -1,17 +0,0 @@
package client
type Spec int32
const (
SpecNative Spec = iota + 1 // 原生客户端
SpecBrowser // 浏览器客户端
SpecWeb // Web 服务
SpecApi // Api 服务
)
type Type int32
const (
TypeNormal Type = iota // 普通客户端
TypeInternal // 内部客户端
)

View File

@@ -1,9 +0,0 @@
package coupon
type Status int32
const (
StatusUnused = iota // 未使用
StatusUsed // 已使用
StatusExpired // 已过期
)

View File

@@ -1,43 +0,0 @@
package edge
import "strings"
type ISP int32
const (
IspUnknown ISP = iota // 未知
IspChinaTelecom // 中国电信
IspChinaUnicom // 中国联通
IspChinaMobile // 中国移动
)
func ISPFromStr(str string) ISP {
switch {
case strings.Contains(str, "电信"):
return IspChinaTelecom
case strings.Contains(str, "联通"):
return IspChinaUnicom
case strings.Contains(str, "移动"):
return IspChinaMobile
}
return IspUnknown
}
func (isp ISP) String() string {
switch isp {
case IspChinaTelecom:
return "电信"
case IspChinaUnicom:
return "联通"
case IspChinaMobile:
return "移动"
default:
return "未知"
}
}
type Type int32
const (
TypeSelfHosted Type = iota + 1 // 自建节点
)

View File

@@ -1,8 +0,0 @@
package proxy
type Type int32
const (
TypeThirdParty Type = iota + 1 // 三方代理
TypeSelfHosted // 自建代理
)

View File

@@ -1,9 +0,0 @@
package refund
type Status int32
const (
StatusHandling Status = iota + 1 // 待处理
StatusSuccess // 已退款
StatusRefused // 已拒绝
)

View File

@@ -1,15 +0,0 @@
package resource
type Type int32
const (
TypeShort Type = iota + 1 // 短效动态
TypeLong // 长效动态
)
type Mode int32
const (
ModeTime Mode = iota + 1 // 包时
ModeCount // 包量
)

View File

@@ -1,60 +0,0 @@
package trade
import (
m "platform/web/models"
"github.com/shopspring/decimal"
)
type Type int32
const (
TypePurchase Type = iota + 1 // 购买
TypeRecharge // 充值
)
type Method int32
const (
MethodAlipay Method = iota + 1 // 支付宝
MethodWeChat // 微信
MethodSft // 商福通
MethodSftAlipay // 商福通渠道指定支付宝
MethodSftWeChat // 商福通渠道指定微信
)
type Platform int32
const (
PlatformDesktop Platform = iota + 1 // 桌面网站
PlatformMobile // 手机网站
)
type Acquirer int32
const (
AcquirerAlipay Acquirer = iota + 1 // 支付宝
AcquirerWeChat // 微信
AcquirerUnionPay // 银联
)
type Status int32
const (
StatusPending Status = iota // 待支付
StatusSuccess // 已支付
StatusCanceled // 已取消
)
type ProductInfo interface {
GetType() Type
GetSubject() string
GetAmount() decimal.Decimal
Serialize() (string, error)
Deserialize(str string) error
}
type CompleteEvent interface {
Check(t Type) (ProductInfo, bool)
OnTradeComplete(info ProductInfo, trade *m.Trade) error
}

View File

@@ -1,16 +0,0 @@
package user
type IdType int32
const (
IdTypeNone IdType = iota // 未认证
IdTypePersonal // 个人认证
IdTypeEnterprise // 企业认证
)
type Status int32
const (
StatusDisabled Status = iota // 禁用
StatusEnabled // 启用
)

View File

@@ -2,14 +2,20 @@ package events
import ( import (
"encoding/json" "encoding/json"
"github.com/hibiken/asynq"
"log/slog" "log/slog"
"github.com/hibiken/asynq"
) )
const RemoveChannel = "channel:remove" const RemoveChannel = "channel:remove"
func NewRemoveChannel(ids []int32) *asynq.Task { type RemoveChannelData struct {
bytes, err := json.Marshal(ids) Batch string `json:"batch"`
IDs []int32 `json:"ids"`
}
func NewRemoveChannel(data RemoveChannelData) *asynq.Task {
bytes, err := json.Marshal(data)
if err != nil { if err != nil {
slog.Error("序列化删除通道任务失败", "error", err) slog.Error("序列化删除通道任务失败", "error", err)
return nil return nil

9
web/events/proxy.go Normal file
View File

@@ -0,0 +1,9 @@
package events
import "github.com/hibiken/asynq"
const FlushGateway = "gateway:flush"
func NewFlushGateway() *asynq.Task {
return asynq.NewTask(FlushGateway, nil)
}

View File

@@ -3,7 +3,8 @@ package events
import ( import (
"encoding/json" "encoding/json"
"log/slog" "log/slog"
trade2 "platform/web/domains/trade"
m "platform/web/models"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
) )
@@ -12,7 +13,7 @@ const CancelTrade = "trade:update"
type CancelTradeData struct { type CancelTradeData struct {
TradeNo string `json:"trade_no" validate:"required"` TradeNo string `json:"trade_no" validate:"required"`
Method trade2.Method `json:"method" validate:"required"` Method m.TradeMethod `json:"method" validate:"required"`
} }
func NewCancelTrade(data CancelTradeData) *asynq.Task { func NewCancelTrade(data CancelTradeData) *asynq.Task {

32
web/globals/orm/inet.go Normal file
View File

@@ -0,0 +1,32 @@
package orm
import (
"database/sql/driver"
"fmt"
"net/netip"
)
type Inet struct {
netip.Addr
}
func (inet Inet) Value() (driver.Value, error) {
return inet.MarshalBinary()
}
func (inet *Inet) Scan(value any) (err error) {
switch value := value.(type) {
case []byte:
return inet.UnmarshalBinary(value)
default:
return fmt.Errorf("不支持的类型: %T", value)
}
}
func ParseInet(ip string) (*Inet, error) {
addr, err := netip.ParseAddr(ip)
if err != nil {
return nil, err
}
return &Inet{addr}, nil
}

View File

@@ -20,8 +20,7 @@ var formats = []string{
"2006-01-02", "2006-01-02",
} }
//goland:noinspection GoMixedReceiverTypes func (ldt *LocalDateTime) Scan(value any) (err error) {
func (ldt *LocalDateTime) Scan(value interface{}) (err error) {
var t time.Time var t time.Time
if strValue, ok := value.(string); ok { if strValue, ok := value.(string); ok {
var timeValue time.Time var timeValue time.Time
@@ -50,35 +49,26 @@ func (ldt *LocalDateTime) Scan(value interface{}) (err error) {
return return
} }
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) Value() (driver.Value, error) { func (ldt LocalDateTime) Value() (driver.Value, error) {
return time.Time(ldt).Local(), nil return time.Time(ldt).Local(), nil
} }
// GormDataType gorm common data type
//
//goland:noinspection GoMixedReceiverTy
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) GormDataType() string { func (ldt LocalDateTime) GormDataType() string {
return "ldt" return "ldt"
} }
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) GobEncode() ([]byte, error) { func (ldt LocalDateTime) GobEncode() ([]byte, error) {
return time.Time(ldt).GobEncode() return time.Time(ldt).GobEncode()
} }
//goland:noinspection GoMixedReceiverTypes
func (ldt *LocalDateTime) GobDecode(b []byte) error { func (ldt *LocalDateTime) GobDecode(b []byte) error {
return (*time.Time)(ldt).GobDecode(b) return (*time.Time)(ldt).GobDecode(b)
} }
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) MarshalJSON() ([]byte, error) { func (ldt LocalDateTime) MarshalJSON() ([]byte, error) {
return time.Time(ldt).MarshalJSON() return time.Time(ldt).MarshalJSON()
} }
//goland:noinspection GoMixedReceiverTypes
func (ldt *LocalDateTime) UnmarshalJSON(b []byte) error { func (ldt *LocalDateTime) UnmarshalJSON(b []byte) error {
return (*time.Time)(ldt).UnmarshalJSON(b) return (*time.Time)(ldt).UnmarshalJSON(b)
} }

24
web/globals/orm/slice.go Normal file
View File

@@ -0,0 +1,24 @@
package orm
import (
"database/sql/driver"
"encoding/json"
"fmt"
)
type Slice[T any] struct {
Arr []T
}
func (s Slice[T]) Value() (driver.Value, error) {
return json.Marshal(s)
}
func (s *Slice[T]) Scan(value any) error {
switch value := value.(type) {
case []byte:
return json.Unmarshal(value, s)
default:
return fmt.Errorf("不支持的类型: %T", value)
}
}

View File

@@ -3,7 +3,6 @@ package handlers
import ( import (
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
"platform/web/globals/orm"
q "platform/web/queries" q "platform/web/queries"
"time" "time"
@@ -39,13 +38,13 @@ func ListBill(c *fiber.Ctx) error {
Where(q.Bill.UserID.Eq(authCtx.User.ID)) Where(q.Bill.UserID.Eq(authCtx.User.ID))
if req.Type != nil { if req.Type != nil {
do.Where(q.Bill.Type.Eq(int32(*req.Type))) do.Where(q.Bill.Type.Eq(int(*req.Type)))
} }
if req.CreateAfter != nil { if req.CreateAfter != nil {
do.Where(q.Bill.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter))) do.Where(q.Bill.CreatedAt.Gte(*req.CreateAfter))
} }
if req.CreateBefore != nil { if req.CreateBefore != nil {
do.Where(q.Bill.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore))) do.Where(q.Bill.CreatedAt.Lte(*req.CreateBefore))
} }
if req.BillNo != nil && *req.BillNo != "" { if req.BillNo != nil && *req.BillNo != "" {
do.Where(q.Bill.BillNo.Eq(*req.BillNo)) do.Where(q.Bill.BillNo.Eq(*req.BillNo))

View File

@@ -1,11 +1,11 @@
package handlers package handlers
import ( import (
"fmt" "net/netip"
"platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
channel2 "platform/web/domains/channel" m "platform/web/models"
"platform/web/globals/orm"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"time" "time"
@@ -40,18 +40,18 @@ func ListChannels(c *fiber.Ctx) error {
Where(q.Channel.UserID.Eq(authContext.User.ID)) Where(q.Channel.UserID.Eq(authContext.User.ID))
switch req.AuthType { switch req.AuthType {
case s.ChannelAuthTypeIp: case s.ChannelAuthTypeIp:
cond.Where(q.Channel.AuthIP.Is(true)) cond.Where(q.Channel.Whitelists.IsNotNull())
case s.ChannelAuthTypePass: case s.ChannelAuthTypePass:
cond.Where(q.Channel.AuthPass.Is(true)) cond.Where(q.Channel.Username.IsNotNull(), q.Channel.Password.IsNotNull())
default: default:
break break
} }
if req.ExpireAfter != nil { if req.ExpireAfter != nil {
cond.Where(q.Channel.Expiration.Gte(orm.LocalDateTime(*req.ExpireAfter))) cond.Where(q.Channel.ExpiredAt.Gte(*req.ExpireAfter))
} }
if req.ExpireBefore != nil { if req.ExpireBefore != nil {
cond.Where(q.Channel.Expiration.Lte(orm.LocalDateTime(*req.ExpireBefore))) cond.Where(q.Channel.ExpiredAt.Lte(*req.ExpireBefore))
} }
// 查询数据 // 查询数据
@@ -92,19 +92,19 @@ func ListChannels(c *fiber.Ctx) error {
type CreateChannelReq struct { type CreateChannelReq struct {
ResourceId int32 `json:"resource_id" validate:"required"` ResourceId int32 `json:"resource_id" validate:"required"`
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"` AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
Protocol channel2.Protocol `json:"protocol" validate:"required"` Protocol int `json:"protocol" validate:"required"`
Count int `json:"count" validate:"required"` Count int `json:"count" validate:"required"`
Prov string `json:"prov"` Prov *string `json:"prov"`
City string `json:"city"` City *string `json:"city"`
Isp string `json:"isp"` Isp *int `json:"isp"`
} }
type CreateChannelRespItem struct { type CreateChannelRespItem struct {
Proto channel2.Protocol `json:"-"` Proto int `json:"-"`
Host string `json:"host"` Host string `json:"host"`
Port int32 `json:"port"` Port uint16 `json:"port"`
Username *string `json:"username,omitempty"` Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"` Password *string `json:"password,omitempty"`
} }
func CreateChannel(c *fiber.Ctx) error { func CreateChannel(c *fiber.Ctx) error {
@@ -115,48 +115,32 @@ func CreateChannel(c *fiber.Ctx) error {
return err return err
} }
// 检查用户其他权限
user := authCtx.User user := authCtx.User
if user.IDToken == nil || *user.IDToken == "" { if user.IDToken == nil || *user.IDToken == "" {
return fiber.NewError(fiber.StatusForbidden, "账号未实名") return fiber.NewError(fiber.StatusForbidden, "账号未实名")
} }
count, err := q.Whitelist.Where( // 解析参数
q.Whitelist.UserID.Eq(user.ID),
q.Whitelist.Host.Eq(c.IP()),
).Count()
if err != nil {
return err
}
if count == 0 {
return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("非白名单IP %s", c.IP()))
}
req := new(CreateChannelReq) req := new(CreateChannelReq)
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {
return err return err
} }
var isp string ip, err := netip.ParseAddr(c.Get(core.HeaderUserIP))
switch req.Isp { if err != nil {
case "1": return core.NewBizErr("解析请求头客户端 IP 地址失败", err)
isp = "电信"
case "2":
isp = "联通"
case "3":
isp = "移动"
} }
// 创建通道 // 创建通道
result, err := s.Channel.CreateChannel( result, err := s.Channel.CreateChannels(
c, ip,
user.ID, user.ID,
req.ResourceId, req.ResourceId,
req.Protocol, req.AuthType == s.ChannelAuthTypeIp,
req.AuthType, req.AuthType == s.ChannelAuthTypePass,
req.Count, req.Count,
s.EdgeFilter{ s.EdgeFilter{
Isp: isp, Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
Prov: req.Prov, Prov: req.Prov,
City: req.City, City: req.City,
}, },
@@ -170,8 +154,8 @@ func CreateChannel(c *fiber.Ctx) error {
for i, channel := range result { for i, channel := range result {
resp[i] = &CreateChannelRespItem{ resp[i] = &CreateChannelRespItem{
Proto: req.Protocol, Proto: req.Protocol,
Host: channel.ProxyHost, Host: channel.Proxy.IP.String(),
Port: channel.ProxyPort, Port: channel.Port,
} }
if req.AuthType == s.ChannelAuthTypePass { if req.AuthType == s.ChannelAuthTypePass {
resp[i].Username = channel.Username resp[i].Username = channel.Username
@@ -188,12 +172,13 @@ type CreateChannelResultType string
// region RemoveChannels // region RemoveChannels
type RemoveChannelsReq struct { type RemoveChannelsReq struct {
ByIds []int32 `json:"by_ids" validate:"required"` Batch string `json:"batch" validate:"required"`
Ids []int32 `json:"ids" validate:"required"`
} }
func RemoveChannels(c *fiber.Ctx) error { func RemoveChannels(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() _, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil { if err != nil {
return err return err
} }
@@ -205,7 +190,7 @@ func RemoveChannels(c *fiber.Ctx) error {
} }
// 删除通道 // 删除通道
err = s.Channel.RemoveChannels(req.ByIds, authCtx.User.ID) err = s.Channel.RemoveChannels(req.Batch, req.Ids)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,20 +1,8 @@
package handlers package handlers
import ( import (
"errors"
"log/slog"
"platform/pkg/u"
"platform/web/auth"
edge2 "platform/web/domains/edge"
proxy2 "platform/web/domains/proxy"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"gorm.io/gen/field"
"gorm.io/gorm"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -29,80 +17,82 @@ type RegisterEdgeResp struct {
} }
func AssignEdge(c *fiber.Ctx) (err error) { func AssignEdge(c *fiber.Ctx) (err error) {
return c.JSON(map[string]any{
// 验证请求参数 "error": "接口暂不可用",
var req = new(RegisterEdgeReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 全局锁,防止并发注册
var mutex = g.Redsync.NewMutex("edge:discovery")
if err := mutex.Lock(); err != nil {
return errors.New("服务繁忙,请稍后重试")
}
defer func() {
if ok, err := mutex.Unlock(); err != nil {
slog.Error("解锁失败", slog.Bool("ok", ok), slog.Any("err", err))
}
}()
// 检查节点
var fwd *m.Proxy
var edge *m.Edge
edge, err = q.Edge.
Where(q.Edge.Name.Eq(req.Name)).
Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
// 挑选合适的转发服务
fwd, err = q.Proxy.
LeftJoin(q.Edge, q.Edge.ProxyID.EqCol(q.Proxy.ID), q.Edge.Status.Eq(1)).
Select(q.Proxy.ALL, q.Edge.ALL.Count().As("count")).
Where(q.Proxy.Type.Eq(int32(proxy2.TypeSelfHosted))).
Group(q.Proxy.ID).
Order(field.NewField("", "count").Desc()).
First()
if err != nil {
return err
}
// 保存节点信息
edge = &m.Edge{
Name: req.Name,
Version: int32(req.Version),
Type: int32(edge2.TypeSelfHosted),
ProxyID: &fwd.ID,
}
err = q.Edge.Create(edge)
if err != nil {
return err
}
} else if err == nil {
// 获取已配置的转发服务
fwd, err = q.Proxy.
Where(q.Proxy.ID.Eq(*edge.ProxyID)).
Take()
if err != nil {
return err
}
// 节点已存在,更新版本号
if edge.Version < int32(req.Version) {
_, err = q.Edge.
Where(q.Edge.ID.Eq(edge.ID)).
UpdateSimple(q.Edge.Version.Value(int32(req.Version)))
if err != nil {
return err
}
}
} else {
return err
}
// 返回服务地址
return c.JSON(RegisterEdgeResp{
Id: edge.ID,
Host: fwd.Host,
}) })
// // 验证请求参数
// var req = new(RegisterEdgeReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 全局锁,防止并发注册
// var mutex = g.Redsync.NewMutex("edge:discovery")
// if err := mutex.Lock(); err != nil {
// return errors.New("服务繁忙,请稍后重试")
// }
// defer func() {
// if ok, err := mutex.Unlock(); err != nil {
// slog.Error("解锁失败", slog.Bool("ok", ok), slog.Any("err", err))
// }
// }()
// // 检查节点
// var fwd *m.Proxy
// var edge *m.Edge
// edge, err = q.Edge.
// Where(q.Edge.Mac.Eq(req.Name)).
// Take()
// if errors.Is(err, gorm.ErrRecordNotFound) {
// // 挑选合适的转发服务
// fwd, err = q.Proxy.
// LeftJoin(q.Edge, q.Edge.ProxyID.EqCol(q.Proxy.ID), q.Edge.Status.Eq(1)).
// Select(q.Proxy.ALL, q.Edge.ALL.Count().As("count")).
// Where(q.Proxy.Type.Eq(int32(proxy2.TypeSelfHosted))).
// Group(q.Proxy.ID).
// Order(field.NewField("", "count").Desc()).
// First()
// if err != nil {
// return err
// }
// // 保存节点信息
// edge = &m.Edge{
// Name: req.Name,
// Version: int32(req.Version),
// Type: int32(edge2.TypeSelfHosted),
// ProxyID: &fwd.ID,
// }
// err = q.Edge.Create(edge)
// if err != nil {
// return err
// }
// } else if err == nil {
// // 获取已配置的转发服务
// fwd, err = q.Proxy.
// Where(q.Proxy.ID.Eq(*edge.ProxyID)).
// Take()
// if err != nil {
// return err
// }
// // 节点已存在,更新版本号
// if edge.Version < int32(req.Version) {
// _, err = q.Edge.
// Where(q.Edge.ID.Eq(edge.ID)).
// UpdateSimple(q.Edge.Version.Value(int32(req.Version)))
// if err != nil {
// return err
// }
// }
// } else {
// return err
// }
// // 返回服务地址
// return c.JSON(RegisterEdgeResp{
// Id: edge.ID,
// Host: fwd.Host,
// })
} }
type AllEdgesAvailableReq struct { type AllEdgesAvailableReq struct {
@@ -120,36 +110,40 @@ type AllEdgesAvailableRespItem struct {
} }
func AllEdgesAvailable(c *fiber.Ctx) (err error) { func AllEdgesAvailable(c *fiber.Ctx) (err error) {
// 检查权限 return c.JSON(map[string]any{
_, err = auth.GetAuthCtx(c).PermitSecretClient() "error": "接口暂不可用",
if err != nil { })
return err
}
// 验证请求参数 // // 检查权限
var req = new(AllEdgesAvailableReq) // _, err = auth.GetAuthCtx(c).PermitSecretClient()
err = g.Validator.Validate(c, req) // if err != nil {
if err != nil { // return err
return err // }
}
// 获取可用的转发服务 // // 验证请求参数
infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter) // var req = new(AllEdgesAvailableReq)
if err != nil { // err = g.Validator.Validate(c, req)
return err // if err != nil {
} // return err
// }
// 返回结果 // // 获取可用的转发服务
var edges = make([]AllEdgesAvailableRespItem, len(infos)) // infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter)
for i, info := range infos { // if err != nil {
edges[i] = AllEdgesAvailableRespItem{ // return err
Ip: info.Host, // }
Port: u.Z(info.ProxyPort),
Isp: edge2.ISP(info.Isp).String(), // // 返回结果
Prov: info.Prov, // var edges = make([]AllEdgesAvailableRespItem, len(infos))
City: info.City, // for i, info := range infos {
Status: info.Status, // edges[i] = AllEdgesAvailableRespItem{
} // Ip: info.Host,
} // Port: u.Z(info.ProxyPort),
return c.JSON(edges) // Isp: edge2.ISP(info.Isp).String(),
// Prov: info.Prov,
// City: info.City,
// Status: info.Status,
// }
// }
// return c.JSON(edges)
} }

View File

@@ -24,7 +24,7 @@ import (
// region Identify // region Identify
type IdentifyReq struct { type IdentifyReq struct {
Type int32 `json:"type" validate:"required,oneof=1 2"` Type int `json:"type" validate:"required,oneof=1 2"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
IdenNo string `json:"iden_no" validate:"required"` IdenNo string `json:"iden_no" validate:"required"`
} }
@@ -173,7 +173,7 @@ func IdentifyCallback(c *fiber.Ctx) error {
q.User.Name, q.User.Name,
). ).
Updates(m.User{ Updates(m.User{
IDType: info.Type, IDType: m.UserIDType(info.Type),
IDNo: &info.IdNo, IDNo: &info.IdNo,
IDToken: &info.Token, IDToken: &info.Token,
Name: &info.Name, Name: &info.Name,
@@ -202,7 +202,7 @@ func idenKey(id string) string {
type idenInfo struct { type idenInfo struct {
Uid int32 `json:"uid"` Uid int32 `json:"uid"`
Type int32 `json:"type"` Type int `json:"type"`
Name string `json:"name"` Name string `json:"name"`
IdNo string `json:"id_no"` IdNo string `json:"id_no"`
Token string `json:"token"` Token string `json:"token"`

View File

@@ -1,26 +1,9 @@
package handlers package handlers
import ( import (
"crypto/rand"
"encoding/base32"
"fmt"
"log/slog"
"platform/pkg/u"
auth2 "platform/web/auth"
"platform/web/core"
edge2 "platform/web/domains/edge"
proxy2 "platform/web/domains/proxy"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
"strings"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
) )
// region 报告上线 // region 报告上线
@@ -38,103 +21,106 @@ type ProxyReportOnlineResp struct {
} }
func ProxyReportOnline(c *fiber.Ctx) (err error) { func ProxyReportOnline(c *fiber.Ctx) (err error) {
return c.JSON(map[string]any{
// 检查接口权限 "error": "接口暂不可用",
_, err = auth2.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
// 验证请求参数
var req = new(ProxyReportOnlineReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 创建代理
var ip = c.Context().RemoteIP()
var secretBytes = make([]byte, 16)
if _, err := rand.Read(secretBytes); err != nil {
return err
}
var secret = base32.StdEncoding.
WithPadding(base32.NoPadding).
EncodeToString(secretBytes)
slog.Debug("生成随机密钥", "ip", ip, "secret", secret)
var proxy = &m.Proxy{
Name: req.Name,
Version: int32(req.Version),
Type: int32(proxy2.TypeSelfHosted),
Host: ip.String(),
Secret: &secret,
Status: 1,
}
err = q.Proxy.
Clauses(clause.OnConflict{
UpdateAll: true,
Columns: []clause.Column{
{Name: q.Proxy.Name.ColumnName().String()},
},
}).
Create(proxy)
if err != nil {
return err
}
// 获取边缘节点信息
data, err := q.Edge.Where(
q.Edge.ProxyID.Eq(proxy.ID),
).Find()
if err != nil {
return err
}
edges := make([]*ProxyEdge, len(data))
for i, edge := range data {
edges[i] = &ProxyEdge{
Id: edge.ID,
Port: edge.ProxyPort,
Prov: &edge.Prov,
City: &edge.City,
Isp: u.P(edge2.ISP(edge.Isp).String()),
Status: &edge.Status,
Loss: edge.Loss,
Rtt: edge.Rtt,
}
}
// 获取许可配置
channels, err := q.Channel.Where(
q.Channel.ProxyID.Eq(proxy.ID),
q.Channel.Expiration.Gt(orm.LocalDateTime(time.Now())),
).Find()
if err != nil {
return err
}
var permits = make([]*ProxyPermit, len(channels))
for i, channel := range channels {
if channel.EdgeID == nil {
return core.NewBizErr(fmt.Sprintf("权限解析异常通道缺少边缘节点ID %d", channel.ID))
}
permits[i] = &ProxyPermit{
Id: *channel.EdgeID,
Expire: time.Time(channel.Expiration),
Whitelists: u.P(strings.Split(u.Z(channel.Whitelists), ",")),
Username: channel.Username,
Password: channel.Password,
}
}
slog.Debug("注册转发服务", "ip", ip, "id", proxy.ID)
return c.JSON(&ProxyReportOnlineResp{
Id: proxy.ID,
Secret: secret,
Edges: edges,
Permits: permits,
}) })
// // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// // 验证请求参数
// var req = new(ProxyReportOnlineReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 创建代理
// var ip = c.Context().RemoteIP()
// var secretBytes = make([]byte, 16)
// if _, err := rand.Read(secretBytes); err != nil {
// return err
// }
// var secret = base32.StdEncoding.
// WithPadding(base32.NoPadding).
// EncodeToString(secretBytes)
// slog.Debug("生成随机密钥", "ip", ip, "secret", secret)
// var proxy = &m.Proxy{
// Mac: req.Name,
// Version: int32(req.Version),
// Type: m.ProxyTypeSelfHosted,
// IP: ip,
// Secret: &secret,
// Status: 1,
// }
// err = q.Proxy.
// Clauses(clause.OnConflict{
// UpdateAll: true,
// Columns: []clause.Column{
// {Name: q.Proxy.Mac.ColumnName().String()},
// },
// }).
// Create(proxy)
// if err != nil {
// return err
// }
// // 获取边缘节点信息
// data, err := q.Edge.Where(
// q.Edge.ProxyID.Eq(proxy.ID),
// ).Find()
// if err != nil {
// return err
// }
// edges := make([]*ProxyEdge, len(data))
// for i, edge := range data {
// edges[i] = &ProxyEdge{
// Id: edge.ID,
// Port: edge.ProxyPort,
// Prov: &edge.Prov,
// City: &edge.City,
// Isp: u.P(edge2.ISP(edge.Isp).String()),
// Status: &edge.Status,
// Loss: edge.Loss,
// Rtt: edge.Rtt,
// }
// }
// // 获取许可配置
// channels, err := q.Channel.Where(
// q.Channel.ProxyID.Eq(proxy.ID),
// q.Channel.Expiration.Gt(orm.LocalDateTime(time.Now())),
// ).Find()
// if err != nil {
// return err
// }
// var permits = make([]*ProxyPermit, len(channels))
// for i, channel := range channels {
// if channel.EdgeID == nil {
// return core.NewBizErr(fmt.Sprintf("权限解析异常通道缺少边缘节点ID %d", channel.ID))
// }
// permits[i] = &ProxyPermit{
// Id: *channel.EdgeID,
// Expire: time.Time(channel.Expiration),
// Whitelists: u.P(strings.Split(u.Z(channel.Whitelists), ",")),
// Username: channel.Username,
// Password: channel.Password,
// }
// }
// slog.Debug("注册转发服务", "ip", ip, "id", proxy.ID)
// return c.JSON(&ProxyReportOnlineResp{
// Id: proxy.ID,
// Secret: secret,
// Edges: edges,
// Permits: permits,
// })
} }
// endregion // endregion
@@ -146,36 +132,40 @@ type ProxyReportOfflineReq struct {
} }
func ProxyReportOffline(c *fiber.Ctx) (err error) { func ProxyReportOffline(c *fiber.Ctx) (err error) {
// 检查接口权限 return c.JSON(map[string]any{
_, err = auth2.GetAuthCtx(c).PermitSecretClient() "error": "接口暂不可用",
if err != nil { })
return err
}
// 验证请求参数 // // 检查接口权限
var req = new(ProxyReportOfflineReq) // _, err = auth2.GetAuthCtx(c).PermitSecretClient()
err = g.Validator.Validate(c, req) // if err != nil {
if err != nil { // return err
return err // }
}
// 下线转发服务 // // 验证请求参数
_, err = q.Proxy. // var req = new(ProxyReportOfflineReq)
Where(q.Proxy.ID.Eq(req.Id)). // err = g.Validator.Validate(c, req)
UpdateSimple(q.Proxy.Status.Value(0)) // if err != nil {
if err != nil { // return err
return err // }
}
// 下线所有相关的边缘节点 // // 下线转发服务
_, err = q.Edge. // _, err = q.Proxy.
Where(q.Edge.ProxyID.Eq(req.Id)). // Where(q.Proxy.ID.Eq(req.Id)).
UpdateSimple(q.Edge.Status.Value(0)) // UpdateSimple(q.Proxy.Status.Value(0))
if err != nil { // if err != nil {
return err // return err
} // }
return nil // // 下线所有相关的边缘节点
// _, err = q.Edge.
// Where(q.Edge.ProxyID.Eq(req.Id)).
// UpdateSimple(q.Edge.Status.Value(0))
// if err != nil {
// return err
// }
// return nil
} }
// endregion // endregion
@@ -188,157 +178,161 @@ type ProxyReportUpdateReq struct {
} }
func ProxyReportUpdate(c *fiber.Ctx) (err error) { func ProxyReportUpdate(c *fiber.Ctx) (err error) {
// 检查接口权限 return c.JSON(map[string]any{
_, err = auth2.GetAuthCtx(c).PermitSecretClient() "error": "接口暂不可用",
if err != nil {
return err
}
// 验证请求参数
var req = new(ProxyReportUpdateReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 更新节点信息
var idsActive = make([]int32, 0, len(req.Edges))
var idsInactive = make([]int32, 0, len(req.Edges))
var idsIspUnknown = make([]int32, 0, len(req.Edges))
var idsIspTelecom = make([]int32, 0, len(req.Edges))
var idsIspUnicom = make([]int32, 0, len(req.Edges))
var idsIspMobile = make([]int32, 0, len(req.Edges))
var otherEdges = make([]*ProxyEdge, 0, len(req.Edges))
for _, edge := range req.Edges {
// 检查更新ISP
if edge.Isp != nil {
switch edge2.ISPFromStr(*edge.Isp) {
case edge2.IspUnknown:
idsIspUnknown = append(idsIspUnknown, edge.Id)
case edge2.IspChinaTelecom:
idsIspTelecom = append(idsIspTelecom, edge.Id)
case edge2.IspChinaUnicom:
idsIspUnicom = append(idsIspUnicom, edge.Id)
case edge2.IspChinaMobile:
idsIspMobile = append(idsIspMobile, edge.Id)
}
}
// 检查更新状态
if edge.Status != nil {
if *edge.Status == 1 {
idsActive = append(idsActive, edge.Id)
} else {
idsInactive = append(idsInactive, edge.Id)
}
}
// 无法分类更新
if edge.Host != nil || edge.Port != nil || edge.Prov != nil || edge.City != nil {
otherEdges = append(otherEdges, edge)
continue
}
}
slog.Debug("更新边缘节点信息",
"active", len(idsActive),
"inactive", len(idsInactive),
"isp_unknown", len(idsIspUnknown),
"isp_telecom", len(idsIspTelecom),
"isp_unicom", len(idsIspUnicom),
"isp_mobile", len(idsIspMobile),
"other_edges", len(otherEdges),
)
err = q.Q.Transaction(func(q *q.Query) error {
// 更新边缘节点状态
if len(idsActive) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsActive...)).
UpdateSimple(q.Edge.Status.Value(1))
if err != nil {
return err
}
}
if len(idsInactive) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsInactive...)).
UpdateSimple(q.Edge.Status.Value(0))
if err != nil {
return err
}
}
// 更新边缘节点ISP
if len(idsIspUnknown) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspUnknown...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspUnknown)))
if err != nil {
return err
}
}
if len(idsIspTelecom) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspTelecom...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaTelecom)))
if err != nil {
return err
}
}
if len(idsIspUnicom) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspUnicom...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaUnicom)))
if err != nil {
return err
}
}
if len(idsIspMobile) > 0 {
_, err = q.Edge.Debug().
Where(q.Edge.ID.In(idsIspMobile...)).
UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaMobile)))
if err != nil {
return err
}
}
// 更新其他边缘节点信息
for _, edge := range otherEdges {
do := q.Edge.Debug().Where(q.Edge.ID.Eq(edge.Id))
var assigns = make([]field.AssignExpr, 0, 5)
if edge.Host != nil {
assigns = append(assigns, q.Edge.Host.Value(*edge.Host))
}
if edge.Port != nil {
assigns = append(assigns, q.Edge.ProxyPort.Value(*edge.Port))
}
if edge.Prov != nil {
assigns = append(assigns, q.Edge.Prov.Value(*edge.Prov))
}
if edge.City != nil {
assigns = append(assigns, q.Edge.City.Value(*edge.City))
}
// 更新边缘节点
_, err := do.UpdateSimple(assigns...)
if err != nil {
return fmt.Errorf("更新边缘节点 %d 失败: %w", edge.Id, err)
}
}
return nil
}) })
if err != nil {
return err
}
return nil // // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// // 验证请求参数
// var req = new(ProxyReportUpdateReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// // 更新节点信息
// var idsActive = make([]int32, 0, len(req.Edges))
// var idsInactive = make([]int32, 0, len(req.Edges))
// var idsIspUnknown = make([]int32, 0, len(req.Edges))
// var idsIspTelecom = make([]int32, 0, len(req.Edges))
// var idsIspUnicom = make([]int32, 0, len(req.Edges))
// var idsIspMobile = make([]int32, 0, len(req.Edges))
// var otherEdges = make([]*ProxyEdge, 0, len(req.Edges))
// for _, edge := range req.Edges {
// // 检查更新ISP
// if edge.Isp != nil {
// switch edge2.ISPFromStr(*edge.Isp) {
// case edge2.IspUnknown:
// idsIspUnknown = append(idsIspUnknown, edge.Id)
// case edge2.IspChinaTelecom:
// idsIspTelecom = append(idsIspTelecom, edge.Id)
// case edge2.IspChinaUnicom:
// idsIspUnicom = append(idsIspUnicom, edge.Id)
// case edge2.IspChinaMobile:
// idsIspMobile = append(idsIspMobile, edge.Id)
// }
// }
// // 检查更新状态
// if edge.Status != nil {
// if *edge.Status == 1 {
// idsActive = append(idsActive, edge.Id)
// } else {
// idsInactive = append(idsInactive, edge.Id)
// }
// }
// // 无法分类更新
// if edge.Host != nil || edge.Port != nil || edge.Prov != nil || edge.City != nil {
// otherEdges = append(otherEdges, edge)
// continue
// }
// }
// slog.Debug("更新边缘节点信息",
// "active", len(idsActive),
// "inactive", len(idsInactive),
// "isp_unknown", len(idsIspUnknown),
// "isp_telecom", len(idsIspTelecom),
// "isp_unicom", len(idsIspUnicom),
// "isp_mobile", len(idsIspMobile),
// "other_edges", len(otherEdges),
// )
// err = q.Q.Transaction(func(q *q.Query) error {
// // 更新边缘节点状态
// if len(idsActive) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsActive...)).
// UpdateSimple(q.Edge.Status.Value(1))
// if err != nil {
// return err
// }
// }
// if len(idsInactive) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsInactive...)).
// UpdateSimple(q.Edge.Status.Value(0))
// if err != nil {
// return err
// }
// }
// // 更新边缘节点ISP
// if len(idsIspUnknown) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspUnknown...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspUnknown)))
// if err != nil {
// return err
// }
// }
// if len(idsIspTelecom) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspTelecom...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaTelecom)))
// if err != nil {
// return err
// }
// }
// if len(idsIspUnicom) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspUnicom...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaUnicom)))
// if err != nil {
// return err
// }
// }
// if len(idsIspMobile) > 0 {
// _, err = q.Edge.Debug().
// Where(q.Edge.ID.In(idsIspMobile...)).
// UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaMobile)))
// if err != nil {
// return err
// }
// }
// // 更新其他边缘节点信息
// for _, edge := range otherEdges {
// do := q.Edge.Debug().Where(q.Edge.ID.Eq(edge.Id))
// var assigns = make([]field.AssignExpr, 0, 5)
// if edge.Host != nil {
// assigns = append(assigns, q.Edge.Host.Value(*edge.Host))
// }
// if edge.Port != nil {
// assigns = append(assigns, q.Edge.ProxyPort.Value(*edge.Port))
// }
// if edge.Prov != nil {
// assigns = append(assigns, q.Edge.Prov.Value(*edge.Prov))
// }
// if edge.City != nil {
// assigns = append(assigns, q.Edge.City.Value(*edge.City))
// }
// // 更新边缘节点
// _, err := do.UpdateSimple(assigns...)
// if err != nil {
// return fmt.Errorf("更新边缘节点 %d 失败: %w", edge.Id, err)
// }
// }
// return nil
// })
// if err != nil {
// return err
// }
// return nil
} }
// endregion // endregion

View File

@@ -4,9 +4,8 @@ import (
"platform/pkg/u" "platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
resource2 "platform/web/domains/resource"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"time" "time"
@@ -43,7 +42,7 @@ func ListResourceShort(c *fiber.Ctx) error {
// 查询套餐列表 // 查询套餐列表
do := q.Resource.Where( do := q.Resource.Where(
q.Resource.UserID.Eq(authCtx.User.ID), q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Type.Eq(int32(resource2.TypeShort)), q.Resource.Type.Eq(int(m.ResourceTypeShort)),
) )
if req.ResourceNo != nil && *req.ResourceNo != "" { if req.ResourceNo != nil && *req.ResourceNo != "" {
do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
@@ -52,19 +51,19 @@ func ListResourceShort(c *fiber.Ctx) error {
do.Where(q.Resource.Active.Is(*req.Active)) do.Where(q.Resource.Active.Is(*req.Active))
} }
if req.Type != nil { if req.Type != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(int32(*req.Type))) do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(*req.Type))
} }
if req.CreateAfter != nil { if req.CreateAfter != nil {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter))) do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
} }
if req.CreateBefore != nil { if req.CreateBefore != nil {
do.Where(q.Resource.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore))) do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
} }
if req.ExpireAfter != nil { if req.ExpireAfter != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(orm.LocalDateTime(*req.ExpireAfter))) do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(*req.ExpireAfter))
} }
if req.ExpireBefore != nil { if req.ExpireBefore != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(orm.LocalDateTime(*req.ExpireBefore))) do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(*req.ExpireBefore))
} }
resource, err := q.Resource.Debug().Where(do). resource, err := q.Resource.Debug().Where(do).
@@ -124,7 +123,7 @@ func ListResourceLong(c *fiber.Ctx) error {
// 查询套餐列表 // 查询套餐列表
do := q.Resource.Where( do := q.Resource.Where(
q.Resource.UserID.Eq(authCtx.User.ID), q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Type.Eq(int32(resource2.TypeLong)), q.Resource.Type.Eq(int(m.ResourceTypeLong)),
) )
if req.ResourceNo != nil && *req.ResourceNo != "" { if req.ResourceNo != nil && *req.ResourceNo != "" {
do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
@@ -133,19 +132,19 @@ func ListResourceLong(c *fiber.Ctx) error {
do.Where(q.Resource.Active.Is(*req.Active)) do.Where(q.Resource.Active.Is(*req.Active))
} }
if req.Type != nil { if req.Type != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int32(*req.Type))) do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int(*req.Type)))
} }
if req.CreateAfter != nil { if req.CreateAfter != nil {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter))) do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
} }
if req.CreateBefore != nil { if req.CreateBefore != nil {
do.Where(q.Resource.CreatedAt.Lte(orm.LocalDateTime(*req.CreateBefore))) do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
} }
if req.ExpireAfter != nil { if req.ExpireAfter != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(orm.LocalDateTime(*req.ExpireAfter))) do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(*req.ExpireAfter))
} }
if req.ExpireBefore != nil { if req.ExpireBefore != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(orm.LocalDateTime(*req.ExpireBefore))) do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(*req.ExpireBefore))
} }
resource, err := q.Resource.Debug().Where(do). resource, err := q.Resource.Debug().Where(do).
@@ -202,27 +201,27 @@ func AllActiveResource(c *fiber.Ctx) error {
q.Resource.UserID.Eq(authCtx.User.ID), q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Active.Is(true), q.Resource.Active.Is(true),
q.Resource.Where( q.Resource.Where(
q.Resource.Type.Eq(int32(resource2.TypeShort)), q.Resource.Type.Eq(int(m.ResourceTypeShort)),
q.ResourceShort.As(q.Resource.Short.Name()).Where( q.ResourceShort.As(q.Resource.Short.Name()).Where(
short.Type.Eq(int32(resource2.ModeTime)), short.Type.Eq(int(m.ResourceModeTime)),
short.Expire.Gte(orm.LocalDateTime(now)), short.Expire.Gte(now),
q.ResourceShort.As(q.Resource.Short.Name()). q.ResourceShort.As(q.Resource.Short.Name()).
Where(short.DailyLast.Lt(orm.LocalDateTime(u.Today()))). Where(short.DailyLast.Lt(u.Today())).
Or(short.DailyLimit.GtCol(short.DailyUsed)), Or(short.DailyLimit.GtCol(short.DailyUsed)),
).Or( ).Or(
short.Type.Eq(int32(resource2.ModeCount)), short.Type.Eq(int(m.ResourceModeQuota)),
short.Quota.GtCol(short.Used), short.Quota.GtCol(short.Used),
), ),
).Or( ).Or(
q.Resource.Type.Eq(int32(resource2.TypeLong)), q.Resource.Type.Eq(int(m.ResourceTypeLong)),
q.ResourceLong.As(q.Resource.Long.Name()).Where( q.ResourceLong.As(q.Resource.Long.Name()).Where(
long.Type.Eq(int32(resource2.ModeTime)), long.Type.Eq(int(m.ResourceModeTime)),
long.Expire.Gte(orm.LocalDateTime(now)), long.Expire.Gte(now),
q.ResourceLong.As(q.Resource.Long.Name()). q.ResourceLong.As(q.Resource.Long.Name()).
Where(long.DailyLast.Lt(orm.LocalDateTime(u.Today()))). Where(long.DailyLast.Lt(u.Today())).
Or(long.DailyLimit.GtCol(long.DailyUsed)), Or(long.DailyLimit.GtCol(long.DailyUsed)),
).Or( ).Or(
long.Type.Eq(int32(resource2.ModeCount)), long.Type.Eq(int(m.ResourceModeQuota)),
long.Quota.GtCol(long.Used), long.Quota.GtCol(long.Used),
), ),
), ),
@@ -282,23 +281,23 @@ func StatisticResourceFree(c *fiber.Ctx) error {
switch { switch {
// 短效包量 // 短效包量
case resource2.Type(resource.Type) == resource2.TypeShort && resource2.Mode(resource.Short.Type) == resource2.ModeCount: case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota:
if u.Z(resource.Short.Quota) > resource.Short.Used { if u.Z(resource.Short.Quota) > resource.Short.Used {
shortCount++ shortCount++
shortQuotaSum += int(u.Z(resource.Short.Quota) - resource.Short.Used) shortQuotaSum += int(u.Z(resource.Short.Quota) - resource.Short.Used)
} }
// 长效包量 // 长效包量
case resource2.Type(resource.Type) == resource2.TypeLong && resource2.Mode(resource.Long.Type) == resource2.ModeCount: case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota:
if u.Z(resource.Long.Quota) > resource.Long.Used { if u.Z(resource.Long.Quota) > resource.Long.Used {
longCount++ longCount++
longQuotaSum += int(u.Z(resource.Long.Quota) - resource.Long.Used) longQuotaSum += int(u.Z(resource.Long.Quota) - resource.Long.Used)
} }
// 短效包时 // 短效包时
case resource2.Type(resource.Type) == resource2.TypeShort && resource2.Mode(resource.Short.Type) == resource2.ModeTime: case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime:
if time.Time(*resource.Short.Expire).After(time.Now()) { if time.Time(*resource.Short.Expire).After(time.Now()) {
if resource.Short.DailyLast == nil || u.SameDate(time.Time(*resource.Short.DailyLast)) == false { if resource.Short.DailyLast == nil || u.IsToday(time.Time(*resource.Short.DailyLast)) == false {
shortCount++ shortCount++
shortDailyFreeSum += int(resource.Short.DailyLimit) shortDailyFreeSum += int(resource.Short.DailyLimit)
} else if resource.Short.DailyLimit > resource.Short.DailyUsed { } else if resource.Short.DailyLimit > resource.Short.DailyUsed {
@@ -308,9 +307,9 @@ func StatisticResourceFree(c *fiber.Ctx) error {
} }
// 长效包时 // 长效包时
case resource2.Type(resource.Type) == resource2.TypeLong && resource2.Mode(resource.Long.Type) == resource2.ModeTime: case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime:
if time.Time(*resource.Long.Expire).After(time.Now()) { if time.Time(*resource.Long.Expire).After(time.Now()) {
if resource.Long.DailyLast == nil || u.SameDate(time.Time(*resource.Long.DailyLast)) == false { if resource.Long.DailyLast == nil || u.IsToday(time.Time(*resource.Long.DailyLast)) == false {
longCount++ longCount++
longDailyFreeSum += int(resource.Long.DailyLimit) longDailyFreeSum += int(resource.Long.DailyLimit)
} else if resource.Long.DailyLimit > resource.Long.DailyUsed { } else if resource.Long.DailyLimit > resource.Long.DailyUsed {
@@ -376,10 +375,10 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
do.Where(q.LogsUserUsage.ResourceID.Eq(resourceID)) do.Where(q.LogsUserUsage.ResourceID.Eq(resourceID))
} }
if req.TimeAfter != nil { if req.TimeAfter != nil {
do.Where(q.LogsUserUsage.Time.Gte(orm.LocalDateTime(*req.TimeAfter))) do.Where(q.LogsUserUsage.Time.Gte(*req.TimeAfter))
} }
if req.TimeBefore != nil { if req.TimeBefore != nil {
do.Where(q.LogsUserUsage.Time.Lte(orm.LocalDateTime(*req.TimeBefore))) do.Where(q.LogsUserUsage.Time.Lte(*req.TimeBefore))
} }
var data = new(StatisticResourceUsageResp) var data = new(StatisticResourceUsageResp)

View File

@@ -4,8 +4,8 @@ import (
"log/slog" "log/slog"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
trade2 "platform/web/domains/trade"
g "platform/web/globals" g "platform/web/globals"
m "platform/web/models"
s "platform/web/services" s "platform/web/services"
"time" "time"
@@ -14,7 +14,7 @@ import (
type TradeCreateReq struct { type TradeCreateReq struct {
s.CreateTradeData s.CreateTradeData
Type trade2.Type `json:"type" validate:"required"` Type m.TradeType `json:"type" validate:"required"`
Resource *s.CreateResourceData `json:"resource,omitempty"` Resource *s.CreateResourceData `json:"resource,omitempty"`
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"` Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
} }
@@ -38,12 +38,12 @@ func TradeCreate(c *fiber.Ctx) error {
} }
switch req.Type { switch req.Type {
case trade2.TypePurchase: case m.TradeTypePurchase:
if req.Resource == nil { if req.Resource == nil {
return core.NewBizErr("购买信息不能为空") return core.NewBizErr("购买信息不能为空")
} }
req.Product = req.Resource req.Product = req.Resource
case trade2.TypeRecharge: case m.TradeTypeRecharge:
if req.Recharge == nil { if req.Recharge == nil {
return core.NewBizErr("充值信息不能为空") return core.NewBizErr("充值信息不能为空")
} }

View File

@@ -19,7 +19,7 @@ type VerifierReq struct {
func SmsCode(c *fiber.Ctx) error { func SmsCode(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitInternalClient() _, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,11 +1,12 @@
package handlers package handlers
import ( import (
"net"
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
"time" "time"
@@ -89,7 +90,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
return err return err
} }
err = secureAddr(req.Host) ip, err := secureAddr(req.Host)
if err != nil { if err != nil {
return err return err
} }
@@ -97,7 +98,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
// 创建白名单 // 创建白名单
err = q.Whitelist.Create(&m.Whitelist{ err = q.Whitelist.Create(&m.Whitelist{
UserID: authCtx.User.ID, UserID: authCtx.User.ID,
Host: req.Host, IP: u.Z(ip),
Remark: &req.Remark, Remark: &req.Remark,
}) })
return nil return nil
@@ -125,17 +126,21 @@ func UpdateWhitelist(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "id is required") return fiber.NewError(fiber.StatusBadRequest, "id is required")
} }
ip, err := secureAddr(req.Host)
if err != nil {
return err
}
// 更新白名单 // 更新白名单
_, err = q.Whitelist. _, err = q.Whitelist.
Where( Where(
q.Whitelist.ID.Eq(req.ID), q.Whitelist.ID.Eq(req.ID),
q.Whitelist.UserID.Eq(authCtx.User.ID), q.Whitelist.UserID.Eq(authCtx.User.ID),
). ).
Updates(&m.Whitelist{ UpdateSimple(
ID: req.ID, q.Whitelist.IP.Value(ip),
Host: req.Host, q.Whitelist.Remark.Value(req.Remark),
Remark: &req.Remark, )
})
if err != nil { if err != nil {
return err return err
} }
@@ -186,13 +191,13 @@ func RemoveWhitelist(c *fiber.Ctx) error {
return nil return nil
} }
func secureAddr(str string) error { func secureAddr(str string) (*orm.Inet, error) {
var addr = net.ParseIP(str) ip, err := orm.ParseInet(str)
if addr == nil { if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "IP 解析失败") return nil, err
} }
if env.RunMode == env.RunModeDev || addr.IsGlobalUnicast() { if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
return nil return nil, fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用")
} }
return fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用") return ip, nil
} }

View File

@@ -1,36 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameAdmin = "admin"
// Admin mapped from table <admin>
type Admin struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:管理员ID" json:"id"` // 管理员ID
Username string `gorm:"column:username;type:character varying(255);not null;comment:用户名" json:"username"` // 用户名
Password string `gorm:"column:password;type:character varying(255);not null;comment:密码" json:"password"` // 密码
Name *string `gorm:"column:name;type:character varying(255);comment:真实姓名" json:"name"` // 真实姓名
Avatar *string `gorm:"column:avatar;type:character varying(255);comment:头像URL" json:"avatar"` // 头像URL
Phone *string `gorm:"column:phone;type:character varying(255);comment:手机号码" json:"phone"` // 手机号码
Email *string `gorm:"column:email;type:character varying(255);comment:邮箱" json:"email"` // 邮箱
Status int32 `gorm:"column:status;type:integer;not null;default:1;comment:状态0-禁用1-正常" json:"status"` // 状态0-禁用1-正常
LastLogin *orm.LocalDateTime `gorm:"column:last_login;type:timestamp without time zone;comment:最后登录时间" json:"last_login"` // 最后登录时间
LastLoginHost *string `gorm:"column:last_login_host;type:character varying(45);comment:最后登录地址" json:"last_login_host"` // 最后登录地址
LastLoginAgent *string `gorm:"column:last_login_agent;type:character varying(255);comment:最后登录代理" json:"last_login_agent"` // 最后登录代理
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Admin's table name
func (*Admin) TableName() string {
return TableNameAdmin
}

31
web/models/admin.go Normal file
View File

@@ -0,0 +1,31 @@
package models
import (
"time"
"platform/web/core"
"platform/web/globals/orm"
)
// Admin 管理员表
type Admin struct {
core.Model
Username string `json:"username" gorm:"column:username"` // 用户名
Password string `json:"password" gorm:"column:password"` // 密码
Name *string `json:"name" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar" gorm:"column:avatar"` // 头像URL
Phone *string `json:"phone" gorm:"column:phone"` // 手机号码
Email *string `json:"email" gorm:"column:email"` // 邮箱
Status AdminStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常
LastLogin *time.Time `json:"last_login" gorm:"column:last_login"` // 最后登录时间
LastLoginIP *orm.Inet `json:"last_login_ip" gorm:"column:last_login_ip"` // 最后登录地址
LastLoginUA *string `json:"last_login_ua" gorm:"column:last_login_ua"` // 最后登录代理
}
// AdminStatus 管理员状态枚举
type AdminStatus int
const (
AdminStatusDisabled AdminStatus = 0 // 禁用
AdminStatusEnabled AdminStatus = 1 // 正常
)

View File

@@ -1,30 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameAdminRole = "admin_role"
// AdminRole mapped from table <admin_role>
type AdminRole struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:管理员角色ID" json:"id"` // 管理员角色ID
Name string `gorm:"column:name;type:character varying(255);not null;comment:角色名称" json:"name"` // 角色名称
Description *string `gorm:"column:description;type:character varying(255);comment:角色描述" json:"description"` // 角色描述
Active *bool `gorm:"column:active;type:boolean;default:true;comment:是否激活" json:"active"` // 是否激活
Sort *int32 `gorm:"column:sort;type:integer;comment:排序" json:"sort"` // 排序
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName AdminRole's table name
func (*AdminRole) TableName() string {
return TableNameAdminRole
}

14
web/models/admin_role.go Normal file
View File

@@ -0,0 +1,14 @@
package models
import (
"platform/web/core"
)
// AdminRole 管理员角色表
type AdminRole struct {
core.Model
Name string `json:"name" gorm:"column:name"` // 角色名称
Description *string `json:"description" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
}

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameAdminRoleLink = "admin_role_link"
// AdminRoleLink mapped from table <admin_role_link>
type AdminRoleLink struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
AdminID int32 `gorm:"column:admin_id;type:integer;not null;comment:管理员ID" json:"admin_id"` // 管理员ID
RoleID int32 `gorm:"column:role_id;type:integer;not null;comment:角色ID" json:"role_id"` // 角色ID
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName AdminRoleLink's table name
func (*AdminRoleLink) TableName() string {
return TableNameAdminRoleLink
}

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameAdminRolePermissionLink = "admin_role_permission_link"
// AdminRolePermissionLink mapped from table <admin_role_permission_link>
type AdminRolePermissionLink struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
RoleID int32 `gorm:"column:role_id;type:integer;not null;comment:角色ID" json:"role_id"` // 角色ID
PermissionID int32 `gorm:"column:permission_id;type:integer;not null;comment:权限ID" json:"permission_id"` // 权限ID
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName AdminRolePermissionLink's table name
func (*AdminRolePermissionLink) TableName() string {
return TableNameAdminRolePermissionLink
}

View File

@@ -1,32 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameAnnouncement = "announcement"
// Announcement mapped from table <announcement>
type Announcement struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:公告ID" json:"id"` // 公告ID
Title string `gorm:"column:title;type:character varying(255);not null;comment:公告标题" json:"title"` // 公告标题
Content *string `gorm:"column:content;type:text;comment:公告内容" json:"content"` // 公告内容
Type int32 `gorm:"column:type;type:integer;not null;default:1;comment:公告类型1-普通公告" json:"type"` // 公告类型1-普通公告
Pin bool `gorm:"column:pin;type:boolean;not null;comment:是否置顶" json:"pin"` // 是否置顶
Status int32 `gorm:"column:status;type:integer;not null;default:1;comment:公告状态0-禁用1-正常" json:"status"` // 公告状态0-禁用1-正常
Sort int32 `gorm:"column:sort;type:integer;not null;comment:公告排序" json:"sort"` // 公告排序
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Announcement's table name
func (*Announcement) TableName() string {
return TableNameAnnouncement
}

View File

@@ -0,0 +1,31 @@
package models
import (
"platform/web/core"
)
// Announcement 公告表
type Announcement struct {
core.Model
Title string `json:"title" gorm:"column:title"` // 公告标题
Content *string `json:"content" gorm:"column:content"` // 公告内容
Type AnnouncementType `json:"type" gorm:"column:type"` // 公告类型1-普通公告
Pin bool `json:"pin" gorm:"column:pin"` // 是否置顶
Status AnnouncementStatus `json:"status" gorm:"column:status"` // 公告状态0-禁用1-正常
Sort int32 `json:"sort" gorm:"column:sort"` // 公告排序
}
// AnnouncementType 公告类型枚举
type AnnouncementType int
const (
AnnouncementTypeNormal AnnouncementType = 1 // 普通公告
)
// AnnouncementStatus 公告状态枚举
type AnnouncementStatus int
const (
AnnouncementStatusDisabled AnnouncementStatus = 0 // 禁用
AnnouncementStatusEnabled AnnouncementStatus = 1 // 正常
)

View File

@@ -1,38 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
const TableNameBill = "bill"
// Bill mapped from table <bill>
type Bill struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:账单ID" json:"id"` // 账单ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
TradeID *int32 `gorm:"column:trade_id;type:integer;comment:订单ID" json:"trade_id"` // 订单ID
ResourceID *int32 `gorm:"column:resource_id;type:integer;comment:套餐ID" json:"resource_id"` // 套餐ID
RefundID *int32 `gorm:"column:refund_id;type:integer;comment:退款ID" json:"refund_id"` // 退款ID
BillNo string `gorm:"column:bill_no;type:character varying(255);not null;comment:易读账单号" json:"bill_no"` // 易读账单号
Info *string `gorm:"column:info;type:character varying(255);comment:产品可读信息" json:"info"` // 产品可读信息
Type int32 `gorm:"column:type;type:integer;not null;comment:账单类型1-消费2-退款3-充值" json:"type"` // 账单类型1-消费2-退款3-充值
Amount decimal.Decimal `gorm:"column:amount;type:numeric(12,2);not null;comment:账单金额" json:"amount"` // 账单金额
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
Trade *Trade `gorm:"foreignKey:TradeID" json:"trade"`
Refund *Refund `gorm:"foreignKey:RefundID" json:"refund"`
Resource *Resource `gorm:"foreignKey:ResourceID" json:"resource"`
}
// TableName Bill's table name
func (*Bill) TableName() string {
return TableNameBill
}

34
web/models/bill.go Normal file
View File

@@ -0,0 +1,34 @@
package models
import (
"platform/web/core"
"github.com/shopspring/decimal"
)
// Bill 账单表
type Bill struct {
core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
TradeID *int32 `json:"trade_id" gorm:"column:trade_id"` // 订单ID
ResourceID *int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
RefundID *int32 `json:"refund_id" gorm:"column:refund_id"` // 退款ID
BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号
Info *string `json:"info" gorm:"column:info"` // 产品可读信息
Type BillType `json:"type" gorm:"column:type"` // 账单类型1-消费2-退款3-充值
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 账单金额
User *User `json:"user" gorm:"foreignKey:UserID"`
Trade *Trade `json:"trade" gorm:"foreignKey:TradeID"`
Resource *Resource `json:"resource" gorm:"foreignKey:ResourceID"`
Refund *Refund `json:"refund" gorm:"foreignKey:RefundID"`
}
// BillType 账单类型枚举
type BillType int
const (
BillTypeConsume BillType = 1 // 消费
BillTypeRefund BillType = 2 // 退款
BillTypeRecharge BillType = 3 // 充值
)

View File

@@ -1,40 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameChannel = "channel"
// Channel mapped from table <channel>
type Channel struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:通道ID" json:"id"` // 通道ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
ProxyID int32 `gorm:"column:proxy_id;type:integer;not null;comment:代理ID" json:"proxy_id"` // 代理ID
EdgeID *int32 `gorm:"column:edge_id;type:integer;comment:节点ID" json:"edge_id"` // 节点ID
ResourceID int32 `gorm:"column:resource_id;type:integer;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
ProxyHost string `gorm:"column:proxy_host;type:character varying(255);not null;comment:代理地址" json:"proxy_host"` // 代理地址
ProxyPort int32 `gorm:"column:proxy_port;type:integer;not null;comment:转发端口" json:"proxy_port"` // 转发端口
EdgeHost *string `gorm:"column:edge_host;type:character varying(255);comment:节点地址" json:"edge_host"` // 节点地址
Protocol *int32 `gorm:"column:protocol;type:integer;comment:协议类型1-http2-https3-socks5" json:"protocol"` // 协议类型1-http2-https3-socks5
AuthIP bool `gorm:"column:auth_ip;type:boolean;not null;comment:IP认证" json:"auth_ip"` // IP认证
Whitelists *string `gorm:"column:whitelists;type:text;comment:IP白名单逗号分隔" json:"whitelists"` // IP白名单逗号分隔
AuthPass bool `gorm:"column:auth_pass;type:boolean;not null;comment:密码认证" json:"auth_pass"` // 密码认证
Username *string `gorm:"column:username;type:character varying(255);comment:用户名" json:"username"` // 用户名
Password *string `gorm:"column:password;type:character varying(255);comment:密码" json:"password"` // 密码
Expiration orm.LocalDateTime `gorm:"column:expiration;type:timestamp without time zone;not null;comment:过期时间" json:"expiration"` // 过期时间
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Channel's table name
func (*Channel) TableName() string {
return TableNameChannel
}

31
web/models/channel.go Normal file
View File

@@ -0,0 +1,31 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
"time"
)
// Channel 通道表
type Channel struct {
core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
ProxyID int32 `json:"proxy_id" gorm:"column:proxy_id"` // 代理ID
BatchNo string `json:"batch_no" gorm:"column:batch_no"` // 批次编号
Port uint16 `json:"port" gorm:"column:port"` // 代理端口
EdgeID *int32 `json:"edge_id" gorm:"column:edge_id"` // 节点ID手动配置
FilterISP *EdgeISP `json:"filter_isp" gorm:"column:filter_isp"` // 运营商过滤(自动配置):参考 edge.isp
FilterProv *string `json:"filter_prov" gorm:"column:filter_prov"` // 省份过滤(自动配置)
FilterCity *string `json:"filter_city" gorm:"column:filter_city"` // 城市过滤(自动配置)
IP *orm.Inet `json:"ip" gorm:"column:ip"` // 节点地址
Whitelists *orm.Slice[string] `json:"whitelists" gorm:"column:whitelists"` // IP白名单逗号分隔
Username *string `json:"username" gorm:"column:username"` // 用户名
Password *string `json:"password" gorm:"column:password"` // 密码
ExpiredAt time.Time `json:"expired_at" gorm:"column:expired_at"` // 过期时间
User User `json:"user" gorm:"foreignKey:UserID"`
Resource Resource `json:"resource" gorm:"foreignKey:ResourceID"`
Proxy Proxy `json:"proxy" gorm:"foreignKey:ProxyID"`
Edge *Edge `json:"edge" gorm:"foreignKey:EdgeID"`
}

View File

@@ -1,34 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameClient = "client"
// Client mapped from table <client>
type Client struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:客户端ID" json:"id"` // 客户端ID
ClientID string `gorm:"column:client_id;type:character varying(255);not null;comment:OAuth2客户端标识符" json:"client_id"` // OAuth2客户端标识符
ClientSecret string `gorm:"column:client_secret;type:character varying(255);not null;comment:OAuth2客户端密钥" json:"client_secret"` // OAuth2客户端密钥
RedirectURI *string `gorm:"column:redirect_uri;type:character varying(255);comment:OAuth2 重定向URI" json:"redirect_uri"` // OAuth2 重定向URI
Spec int32 `gorm:"column:spec;type:integer;not null;comment:安全规范1-native2-browser3-web4-api" json:"spec"` // 安全规范1-native2-browser3-web4-api
Name string `gorm:"column:name;type:character varying(255);not null;comment:名称" json:"name"` // 名称
Icon *string `gorm:"column:icon;type:character varying(255);comment:图标URL" json:"icon"` // 图标URL
Status int32 `gorm:"column:status;type:integer;not null;default:1;comment:状态0-禁用1-正常" json:"status"` // 状态0-禁用1-正常
Type int32 `gorm:"column:type;type:integer;not null;comment:类型0-普通1-官方" json:"type"` // 类型0-普通1-官方
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Client's table name
func (*Client) TableName() string {
return TableNameClient
}

44
web/models/client.go Normal file
View File

@@ -0,0 +1,44 @@
package models
import (
"platform/web/core"
)
// Client 客户端表
type Client struct {
core.Model
ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符
ClientSecret string `json:"client_secret" gorm:"column:client_secret"` // OAuth2客户端密钥
RedirectURI *string `json:"redirect_uri" gorm:"column:redirect_uri"` // OAuth2 重定向URI
Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范1-native2-browser3-web4-api
Name string `json:"name" gorm:"column:name"` // 名称
Icon *string `json:"icon" gorm:"column:icon"` // 图标URL
Status ClientStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常
Type ClientType `json:"type" gorm:"column:type"` // 类型0-普通1-官方
}
// ClientSpec 客户端安全规范枚举
type ClientSpec int
const (
ClientSpecNative ClientSpec = 1 // native
ClientSpecBrowser ClientSpec = 2 // browser
ClientSpecWeb ClientSpec = 3 // web
ClientSpecAPI ClientSpec = 4 // api
)
// ClientStatus 客户端状态枚举
type ClientStatus int
const (
ClientStatusDisabled ClientStatus = 0 // 禁用
ClientStatusEnabled ClientStatus = 1 // 正常
)
// ClientType 客户端类型枚举
type ClientType int
const (
ClientTypeNormal ClientType = 0 // 普通
ClientTypeOfficial ClientType = 1 // 官方
)

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameClientPermissionLink = "client_permission_link"
// ClientPermissionLink mapped from table <client_permission_link>
type ClientPermissionLink struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
ClientID int32 `gorm:"column:client_id;type:integer;not null;comment:客户端ID" json:"client_id"` // 客户端ID
PermissionID int32 `gorm:"column:permission_id;type:integer;not null;comment:权限ID" json:"permission_id"` // 权限ID
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName ClientPermissionLink's table name
func (*ClientPermissionLink) TableName() string {
return TableNameClientPermissionLink
}

View File

@@ -1,34 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
const TableNameCoupon = "coupon"
// Coupon mapped from table <coupon>
type Coupon struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:优惠券ID" json:"id"` // 优惠券ID
UserID *int32 `gorm:"column:user_id;type:integer;comment:用户ID" json:"user_id"` // 用户ID
Code string `gorm:"column:code;type:character varying(255);not null;comment:优惠券代码" json:"code"` // 优惠券代码
Remark *string `gorm:"column:remark;type:character varying(255);comment:优惠券备注" json:"remark"` // 优惠券备注
Amount decimal.Decimal `gorm:"column:amount;type:numeric(12,2);not null;comment:优惠券金额" json:"amount"` // 优惠券金额
MinAmount decimal.Decimal `gorm:"column:min_amount;type:numeric(12,2);not null;comment:最低消费金额" json:"min_amount"` // 最低消费金额
Status int32 `gorm:"column:status;type:integer;not null;comment:优惠券状态0-未使用1-已使用2-已过期" json:"status"` // 优惠券状态0-未使用1-已使用2-已过期
ExpireAt *orm.LocalDateTime `gorm:"column:expire_at;type:timestamp without time zone;comment:过期时间" json:"expire_at"` // 过期时间
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Coupon's table name
func (*Coupon) TableName() string {
return TableNameCoupon
}

29
web/models/coupon.go Normal file
View File

@@ -0,0 +1,29 @@
package models
import (
"platform/web/core"
"time"
"github.com/shopspring/decimal"
)
// Coupon 优惠券表
type Coupon struct {
core.Model
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
Code string `json:"code" gorm:"column:code"` // 优惠券代码
Remark *string `json:"remark" gorm:"column:remark"` // 优惠券备注
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 优惠券金额
MinAmount decimal.Decimal `json:"min_amount" gorm:"column:min_amount"` // 最低消费金额
Status CouponStatus `json:"status" gorm:"column:status"` // 优惠券状态0-未使用1-已使用2-已过期
ExpireAt *time.Time `json:"expire_at" gorm:"column:expire_at"` // 过期时间
}
// CouponStatus 优惠券状态枚举
type CouponStatus int
const (
CouponStatusUnused CouponStatus = 0 // 未使用
CouponStatusUsed CouponStatus = 1 // 已使用
CouponStatusExpired CouponStatus = 2 // 已过期
)

View File

@@ -1,38 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameEdge = "edge"
// Edge mapped from table <edge>
type Edge struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:节点ID" json:"id"` // 节点ID
ProxyID *int32 `gorm:"column:proxy_id;type:integer;comment:代理ID" json:"proxy_id"` // 代理ID
Type int32 `gorm:"column:type;type:integer;not null;comment:节点类型1-自建" json:"type"` // 节点类型1-自建
Version int32 `gorm:"column:version;type:integer;not null;comment:节点版本" json:"version"` // 节点版本
Name string `gorm:"column:name;type:character varying(255);not null;comment:节点名称" json:"name"` // 节点名称
Host string `gorm:"column:host;type:character varying(255);not null;comment:节点地址" json:"host"` // 节点地址
Isp int32 `gorm:"column:isp;type:integer;not null;comment:运营商0-未知1-电信2-联通3-移动" json:"isp"` // 运营商0-未知1-电信2-联通3-移动
Prov string `gorm:"column:prov;type:character varying(255);not null;comment:省份" json:"prov"` // 省份
City string `gorm:"column:city;type:character varying(255);not null;comment:城市" json:"city"` // 城市
ProxyPort *int32 `gorm:"column:proxy_port;type:integer;comment:代理端口" json:"proxy_port"` // 代理端口
Status int32 `gorm:"column:status;type:integer;not null;comment:节点状态0-离线1-正常" json:"status"` // 节点状态0-离线1-正常
Rtt *int32 `gorm:"column:rtt;type:integer;comment:最近平均延迟" json:"rtt"` // 最近平均延迟
Loss *int32 `gorm:"column:loss;type:integer;comment:最近丢包率" json:"loss"` // 最近丢包率
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Edge's table name
func (*Edge) TableName() string {
return TableNameEdge
}

65
web/models/edge.go Normal file
View File

@@ -0,0 +1,65 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
)
// Edge 节点表
type Edge struct {
core.Model
Type EdgeType `json:"type" gorm:"column:type"` // 节点类型1-自建
Version int32 `json:"version" gorm:"column:version"` // 节点版本
Mac string `json:"mac" gorm:"column:mac"` // 节点 mac 地址
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // 节点地址
ISP EdgeISP `json:"isp" gorm:"column:isp"` // 运营商0-未知1-电信2-联通3-移动
Prov string `json:"prov" gorm:"column:prov"` // 省份
City string `json:"city" gorm:"column:city"` // 城市
Status EdgeStatus `json:"status" gorm:"column:status"` // 节点状态0-离线1-正常
RTT int32 `json:"rtt" gorm:"column:rtt"` // 最近平均延迟
Loss int32 `json:"loss" gorm:"column:loss"` // 最近丢包率
}
// EdgeType 节点类型枚举
type EdgeType int
const (
EdgeTypeSelfBuilt EdgeType = 1 // 自建
)
// EdgeStatus 节点状态枚举
type EdgeStatus int
const (
EdgeStatusOffline EdgeStatus = 0 // 离线
EdgeStatusNormal EdgeStatus = 1 // 正常
)
// EdgeISP 运营商枚举
type EdgeISP int
const (
EdgeISPTelecom EdgeISP = 1 // 电信
EdgeISPUnicom EdgeISP = 2 // 联通
EdgeISPMobile EdgeISP = 3 // 移动
)
func (isp *EdgeISP) String() string {
if isp == nil {
return ""
}
switch *isp {
case EdgeISPTelecom:
return "电信"
case EdgeISPUnicom:
return "联通"
case EdgeISPMobile:
return "移动"
default:
return ""
}
}
func ToEdgeISP(i int) EdgeISP {
return EdgeISP(i)
}

View File

@@ -0,0 +1,8 @@
package models
// LinkAdminRole 管理员角色关联表
type LinkAdminRole struct {
ID int32 `json:"id" gorm:"column:id"` // 关联ID
AdminID int32 `json:"admin_id" gorm:"column:admin_id"` // 管理员ID
RoleID int32 `json:"role_id" gorm:"column:role_id"` // 角色ID
}

View File

@@ -0,0 +1,8 @@
package models
// LinkAdminRolePermission 管理员角色权限关联表
type LinkAdminRolePermission struct {
ID int32 `json:"id" gorm:"column:id"` // 关联ID
RoleID int32 `json:"role_id" gorm:"column:role_id"` // 角色ID
PermissionID int32 `json:"permission_id" gorm:"column:permission_id"` // 权限ID
}

View File

@@ -0,0 +1,8 @@
package models
// LinkClientPermission 客户端权限关联表
type LinkClientPermission struct {
ID int32 `json:"id" gorm:"column:id"` // 关联ID
ClientID int32 `json:"client_id" gorm:"column:client_id"` // 客户端ID
PermissionID int32 `json:"permission_id" gorm:"column:permission_id"` // 权限ID
}

View File

@@ -0,0 +1,8 @@
package models
// LinkUserRole 用户角色关联表
type LinkUserRole struct {
ID int32 `json:"id" gorm:"column:id"` // 关联ID
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
RoleID int32 `json:"role_id" gorm:"column:role_id"` // 角色ID
}

View File

@@ -0,0 +1,8 @@
package models
// LinkUserRolePermission 用户角色权限关联表
type LinkUserRolePermission struct {
ID int32 `json:"id" gorm:"column:id"` // 关联ID
RoleID int32 `json:"role_id" gorm:"column:role_id"` // 角色ID
PermissionID int32 `json:"permission_id" gorm:"column:permission_id"` // 权限ID
}

View File

@@ -1,26 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameLogsLogin = "logs_login"
// LogsLogin mapped from table <logs_login>
type LogsLogin struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:登录日志ID" json:"id"` // 登录日志ID
IP string `gorm:"column:ip;type:character varying(45);not null;comment:IP地址" json:"ip"` // IP地址
UA string `gorm:"column:ua;type:character varying(255);not null;comment:用户代理" json:"ua"` // 用户代理
GrantType string `gorm:"column:grant_type;type:character varying(255);not null;comment:授权类型authorization_code-授权码模式client_credentials-客户端凭证模式refresh_token-刷新令牌模式password-密码模式" json:"grant_type"` // 授权类型authorization_code-授权码模式client_credentials-客户端凭证模式refresh_token-刷新令牌模式password-密码模式
PasswordGrantType string `gorm:"column:password_grant_type;type:character varying(255);not null;comment:密码模式子授权类型password-账号密码phone_code-手机验证码email_code-邮箱验证码" json:"password_grant_type"` // 密码模式子授权类型password-账号密码phone_code-手机验证码email_code-邮箱验证码
Success bool `gorm:"column:success;type:boolean;not null;comment:登录是否成功" json:"success"` // 登录是否成功
UserID *int32 `gorm:"column:user_id;type:integer;comment:用户ID" json:"user_id"` // 用户ID
Time orm.LocalDateTime `gorm:"column:time;type:timestamp without time zone;not null;comment:登录时间" json:"time"` // 登录时间
}
// TableName LogsLogin's table name
func (*LogsLogin) TableName() string {
return TableNameLogsLogin
}

39
web/models/logs_login.go Normal file
View File

@@ -0,0 +1,39 @@
package models
import (
"platform/web/globals/orm"
"time"
)
// LogsLogin 登录日志表
type LogsLogin struct {
ID int32 `json:"id" gorm:"column:id"` // 登录日志ID
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // IP地址
UA string `json:"ua" gorm:"column:ua"` // 用户代理
GrantType GrantType `json:"grant_type" gorm:"column:grant_type"` // 授权类型
PasswordType PasswordType `json:"password_type" gorm:"column:password_type"` // 密码模式子授权类型
Success bool `json:"success" gorm:"column:success"` // 登录是否成功
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
Time time.Time `json:"time" gorm:"column:time"` // 登录时间
User *User `json:"user" gorm:"foreignKey:UserID"`
}
// GrantType 授权类型枚举
type GrantType string
const (
GrantTypeAuthorizationCode GrantType = "authorization_code" // 授权码模式
GrantTypeClientCredentials GrantType = "client_credentials" // 客户端凭证模式
GrantTypeRefreshToken GrantType = "refresh_token" // 刷新令牌模式
GrantTypePassword GrantType = "password" // 密码模式
)
// PasswordType 密码模式子授权类型枚举
type PasswordType string
const (
PasswordTypePassword PasswordType = "password" // 账号密码
PasswordTypePhoneCode PasswordType = "phone_code" // 手机验证码
PasswordTypeEmailCode PasswordType = "email_code" // 邮箱验证码
)

View File

@@ -1,29 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameLogsRequest = "logs_request"
// LogsRequest mapped from table <logs_request>
type LogsRequest struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:访问日志ID" json:"id"` // 访问日志ID
IP string `gorm:"column:ip;type:character varying(45);not null;comment:IP地址" json:"ip"` // IP地址
UA string `gorm:"column:ua;type:character varying(255);not null;comment:用户代理" json:"ua"` // 用户代理
UserID *int32 `gorm:"column:user_id;type:integer;comment:用户ID" json:"user_id"` // 用户ID
ClientID *int32 `gorm:"column:client_id;type:integer;comment:客户端ID" json:"client_id"` // 客户端ID
Method string `gorm:"column:method;type:character varying(10);not null;comment:请求方法" json:"method"` // 请求方法
Path string `gorm:"column:path;type:character varying(255);not null;comment:请求路径" json:"path"` // 请求路径
Status int32 `gorm:"column:status;type:integer;not null;comment:响应状态码" json:"status"` // 响应状态码
Error *string `gorm:"column:error;type:text;comment:错误信息" json:"error"` // 错误信息
Time orm.LocalDateTime `gorm:"column:time;type:timestamp without time zone;not null;comment:请求时间" json:"time"` // 请求时间
Latency string `gorm:"column:latency;type:character varying(255);not null;comment:请求延迟" json:"latency"` // 请求延迟
}
// TableName LogsRequest's table name
func (*LogsRequest) TableName() string {
return TableNameLogsRequest
}

View File

@@ -0,0 +1,24 @@
package models
import (
"platform/web/globals/orm"
"time"
)
// LogsRequest 访问日志表
type LogsRequest struct {
ID int32 `json:"id" gorm:"column:id"` // 访问日志ID
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // IP地址
UA string `json:"ua" gorm:"column:ua"` // 用户代理
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ClientID *int32 `json:"client_id" gorm:"column:client_id"` // 客户端ID
Method string `json:"method" gorm:"column:method"` // 请求方法
Path string `json:"path" gorm:"column:path"` // 请求路径
Status int16 `json:"status" gorm:"column:status"` // 响应状态码
Error *string `json:"error" gorm:"column:error"` // 错误信息
Time time.Time `json:"time" gorm:"column:time"` // 请求时间
Latency string `json:"latency" gorm:"column:latency"` // 请求延迟
User *User `json:"user" gorm:"foreignKey:UserID"`
Client *Client `json:"client" gorm:"foreignKey:ClientID"`
}

View File

@@ -1,22 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameLogsUserBandwidth = "logs_user_bandwidth"
// LogsUserBandwidth mapped from table <logs_user_bandwidth>
type LogsUserBandwidth struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:日志ID" json:"id"` // 日志ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
Bandwidth int32 `gorm:"column:bandwidth;type:integer;not null;comment:带宽使用量(KB)" json:"bandwidth"` // 带宽使用量(KB)
Time orm.LocalDateTime `gorm:"column:time;type:timestamp without time zone;not null;comment:记录时间" json:"time"` // 记录时间
}
// TableName LogsUserBandwidth's table name
func (*LogsUserBandwidth) TableName() string {
return TableNameLogsUserBandwidth
}

View File

@@ -0,0 +1,13 @@
package models
import (
"time"
)
// LogsUserBandwidth 用户带宽日志表
type LogsUserBandwidth struct {
ID int32 `json:"id" gorm:"column:id;not null"` // 日志ID
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
Bandwidth int32 `json:"bandwidth" gorm:"column:bandwidth"` // 带宽使用量(KB)
Time time.Time `json:"time" gorm:"column:time"` // 记录时间
}

View File

@@ -1,27 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameLogsUserUsage = "logs_user_usage"
// LogsUserUsage mapped from table <logs_user_usage>
type LogsUserUsage struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:日志ID" json:"id"` // 日志ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
ResourceID int32 `gorm:"column:resource_id;type:integer;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
Count_ int32 `gorm:"column:count;type:integer;not null;comment:数量" json:"count"` // 数量
Prov *string `gorm:"column:prov;type:character varying(255);comment:省份" json:"prov"` // 省份
City *string `gorm:"column:city;type:character varying(255);comment:城市" json:"city"` // 城市
Isp *string `gorm:"column:isp;type:character varying(255);comment:运营商" json:"isp"` // 运营商
IP string `gorm:"column:ip;type:character varying(45);not null;comment:IP地址" json:"ip"` // IP地址
Time orm.LocalDateTime `gorm:"column:time;type:timestamp without time zone;not null;comment:提取时间" json:"time"` // 提取时间
}
// TableName LogsUserUsage's table name
func (*LogsUserUsage) TableName() string {
return TableNameLogsUserUsage
}

View File

@@ -0,0 +1,19 @@
package models
import (
"platform/web/globals/orm"
"time"
)
// LogsUserUsage 用户使用日志表
type LogsUserUsage struct {
ID int32 `json:"id" gorm:"column:id"` // 日志ID
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Count int32 `json:"count" gorm:"column:count"` // 数量
Prov *string `json:"prov" gorm:"column:prov"` // 省份
City *string `json:"city" gorm:"column:city"` // 城市
ISP *string `json:"isp" gorm:"column:isp"` // 运营商
IP orm.Inet `json:"ip" gorm:"column:ip"` // IP地址
Time time.Time `json:"time" gorm:"column:time"` // 提取时间
}

View File

@@ -1,29 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNamePermission = "permission"
// Permission mapped from table <permission>
type Permission struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:权限ID" json:"id"` // 权限ID
ParentID *int32 `gorm:"column:parent_id;type:integer;comment:父权限ID" json:"parent_id"` // 父权限ID
Name string `gorm:"column:name;type:character varying(255);not null;comment:权限名称" json:"name"` // 权限名称
Description *string `gorm:"column:description;type:character varying(255);comment:权限描述" json:"description"` // 权限描述
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Permission's table name
func (*Permission) TableName() string {
return TableNamePermission
}

14
web/models/permission.go Normal file
View File

@@ -0,0 +1,14 @@
package models
import "platform/web/core"
// Permission 权限表
type Permission struct {
core.Model
ParentID *int32 `json:"parent_id" gorm:"column:parent_id"` // 父权限ID
Name string `json:"name" gorm:"column:name"` // 权限名称
Description *string `json:"description" gorm:"column:description"` // 权限描述
Parent *Permission `json:"parent" gorm:"foreignKey:ParentID"`
Children []*Permission `json:"children" gorm:"foreignKey:ParentID"`
}

View File

@@ -1,31 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameProduct = "product"
// Product mapped from table <product>
type Product struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:产品ID" json:"id"` // 产品ID
Code string `gorm:"column:code;type:character varying(255);not null;comment:产品代码" json:"code"` // 产品代码
Name string `gorm:"column:name;type:character varying(255);not null;comment:产品名称" json:"name"` // 产品名称
Description *string `gorm:"column:description;type:character varying(255);comment:产品描述" json:"description"` // 产品描述
Sort int32 `gorm:"column:sort;type:integer;not null;comment:排序" json:"sort"` // 排序
Status int32 `gorm:"column:status;type:integer;not null;default:1;comment:产品状态0-禁用1-正常" json:"status"` // 产品状态0-禁用1-正常
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Product's table name
func (*Product) TableName() string {
return TableNameProduct
}

23
web/models/product.go Normal file
View File

@@ -0,0 +1,23 @@
package models
import (
"platform/web/core"
)
// Product 产品表
type Product struct {
core.Model
Code string `json:"code" gorm:"column:code"` // 产品代码
Name string `json:"name" gorm:"column:name"` // 产品名称
Description *string `json:"description" gorm:"column:description"` // 产品描述
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态0-禁用1-正常
}
// ProductStatus 产品状态枚举
type ProductStatus int
const (
ProductStatusDisabled ProductStatus = 0 // 禁用
ProductStatusEnabled ProductStatus = 1 // 正常
)

View File

@@ -1,33 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameProxy = "proxy"
// Proxy mapped from table <proxy>
type Proxy struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:代理服务ID" json:"id"` // 代理服务ID
Version int32 `gorm:"column:version;type:integer;not null;comment:代理服务版本" json:"version"` // 代理服务版本
Name string `gorm:"column:name;type:character varying(255);not null;comment:代理服务名称" json:"name"` // 代理服务名称
Host string `gorm:"column:host;type:character varying(255);not null;comment:代理服务地址" json:"host"` // 代理服务地址
Secret *string `gorm:"column:secret;type:character varying(255);comment:代理服务密钥" json:"secret"` // 代理服务密钥
Type int32 `gorm:"column:type;type:integer;not null;comment:代理服务类型1-三方2-自有" json:"type"` // 代理服务类型1-三方2-自有
Status int32 `gorm:"column:status;type:integer;not null;comment:代理服务状态0-离线1-在线" json:"status"` // 代理服务状态0-离线1-在线
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
Edges []Edge `gorm:"foreignKey:ProxyID;references:ID" json:"edges"`
}
// TableName Proxy's table name
func (*Proxy) TableName() string {
return TableNameProxy
}

38
web/models/proxy.go Normal file
View File

@@ -0,0 +1,38 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
"gorm.io/datatypes"
)
// Proxy 代理服务表
type Proxy struct {
core.Model
Version int32 `json:"version" gorm:"column:version"` // 代理服务版本
Mac string `json:"mac" gorm:"column:mac"` // 代理服务名称
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // 代理服务地址
Secret *string `json:"secret" gorm:"column:secret"` // 代理服务密钥
Type ProxyType `json:"type" gorm:"column:type"` // 代理服务类型1-自有2-白银
Status ProxyStatus `json:"status" gorm:"column:status"` // 代理服务状态0-离线1-在线
Meta *datatypes.JSONType[any] `json:"meta" gorm:"column:meta"` // 代理服务元信息
Channels []Channel `json:"channels" gorm:"foreignkey:ProxyID"`
}
// ProxyType 代理服务类型枚举
type ProxyType int
const (
ProxyTypeSelfHosted ProxyType = 1 // 自有
ProxyTypeBaiYin ProxyType = 2 // 白银
)
// ProxyStatus 代理服务状态枚举
type ProxyStatus int
const (
ProxyStatusOffline ProxyStatus = 0 // 离线
ProxyStatusOnline ProxyStatus = 1 // 在线
)

View File

@@ -1,32 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
const TableNameRefund = "refund"
// Refund mapped from table <refund>
type Refund struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:退款ID" json:"id"` // 退款ID
TradeID int32 `gorm:"column:trade_id;type:integer;not null;comment:订单ID" json:"trade_id"` // 订单ID
ProductID *int32 `gorm:"column:product_id;type:integer;comment:产品ID" json:"product_id"` // 产品ID
Amount decimal.Decimal `gorm:"column:amount;type:numeric(12,2);not null;comment:退款金额" json:"amount"` // 退款金额
Reason *string `gorm:"column:reason;type:character varying(255);comment:退款原因" json:"reason"` // 退款原因
Status int32 `gorm:"column:status;type:integer;not null;comment:退款状态0-待处理1-已退款2-已拒绝" json:"status"` // 退款状态0-待处理1-已退款2-已拒绝
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Refund's table name
func (*Refund) TableName() string {
return TableNameRefund
}

26
web/models/refund.go Normal file
View File

@@ -0,0 +1,26 @@
package models
import (
"platform/web/core"
"github.com/shopspring/decimal"
)
// Refund 退款记录表
type Refund struct {
core.Model
TradeID int32 `json:"trade_id" gorm:"column:trade_id"` // 订单ID
ProductID *int32 `json:"product_id" gorm:"column:product_id"` // 产品ID
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 退款金额
Reason *string `json:"reason" gorm:"column:reason"` // 退款原因
Status RefundStatus `json:"status" gorm:"column:status"` // 退款状态0-待处理1-已退款2-已拒绝
}
// RefundStatus 退款状态枚举
type RefundStatus int
const (
RefundStatusPending RefundStatus = 0 // 待处理
RefundStatusRefunded RefundStatus = 1 // 已退款
RefundStatusRejected RefundStatus = 2 // 已拒绝
)

View File

@@ -1,32 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameResource = "resource"
// Resource mapped from table <resource>
type Resource struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:套餐ID" json:"id"` // 套餐ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
ResourceNo *string `gorm:"column:resource_no;type:character varying(255);comment:套餐编号" json:"resource_no"` // 套餐编号
Active bool `gorm:"column:active;type:boolean;not null;default:true;comment:套餐状态" json:"active"` // 套餐状态
Type int32 `gorm:"column:type;type:integer;not null;comment:套餐类型1-短效动态2-长效动态" json:"type"` // 套餐类型1-短效动态2-长效动态
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
Short *ResourceShort `gorm:"foreignKey:ResourceID;references:ID" json:"short"`
Long *ResourceLong `gorm:"foreignKey:ResourceID;references:ID" json:"long"`
}
// TableName Resource's table name
func (*Resource) TableName() string {
return TableNameResource
}

34
web/models/resource.go Normal file
View File

@@ -0,0 +1,34 @@
package models
import (
"platform/web/core"
)
// Resource 套餐表
type Resource struct {
core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ResourceNo *string `json:"resource_no" gorm:"column:resource_no"` // 套餐编号
Active bool `json:"active" gorm:"column:active"` // 套餐状态
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型1-短效动态2-长效动态
User User `json:"user" gorm:"foreignKey:UserID"`
Short *ResourceShort `json:"short" gorm:"foreignKey:ResourceID"`
Long *ResourceLong `json:"long" gorm:"foreignKey:ResourceID"`
}
// ResourceType 套餐类型枚举
type ResourceType int
const (
ResourceTypeShort ResourceType = 1 // 短效动态
ResourceTypeLong ResourceType = 2 // 长效动态
)
// ResourceLongType 套餐计费模式枚举
type ResourceMode int
const (
ResourceModeTime ResourceMode = 1 // 包时
ResourceModeQuota ResourceMode = 2 // 包量
)

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameResourceLong = "resource_long"
// ResourceLong mapped from table <resource_long>
type ResourceLong struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
ResourceID int32 `gorm:"column:resource_id;type:integer;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
Type int32 `gorm:"column:type;type:integer;not null;comment:套餐类型1-包时2-包量" json:"type"` // 套餐类型1-包时2-包量
Live int32 `gorm:"column:live;type:integer;not null;comment:可用时长(天)" json:"live"` // 可用时长(天)
Expire *orm.LocalDateTime `gorm:"column:expire;type:timestamp without time zone;comment:过期时间" json:"expire"` // 过期时间
Quota *int32 `gorm:"column:quota;type:integer;comment:配额数量" json:"quota"` // 配额数量
Used int32 `gorm:"column:used;type:integer;not null;comment:已用数量" json:"used"` // 已用数量
DailyLimit int32 `gorm:"column:daily_limit;type:integer;not null;comment:每日限制" json:"daily_limit"` // 每日限制
DailyUsed int32 `gorm:"column:daily_used;type:integer;not null;comment:今日已用数量" json:"daily_used"` // 今日已用数量
DailyLast *orm.LocalDateTime `gorm:"column:daily_last;type:timestamp without time zone;comment:今日最后使用时间" json:"daily_last"` // 今日最后使用时间
}
// TableName ResourceLong's table name
func (*ResourceLong) TableName() string {
return TableNameResourceLong
}

View File

@@ -0,0 +1,19 @@
package models
import (
"time"
)
// ResourceLong 长效动态套餐表
type ResourceLong struct {
ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Live int32 `json:"live" gorm:"column:live"` // 可用时长(天)
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量
Used int32 `json:"used" gorm:"column:used"` // 已用数量
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数量
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间
}

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import "platform/web/globals/orm"
const TableNameResourceShort = "resource_short"
// ResourceShort mapped from table <resource_short>
type ResourceShort struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID
ResourceID int32 `gorm:"column:resource_id;type:integer;not null;comment:套餐ID" json:"resource_id"` // 套餐ID
Type int32 `gorm:"column:type;type:integer;not null;comment:套餐类型1-包时2-包量" json:"type"` // 套餐类型1-包时2-包量
Live int32 `gorm:"column:live;type:integer;not null;comment:可用时长(秒)" json:"live"` // 可用时长(秒)
Expire *orm.LocalDateTime `gorm:"column:expire;type:timestamp without time zone;comment:过期时间" json:"expire"` // 过期时间
Quota *int32 `gorm:"column:quota;type:integer;comment:配额数量" json:"quota"` // 配额数量
Used int32 `gorm:"column:used;type:integer;not null;comment:已用数量" json:"used"` // 已用数量
DailyLimit int32 `gorm:"column:daily_limit;type:integer;not null;comment:每日限制" json:"daily_limit"` // 每日限制
DailyUsed int32 `gorm:"column:daily_used;type:integer;not null;comment:今日已用数量" json:"daily_used"` // 今日已用数量
DailyLast *orm.LocalDateTime `gorm:"column:daily_last;type:timestamp without time zone;comment:今日最后使用时间" json:"daily_last"` // 今日最后使用时间
}
// TableName ResourceShort's table name
func (*ResourceShort) TableName() string {
return TableNameResourceShort
}

View File

@@ -0,0 +1,19 @@
package models
import (
"time"
)
// ResourceShort 短效动态套餐表
type ResourceShort struct {
ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒)
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量
Used int32 `json:"used" gorm:"column:used"` // 已用数量
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数量
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间
}

View File

@@ -1,39 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameSession = "session"
// Session mapped from table <session>
type Session struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:会话ID" json:"id"` // 会话ID
UserID *int32 `gorm:"column:user_id;type:integer;comment:用户ID" json:"user_id"` // 用户ID
AdminID *int32 `gorm:"column:admin_id;type:integer;comment:管理员ID" json:"admin_id"` // 管理员ID
ClientID *int32 `gorm:"column:client_id;type:integer;comment:客户端ID" json:"client_id"` // 客户端ID
IP *string `gorm:"column:ip;type:character varying(45);comment:IP地址" json:"ip"` // IP地址
UA *string `gorm:"column:ua;type:character varying(255);comment:用户代理" json:"ua"` // 用户代理
AccessToken string `gorm:"column:access_token;type:character varying(255);not null;comment:访问令牌" json:"access_token"` // 访问令牌
AccessTokenExpires orm.LocalDateTime `gorm:"column:access_token_expires;type:timestamp without time zone;not null;comment:访问令牌过期时间" json:"access_token_expires"` // 访问令牌过期时间
RefreshToken *string `gorm:"column:refresh_token;type:character varying(255);comment:刷新令牌" json:"refresh_token"` // 刷新令牌
RefreshTokenExpires *orm.LocalDateTime `gorm:"column:refresh_token_expires;type:timestamp without time zone;comment:刷新令牌过期时间" json:"refresh_token_expires"` // 刷新令牌过期时间
Scopes_ *string `gorm:"column:scopes;type:character varying(255);comment:权限范围" json:"scopes"` // 权限范围
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
User *User `gorm:"foreignKey:UserID" json:"user"`
Admin *Admin `gorm:"foreignKey:UserID" json:"admin"`
Client *Client `gorm:"belongsTo:ID;foreignKey:ClientID" json:"client"`
}
// TableName Session's table name
func (*Session) TableName() string {
return TableNameSession
}

26
web/models/session.go Normal file
View File

@@ -0,0 +1,26 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
"time"
)
// Session 会话表
type Session struct {
core.Model
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
AdminID *int32 `json:"admin_id" gorm:"column:admin_id"` // 管理员ID
ClientID *int32 `json:"client_id" gorm:"column:client_id"` // 客户端ID
IP *orm.Inet `json:"ip" gorm:"column:ip"` // IP地址
UA *string `json:"ua" gorm:"column:ua"` // 用户代理
AccessToken string `json:"access_token" gorm:"column:access_token"` // 访问令牌
AccessTokenExpires time.Time `json:"access_token_expires" gorm:"column:access_token_expires"` // 访问令牌过期时间
RefreshToken *string `json:"refresh_token" gorm:"column:refresh_token"` // 刷新令牌
RefreshTokenExpires *time.Time `json:"refresh_token_expires" gorm:"column:refresh_token_expires"` // 刷新令牌过期时间
Scopes *string `json:"scopes" gorm:"column:scopes"` // 权限范围
User *User `json:"user" gorm:"foreignKey:UserID"`
Admin *Admin `json:"admin" gorm:"foreignKey:AdminID"`
Client *Client `json:"client" gorm:"foreignKey:ClientID;belongsTo:ID"`
}

View File

@@ -1,43 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
const TableNameTrade = "trade"
// Trade mapped from table <trade>
type Trade struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:订单ID" json:"id"` // 订单ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
InnerNo string `gorm:"column:inner_no;type:character varying(255);not null;comment:内部订单号" json:"inner_no"` // 内部订单号
OuterNo *string `gorm:"column:outer_no;type:character varying(255);comment:外部订单号" json:"outer_no"` // 外部订单号
Type int32 `gorm:"column:type;type:integer;not null;comment:订单类型1-购买产品2-充值余额" json:"type"` // 订单类型1-购买产品2-充值余额
Subject string `gorm:"column:subject;type:character varying(255);not null;comment:订单主题" json:"subject"` // 订单主题
Remark *string `gorm:"column:remark;type:character varying(255);comment:订单备注" json:"remark"` // 订单备注
Amount decimal.Decimal `gorm:"column:amount;type:numeric(12,2);not null;comment:订单总金额" json:"amount"` // 订单总金额
Payment decimal.Decimal `gorm:"column:payment;type:numeric(12,2);not null;comment:实际支付金额" json:"payment"` // 实际支付金额
Method int32 `gorm:"column:method;type:integer;not null;comment:支付方式1-支付宝2-微信3-商福通4-商福通渠道支付宝5-商福通渠道微信" json:"method"` // 支付方式1-支付宝2-微信3-商福通4-商福通渠道支付宝5-商福通渠道微信
Platform int32 `gorm:"column:platform;type:integer;not null;comment:支付平台1-电脑网站2-手机网站" json:"platform"` // 支付平台1-电脑网站2-手机网站
Acquirer *int32 `gorm:"column:acquirer;type:integer;comment:收单机构1-支付宝2-微信3-银联" json:"acquirer"` // 收单机构1-支付宝2-微信3-银联
Status int32 `gorm:"column:status;type:integer;not null;comment:订单状态0-待支付1-已支付2-已取消" json:"status"` // 订单状态0-待支付1-已支付2-已取消
Refunded bool `gorm:"column:refunded;type:boolean;not null" json:"refunded"`
PaymentURL *string `gorm:"column:payment_url;type:text;comment:支付链接" json:"payment_url"` // 支付链接
CompletedAt *orm.LocalDateTime `gorm:"column:completed_at;type:timestamp without time zone;comment:支付时间" json:"completed_at"` // 支付时间
CanceledAt *orm.LocalDateTime `gorm:"column:canceled_at;type:timestamp without time zone;comment:取消时间" json:"canceled_at"` // 取消时间
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Trade's table name
func (*Trade) TableName() string {
return TableNameTrade
}

74
web/models/trade.go Normal file
View File

@@ -0,0 +1,74 @@
package models
import (
"platform/web/core"
"time"
"github.com/shopspring/decimal"
)
// Trade 订单表
type Trade struct {
core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
InnerNo string `json:"inner_no" gorm:"column:inner_no"` // 内部订单号
OuterNo *string `json:"outer_no" gorm:"column:outer_no"` // 外部订单号
Type TradeType `json:"type" gorm:"column:type"` // 订单类型1-购买产品2-充值余额
Subject string `json:"subject" gorm:"column:subject"` // 订单主题
Remark *string `json:"remark" gorm:"column:remark"` // 订单备注
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 订单总金额
Payment decimal.Decimal `json:"payment" gorm:"column:payment"` // 实际支付金额
Method TradeMethod `json:"method" gorm:"column:method"` // 支付方式1-支付宝2-微信3-商福通4-商福通渠道支付宝5-商福通渠道微信
Platform TradePlatform `json:"platform" gorm:"column:platform"` // 支付平台1-电脑网站2-手机网站
Acquirer *TradeAcquirer `json:"acquirer" gorm:"column:acquirer"` // 收单机构1-支付宝2-微信3-银联
Status TradeStatus `json:"status" gorm:"column:status"` // 订单状态0-待支付1-已支付2-已取消
Refunded bool `json:"refunded" gorm:"column:refunded"` // 是否已退款
PaymentURL *string `json:"payment_url" gorm:"column:payment_url"` // 支付链接
CompletedAt *time.Time `json:"completed_at" gorm:"column:completed_at"` // 支付时间
CanceledAt *time.Time `json:"canceled_at" gorm:"column:canceled_at"` // 取消时间
}
// TradeType 订单类型枚举
type TradeType int
const (
TradeTypePurchase TradeType = 1 // 购买产品
TradeTypeRecharge TradeType = 2 // 充值余额
)
// TradeMethod 支付方式枚举
type TradeMethod int
const (
TradeMethodAlipay TradeMethod = 1 // 支付宝
TradeMethodWechat TradeMethod = 2 // 微信
TradeMethodSft TradeMethod = 3 // 商福通
TradeMethodSftAlipay TradeMethod = 4 // 商福通渠道支付宝
TradeMethodSftWechat TradeMethod = 5 // 商福通渠道微信
)
// TradePlatform 支付平台枚举
type TradePlatform int
const (
TradePlatformPC TradePlatform = 1 // 电脑网站
TradePlatformMobile TradePlatform = 2 // 手机网站
)
// TradeAcquirer 收单机构枚举
type TradeAcquirer int
const (
TradeAcquirerAlipay TradeAcquirer = 1 // 支付宝
TradeAcquirerWechat TradeAcquirer = 2 // 微信
TradeAcquirerUnionPay TradeAcquirer = 3 // 银联
)
// TradeStatus 订单状态枚举
type TradeStatus int
const (
TradeStatusPending TradeStatus = 0 // 待支付
TradeStatusSuccess TradeStatus = 1 // 已支付
TradeStatusCanceled TradeStatus = 2 // 已取消
)

View File

@@ -1,44 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
const TableNameUser = "user"
// User mapped from table <user>
type User struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:用户ID" json:"id"` // 用户ID
AdminID *int32 `gorm:"column:admin_id;type:integer;comment:管理员ID" json:"admin_id"` // 管理员ID
Phone string `gorm:"column:phone;type:character varying(255);not null;comment:手机号码" json:"phone"` // 手机号码
Username *string `gorm:"column:username;type:character varying(255);comment:用户名" json:"username"` // 用户名
Email *string `gorm:"column:email;type:character varying(255)" json:"email"`
Password *string `gorm:"column:password;type:character varying(255);comment:用户密码" json:"password"` // 用户密码
Name *string `gorm:"column:name;type:character varying(255);comment:真实姓名" json:"name"` // 真实姓名
Avatar *string `gorm:"column:avatar;type:character varying(255);comment:头像URL" json:"avatar"` // 头像URL
Status int32 `gorm:"column:status;type:integer;not null;default:1;comment:用户状态0-禁用1-正常" json:"status"` // 用户状态0-禁用1-正常
Balance decimal.Decimal `gorm:"column:balance;type:numeric(12,2);not null;comment:账户余额" json:"balance"` // 账户余额
IDType int32 `gorm:"column:id_type;type:integer;not null;comment:认证类型0-未认证1-个人认证2-企业认证" json:"id_type"` // 认证类型0-未认证1-个人认证2-企业认证
IDNo *string `gorm:"column:id_no;type:character varying(255);comment:身份证号或营业执照号" json:"id_no"` // 身份证号或营业执照号
IDToken *string `gorm:"column:id_token;type:character varying(255);comment:身份验证标识" json:"id_token"` // 身份验证标识
ContactQQ *string `gorm:"column:contact_qq;type:character varying(255);comment:QQ联系方式" json:"contact_qq"` // QQ联系方式
ContactWechat *string `gorm:"column:contact_wechat;type:character varying(255);comment:微信联系方式" json:"contact_wechat"` // 微信联系方式
LastLogin *orm.LocalDateTime `gorm:"column:last_login;type:timestamp without time zone;comment:最后登录时间" json:"last_login"` // 最后登录时间
LastLoginHost *string `gorm:"column:last_login_host;type:character varying(45);comment:最后登录地址" json:"last_login_host"` // 最后登录地址
LastLoginAgent *string `gorm:"column:last_login_agent;type:character varying(255);comment:最后登录代理" json:"last_login_agent"` // 最后登录代理
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName User's table name
func (*User) TableName() string {
return TableNameUser
}

50
web/models/user.go Normal file
View File

@@ -0,0 +1,50 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
"time"
"github.com/shopspring/decimal"
)
// User 用户表
type User struct {
core.Model
AdminID *int32 `json:"admin_id" gorm:"column:admin_id"` // 管理员ID
Phone string `json:"phone" gorm:"column:phone"` // 手机号码
Username *string `json:"username" gorm:"column:username"` // 用户名
Email *string `json:"email" gorm:"column:email"` // 邮箱
Password *string `json:"password" gorm:"column:password"` // 用户密码
Name *string `json:"name" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar" gorm:"column:avatar"` // 头像URL
Status UserStatus `json:"status" gorm:"column:status"` // 用户状态0-禁用1-正常
Balance decimal.Decimal `json:"balance" gorm:"column:balance"` // 账户余额
IDType UserIDType `json:"id_type" gorm:"column:id_type"` // 认证类型0-未认证1-个人认证2-企业认证
IDNo *string `json:"id_no" gorm:"column:id_no"` // 身份证号或营业执照号
IDToken *string `json:"id_token" gorm:"column:id_token"` // 身份验证标识
ContactQQ *string `json:"contact_qq" gorm:"column:contact_qq"` // QQ联系方式
ContactWechat *string `json:"contact_wechat" gorm:"column:contact_wechat"` // 微信联系方式
LastLogin *time.Time `json:"last_login" gorm:"column:last_login"` // 最后登录时间
LastLoginIP *orm.Inet `json:"last_login_ip" gorm:"column:last_login_ip"` // 最后登录地址
LastLoginUA *string `json:"last_login_ua" gorm:"column:last_login_ua"` // 最后登录代理
Admin Admin `json:"admin" gorm:"foreignKey:AdminID"`
}
// UserStatus 用户状态枚举
type UserStatus int
const (
UserStatusDisabled UserStatus = 0 // 禁用
UserStatusEnabled UserStatus = 1 // 正常
)
// UserIDType 用户认证类型枚举
type UserIDType int
const (
UserIDTypeUnverified UserIDType = 0 // 未认证
UserIDTypePersonal UserIDType = 1 // 个人认证
UserIDTypeEnterprise UserIDType = 2 // 企业认证
)

View File

@@ -1,30 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameUserRole = "user_role"
// UserRole mapped from table <user_role>
type UserRole struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:角色ID" json:"id"` // 角色ID
Name string `gorm:"column:name;type:character varying(255);not null;comment:角色名称" json:"name"` // 角色名称
Description *string `gorm:"column:description;type:character varying(255);comment:角色描述" json:"description"` // 角色描述
Active *bool `gorm:"column:active;type:boolean;default:true;comment:是否激活" json:"active"` // 是否激活
Sort *int32 `gorm:"column:sort;type:integer;comment:排序" json:"sort"` // 排序
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName UserRole's table name
func (*UserRole) TableName() string {
return TableNameUserRole
}

14
web/models/user_role.go Normal file
View File

@@ -0,0 +1,14 @@
package models
import (
"platform/web/core"
)
// UserRole 用户角色表
type UserRole struct {
core.Model
Name string `json:"name" gorm:"column:name"` // 角色名称
Description *string `json:"description" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
}

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameUserRoleLink = "user_role_link"
// UserRoleLink mapped from table <user_role_link>
type UserRoleLink struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
RoleID int32 `gorm:"column:role_id;type:integer;not null;comment:角色ID" json:"role_id"` // 角色ID
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName UserRoleLink's table name
func (*UserRoleLink) TableName() string {
return TableNameUserRoleLink
}

View File

@@ -1,28 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameUserRolePermissionLink = "user_role_permission_link"
// UserRolePermissionLink mapped from table <user_role_permission_link>
type UserRolePermissionLink struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:关联ID" json:"id"` // 关联ID
RoleID int32 `gorm:"column:role_id;type:integer;not null;comment:角色ID" json:"role_id"` // 角色ID
PermissionID int32 `gorm:"column:permission_id;type:integer;not null;comment:权限ID" json:"permission_id"` // 权限ID
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName UserRolePermissionLink's table name
func (*UserRolePermissionLink) TableName() string {
return TableNameUserRolePermissionLink
}

View File

@@ -1,29 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package models
import (
"platform/web/globals/orm"
"gorm.io/gorm"
)
const TableNameWhitelist = "whitelist"
// Whitelist mapped from table <whitelist>
type Whitelist struct {
ID int32 `gorm:"column:id;type:integer;primaryKey;autoIncrement:true;comment:白名单ID" json:"id"` // 白名单ID
UserID int32 `gorm:"column:user_id;type:integer;not null;comment:用户ID" json:"user_id"` // 用户ID
Host string `gorm:"column:host;type:character varying(45);not null;comment:IP地址" json:"host"` // IP地址
Remark *string `gorm:"column:remark;type:character varying(255);comment:备注" json:"remark"` // 备注
CreatedAt *orm.LocalDateTime `gorm:"column:created_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt *orm.LocalDateTime `gorm:"column:updated_at;type:timestamp without time zone;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp without time zone;comment:删除时间" json:"deleted_at"` // 删除时间
}
// TableName Whitelist's table name
func (*Whitelist) TableName() string {
return TableNameWhitelist
}

14
web/models/whitelist.go Normal file
View File

@@ -0,0 +1,14 @@
package models
import (
"platform/web/core"
"platform/web/globals/orm"
)
// Whitelist 白名单表
type Whitelist struct {
core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // IP地址
Remark *string `json:"remark" gorm:"column:remark"` // 备注
}

View File

@@ -28,19 +28,19 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
tableName := _admin.adminDo.TableName() tableName := _admin.adminDo.TableName()
_admin.ALL = field.NewAsterisk(tableName) _admin.ALL = field.NewAsterisk(tableName)
_admin.ID = field.NewInt32(tableName, "id") _admin.ID = field.NewInt32(tableName, "id")
_admin.CreatedAt = field.NewTime(tableName, "created_at")
_admin.UpdatedAt = field.NewTime(tableName, "updated_at")
_admin.DeletedAt = field.NewField(tableName, "deleted_at")
_admin.Username = field.NewString(tableName, "username") _admin.Username = field.NewString(tableName, "username")
_admin.Password = field.NewString(tableName, "password") _admin.Password = field.NewString(tableName, "password")
_admin.Name = field.NewString(tableName, "name") _admin.Name = field.NewString(tableName, "name")
_admin.Avatar = field.NewString(tableName, "avatar") _admin.Avatar = field.NewString(tableName, "avatar")
_admin.Phone = field.NewString(tableName, "phone") _admin.Phone = field.NewString(tableName, "phone")
_admin.Email = field.NewString(tableName, "email") _admin.Email = field.NewString(tableName, "email")
_admin.Status = field.NewInt32(tableName, "status") _admin.Status = field.NewInt(tableName, "status")
_admin.LastLogin = field.NewField(tableName, "last_login") _admin.LastLogin = field.NewTime(tableName, "last_login")
_admin.LastLoginHost = field.NewString(tableName, "last_login_host") _admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
_admin.LastLoginAgent = field.NewString(tableName, "last_login_agent") _admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
_admin.CreatedAt = field.NewField(tableName, "created_at")
_admin.UpdatedAt = field.NewField(tableName, "updated_at")
_admin.DeletedAt = field.NewField(tableName, "deleted_at")
_admin.fillFieldMap() _admin.fillFieldMap()
@@ -50,21 +50,21 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
type admin struct { type admin struct {
adminDo adminDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 // 管理员ID ID field.Int32
Username field.String // 用户名 CreatedAt field.Time
Password field.String // 密码 UpdatedAt field.Time
Name field.String // 真实姓名 DeletedAt field.Field
Avatar field.String // 头像URL Username field.String
Phone field.String // 手机号码 Password field.String
Email field.String // 邮箱 Name field.String
Status field.Int32 // 状态0-禁用1-正常 Avatar field.String
LastLogin field.Field // 最后登录时间 Phone field.String
LastLoginHost field.String // 最后登录地址 Email field.String
LastLoginAgent field.String // 最后登录代理 Status field.Int
CreatedAt field.Field // 创建时间 LastLogin field.Time
UpdatedAt field.Field // 更新时间 LastLoginIP field.Field
DeletedAt field.Field // 删除时间 LastLoginUA field.String
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -82,19 +82,19 @@ func (a admin) As(alias string) *admin {
func (a *admin) updateTableName(table string) *admin { func (a *admin) updateTableName(table string) *admin {
a.ALL = field.NewAsterisk(table) a.ALL = field.NewAsterisk(table)
a.ID = field.NewInt32(table, "id") a.ID = field.NewInt32(table, "id")
a.CreatedAt = field.NewTime(table, "created_at")
a.UpdatedAt = field.NewTime(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.Username = field.NewString(table, "username") a.Username = field.NewString(table, "username")
a.Password = field.NewString(table, "password") a.Password = field.NewString(table, "password")
a.Name = field.NewString(table, "name") a.Name = field.NewString(table, "name")
a.Avatar = field.NewString(table, "avatar") a.Avatar = field.NewString(table, "avatar")
a.Phone = field.NewString(table, "phone") a.Phone = field.NewString(table, "phone")
a.Email = field.NewString(table, "email") a.Email = field.NewString(table, "email")
a.Status = field.NewInt32(table, "status") a.Status = field.NewInt(table, "status")
a.LastLogin = field.NewField(table, "last_login") a.LastLogin = field.NewTime(table, "last_login")
a.LastLoginHost = field.NewString(table, "last_login_host") a.LastLoginIP = field.NewField(table, "last_login_ip")
a.LastLoginAgent = field.NewString(table, "last_login_agent") a.LastLoginUA = field.NewString(table, "last_login_ua")
a.CreatedAt = field.NewField(table, "created_at")
a.UpdatedAt = field.NewField(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.fillFieldMap() a.fillFieldMap()
@@ -113,6 +113,9 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
func (a *admin) fillFieldMap() { func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 14) a.fieldMap = make(map[string]field.Expr, 14)
a.fieldMap["id"] = a.ID a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["username"] = a.Username a.fieldMap["username"] = a.Username
a.fieldMap["password"] = a.Password a.fieldMap["password"] = a.Password
a.fieldMap["name"] = a.Name a.fieldMap["name"] = a.Name
@@ -121,11 +124,8 @@ func (a *admin) fillFieldMap() {
a.fieldMap["email"] = a.Email a.fieldMap["email"] = a.Email
a.fieldMap["status"] = a.Status a.fieldMap["status"] = a.Status
a.fieldMap["last_login"] = a.LastLogin a.fieldMap["last_login"] = a.LastLogin
a.fieldMap["last_login_host"] = a.LastLoginHost a.fieldMap["last_login_ip"] = a.LastLoginIP
a.fieldMap["last_login_agent"] = a.LastLoginAgent a.fieldMap["last_login_ua"] = a.LastLoginUA
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
} }
func (a admin) clone(db *gorm.DB) admin { func (a admin) clone(db *gorm.DB) admin {

View File

@@ -28,13 +28,13 @@ func newAdminRole(db *gorm.DB, opts ...gen.DOOption) adminRole {
tableName := _adminRole.adminRoleDo.TableName() tableName := _adminRole.adminRoleDo.TableName()
_adminRole.ALL = field.NewAsterisk(tableName) _adminRole.ALL = field.NewAsterisk(tableName)
_adminRole.ID = field.NewInt32(tableName, "id") _adminRole.ID = field.NewInt32(tableName, "id")
_adminRole.CreatedAt = field.NewTime(tableName, "created_at")
_adminRole.UpdatedAt = field.NewTime(tableName, "updated_at")
_adminRole.DeletedAt = field.NewField(tableName, "deleted_at")
_adminRole.Name = field.NewString(tableName, "name") _adminRole.Name = field.NewString(tableName, "name")
_adminRole.Description = field.NewString(tableName, "description") _adminRole.Description = field.NewString(tableName, "description")
_adminRole.Active = field.NewBool(tableName, "active") _adminRole.Active = field.NewBool(tableName, "active")
_adminRole.Sort = field.NewInt32(tableName, "sort") _adminRole.Sort = field.NewInt32(tableName, "sort")
_adminRole.CreatedAt = field.NewField(tableName, "created_at")
_adminRole.UpdatedAt = field.NewField(tableName, "updated_at")
_adminRole.DeletedAt = field.NewField(tableName, "deleted_at")
_adminRole.fillFieldMap() _adminRole.fillFieldMap()
@@ -45,14 +45,14 @@ type adminRole struct {
adminRoleDo adminRoleDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 // 管理员角色ID ID field.Int32
Name field.String // 角色名称 CreatedAt field.Time
Description field.String // 角色描述 UpdatedAt field.Time
Active field.Bool // 是否激活 DeletedAt field.Field
Sort field.Int32 // 排序 Name field.String
CreatedAt field.Field // 创建时间 Description field.String
UpdatedAt field.Field // 更新时间 Active field.Bool
DeletedAt field.Field // 删除时间 Sort field.Int32
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -70,13 +70,13 @@ func (a adminRole) As(alias string) *adminRole {
func (a *adminRole) updateTableName(table string) *adminRole { func (a *adminRole) updateTableName(table string) *adminRole {
a.ALL = field.NewAsterisk(table) a.ALL = field.NewAsterisk(table)
a.ID = field.NewInt32(table, "id") a.ID = field.NewInt32(table, "id")
a.CreatedAt = field.NewTime(table, "created_at")
a.UpdatedAt = field.NewTime(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.Name = field.NewString(table, "name") a.Name = field.NewString(table, "name")
a.Description = field.NewString(table, "description") a.Description = field.NewString(table, "description")
a.Active = field.NewBool(table, "active") a.Active = field.NewBool(table, "active")
a.Sort = field.NewInt32(table, "sort") a.Sort = field.NewInt32(table, "sort")
a.CreatedAt = field.NewField(table, "created_at")
a.UpdatedAt = field.NewField(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.fillFieldMap() a.fillFieldMap()
@@ -95,13 +95,13 @@ func (a *adminRole) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
func (a *adminRole) fillFieldMap() { func (a *adminRole) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 8) a.fieldMap = make(map[string]field.Expr, 8)
a.fieldMap["id"] = a.ID a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["name"] = a.Name a.fieldMap["name"] = a.Name
a.fieldMap["description"] = a.Description a.fieldMap["description"] = a.Description
a.fieldMap["active"] = a.Active a.fieldMap["active"] = a.Active
a.fieldMap["sort"] = a.Sort a.fieldMap["sort"] = a.Sort
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
} }
func (a adminRole) clone(db *gorm.DB) adminRole { func (a adminRole) clone(db *gorm.DB) adminRole {

View File

@@ -1,339 +0,0 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package queries
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"platform/web/models"
)
func newAdminRoleLink(db *gorm.DB, opts ...gen.DOOption) adminRoleLink {
_adminRoleLink := adminRoleLink{}
_adminRoleLink.adminRoleLinkDo.UseDB(db, opts...)
_adminRoleLink.adminRoleLinkDo.UseModel(&models.AdminRoleLink{})
tableName := _adminRoleLink.adminRoleLinkDo.TableName()
_adminRoleLink.ALL = field.NewAsterisk(tableName)
_adminRoleLink.ID = field.NewInt32(tableName, "id")
_adminRoleLink.AdminID = field.NewInt32(tableName, "admin_id")
_adminRoleLink.RoleID = field.NewInt32(tableName, "role_id")
_adminRoleLink.CreatedAt = field.NewField(tableName, "created_at")
_adminRoleLink.UpdatedAt = field.NewField(tableName, "updated_at")
_adminRoleLink.DeletedAt = field.NewField(tableName, "deleted_at")
_adminRoleLink.fillFieldMap()
return _adminRoleLink
}
type adminRoleLink struct {
adminRoleLinkDo
ALL field.Asterisk
ID field.Int32 // 关联ID
AdminID field.Int32 // 管理员ID
RoleID field.Int32 // 角色ID
CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间
fieldMap map[string]field.Expr
}
func (a adminRoleLink) Table(newTableName string) *adminRoleLink {
a.adminRoleLinkDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a adminRoleLink) As(alias string) *adminRoleLink {
a.adminRoleLinkDo.DO = *(a.adminRoleLinkDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *adminRoleLink) updateTableName(table string) *adminRoleLink {
a.ALL = field.NewAsterisk(table)
a.ID = field.NewInt32(table, "id")
a.AdminID = field.NewInt32(table, "admin_id")
a.RoleID = field.NewInt32(table, "role_id")
a.CreatedAt = field.NewField(table, "created_at")
a.UpdatedAt = field.NewField(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.fillFieldMap()
return a
}
func (a *adminRoleLink) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *adminRoleLink) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 6)
a.fieldMap["id"] = a.ID
a.fieldMap["admin_id"] = a.AdminID
a.fieldMap["role_id"] = a.RoleID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
}
func (a adminRoleLink) clone(db *gorm.DB) adminRoleLink {
a.adminRoleLinkDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a adminRoleLink) replaceDB(db *gorm.DB) adminRoleLink {
a.adminRoleLinkDo.ReplaceDB(db)
return a
}
type adminRoleLinkDo struct{ gen.DO }
func (a adminRoleLinkDo) Debug() *adminRoleLinkDo {
return a.withDO(a.DO.Debug())
}
func (a adminRoleLinkDo) WithContext(ctx context.Context) *adminRoleLinkDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a adminRoleLinkDo) ReadDB() *adminRoleLinkDo {
return a.Clauses(dbresolver.Read)
}
func (a adminRoleLinkDo) WriteDB() *adminRoleLinkDo {
return a.Clauses(dbresolver.Write)
}
func (a adminRoleLinkDo) Session(config *gorm.Session) *adminRoleLinkDo {
return a.withDO(a.DO.Session(config))
}
func (a adminRoleLinkDo) Clauses(conds ...clause.Expression) *adminRoleLinkDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a adminRoleLinkDo) Returning(value interface{}, columns ...string) *adminRoleLinkDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a adminRoleLinkDo) Not(conds ...gen.Condition) *adminRoleLinkDo {
return a.withDO(a.DO.Not(conds...))
}
func (a adminRoleLinkDo) Or(conds ...gen.Condition) *adminRoleLinkDo {
return a.withDO(a.DO.Or(conds...))
}
func (a adminRoleLinkDo) Select(conds ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Select(conds...))
}
func (a adminRoleLinkDo) Where(conds ...gen.Condition) *adminRoleLinkDo {
return a.withDO(a.DO.Where(conds...))
}
func (a adminRoleLinkDo) Order(conds ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Order(conds...))
}
func (a adminRoleLinkDo) Distinct(cols ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a adminRoleLinkDo) Omit(cols ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a adminRoleLinkDo) Join(table schema.Tabler, on ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a adminRoleLinkDo) LeftJoin(table schema.Tabler, on ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a adminRoleLinkDo) RightJoin(table schema.Tabler, on ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a adminRoleLinkDo) Group(cols ...field.Expr) *adminRoleLinkDo {
return a.withDO(a.DO.Group(cols...))
}
func (a adminRoleLinkDo) Having(conds ...gen.Condition) *adminRoleLinkDo {
return a.withDO(a.DO.Having(conds...))
}
func (a adminRoleLinkDo) Limit(limit int) *adminRoleLinkDo {
return a.withDO(a.DO.Limit(limit))
}
func (a adminRoleLinkDo) Offset(offset int) *adminRoleLinkDo {
return a.withDO(a.DO.Offset(offset))
}
func (a adminRoleLinkDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *adminRoleLinkDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a adminRoleLinkDo) Unscoped() *adminRoleLinkDo {
return a.withDO(a.DO.Unscoped())
}
func (a adminRoleLinkDo) Create(values ...*models.AdminRoleLink) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a adminRoleLinkDo) CreateInBatches(values []*models.AdminRoleLink, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a adminRoleLinkDo) Save(values ...*models.AdminRoleLink) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a adminRoleLinkDo) First() (*models.AdminRoleLink, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.AdminRoleLink), nil
}
}
func (a adminRoleLinkDo) Take() (*models.AdminRoleLink, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.AdminRoleLink), nil
}
}
func (a adminRoleLinkDo) Last() (*models.AdminRoleLink, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.AdminRoleLink), nil
}
}
func (a adminRoleLinkDo) Find() ([]*models.AdminRoleLink, error) {
result, err := a.DO.Find()
return result.([]*models.AdminRoleLink), err
}
func (a adminRoleLinkDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.AdminRoleLink, err error) {
buf := make([]*models.AdminRoleLink, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a adminRoleLinkDo) FindInBatches(result *[]*models.AdminRoleLink, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a adminRoleLinkDo) Attrs(attrs ...field.AssignExpr) *adminRoleLinkDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a adminRoleLinkDo) Assign(attrs ...field.AssignExpr) *adminRoleLinkDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a adminRoleLinkDo) Joins(fields ...field.RelationField) *adminRoleLinkDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a adminRoleLinkDo) Preload(fields ...field.RelationField) *adminRoleLinkDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a adminRoleLinkDo) FirstOrInit() (*models.AdminRoleLink, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.AdminRoleLink), nil
}
}
func (a adminRoleLinkDo) FirstOrCreate() (*models.AdminRoleLink, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.AdminRoleLink), nil
}
}
func (a adminRoleLinkDo) FindByPage(offset int, limit int) (result []*models.AdminRoleLink, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a adminRoleLinkDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a adminRoleLinkDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a adminRoleLinkDo) Delete(models ...*models.AdminRoleLink) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *adminRoleLinkDo) withDO(do gen.Dao) *adminRoleLinkDo {
a.DO = *do.(*gen.DO)
return a
}

Some files were not shown because too many files have changed in this diff Show More