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

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
日志记录,使用任务来实现
限制提取单次最大量
支付后事件,需要检测是否已完成操作
支付后异步任务,到时间后需要尝试完成订单,如果无法完成再关闭
重新实现接口 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 网关更新接口可以传输更结构化的数据,直接区分不同类型以加快更新速度
## 业务逻辑
### 订单关闭的几种方式
1. 创建订单后推送异步任务,到时间后尝试关闭订单
1. 创建订单后推送异步任务,到时间后尝试完成订单或关闭订单
2. sse 接口推送订单状态,轮询尝试完成订单
3. 异步回调事件,收到支付成功事件后自动完成订单
4. 用户退出支付界面,客户端主动发起关闭订单
### 产品字典表
| 代码 | 产品 |
|-------|------|
| short | 短效代理 |
| long | 长效代理 |
| 代码 | 产品 |
| ----- | ------------ |
| short | 短效动态代理 |
| long | 长效动态代理 |
## 问题备忘录
### 节点分配与存储逻辑
### 商福通支付接口的同步跳转参数
添加:
- 检查用户 ip 是否在白名单内
- 取用端口,不够则返回失败
- 将分配结果转写成配置发送到网关
- 保存通道信息和分配记录,其中通道信息以网关为主体,分配记录以用户为主体
- 添加异步任务,当时间结束后释放取用的端口并清空网关配置
部分通道支持这个参数,银盛和汇付不支持这个参数
删除:
- 如果传入用户信息,检查要删除的连接是否属于该用户
- 释放可用端口
- redis 脚本中检查,如果端口所属节点已下线则直接忽略
- 提交清空配置到网关
缩扩容:
- 通过调度任务实现缩扩容
- 每 n 秒检查一次全部配置,按代理分组
- 获取所有代理后备配置
- 后备配置/当前配置
- 当比例 < 1.5 或 > 3 时,重新更新为 2 倍

View File

@@ -1,11 +1,10 @@
package main
import (
"strings"
m "platform/web/models"
"gorm.io/driver/postgres"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
@@ -26,149 +25,42 @@ func main() {
g = gen.NewGenerator(gen.Config{
OutPath: "web/queries",
ModelPkgPath: "models",
FieldNullable: true,
FieldSignable: true,
FieldWithTypeTag: true,
Mode: gen.WithDefaultQuery | gen.WithoutContext,
})
g.UseDB(db)
// 公共参数
common := []gen.ModelOpt{
gen.FieldModify(func(field gen.Field) gen.Field {
switch {
case field.Type == "*time.Time":
field.Type = "*orm.LocalDateTime"
case field.Type == "time.Time":
field.Type = "orm.LocalDateTime"
case strings.Contains(field.Tags(), "numeric"):
field.Type = "decimal.Decimal"
}
return field
}),
gen.FieldRename("contact_qq", "ContactQQ"),
gen.FieldRename("ua", "UA"),
}
// 生成模型
customs := make(map[string]any)
// resource
resourceShort := g.GenerateModel("resource_short", common...)
customs["resource_short"] = resourceShort
resourceLong := g.GenerateModel("resource_long", common...)
customs["resource_long"] = resourceLong
resource := g.GenerateModel("resource", append(common,
gen.FieldRelate(field.HasOne, "Short", resourceShort, &field.RelateConfig{
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.ApplyBasic(
m.Admin{},
m.AdminRole{},
m.Announcement{},
m.Bill{},
m.Channel{},
m.Client{},
m.Coupon{},
m.Edge{},
m.LinkAdminRole{},
m.LinkAdminRolePermission{},
m.LinkClientPermission{},
m.LinkUserRole{},
m.LinkUserRolePermission{},
m.LogsLogin{},
m.LogsRequest{},
m.LogsUserBandwidth{},
m.LogsUserUsage{},
m.Permission{},
m.Product{},
m.Proxy{},
m.Refund{},
m.Resource{},
m.ResourceLong{},
m.ResourceShort{},
m.Session{},
m.Trade{},
m.User{},
m.UserRole{},
m.Whitelist{},
)
g.Execute()
}

View File

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

View File

@@ -5,11 +5,36 @@ import (
"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 {
return &v
}
// Z 转换值为不可空,如果值为 nil则返回其零值
func Z[T any](v *T) T {
if v == nil {
var zero T
@@ -18,6 +43,7 @@ func Z[T any](v *T) T {
return *v
}
// X 转换值为可空,如果值为零值,则返回 nil
func X[T comparable](v T) *T {
var zero T
if v == zero {
@@ -26,28 +52,50 @@ func X[T comparable](v T) *T {
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 {
return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
}
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
func Map[T any, R any](src []T, convert func(T) R) []R {
dst := make([]R, len(src))
for i, item := range src {
dst[i] = convert(item)
}
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 {
var combinedErr error = nil
for _, err := range errs {

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -10,7 +10,6 @@ import (
"platform/pkg/env"
"platform/pkg/u"
"platform/web/core"
user2 "platform/web/domains/user"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
@@ -162,7 +161,7 @@ func Token(c *fiber.Ctx) error {
AccessToken: session.AccessToken,
RefreshToken: u.Z(session.RefreshToken),
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(
q.User.ID.Eq(codeCtx.UserID),
q.User.Status.Eq(int32(user2.StatusEnabled)),
q.User.Status.Eq(int(m.UserStatusEnabled)),
).First()
if err != nil {
return nil, err
@@ -211,18 +210,20 @@ func authAuthorizationCode(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now tim
// todo 检查 scope
// 生成会话
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
session := &m.Session{
IP: u.X(ctx.IP()),
UA: u.X(ctx.Get(fiber.HeaderUserAgent)),
IP: ip,
UA: u.X(ua),
UserID: &user.ID,
ClientID: &auth.Client.ID,
Scopes_: u.P(strings.Join(codeCtx.Scopes, " ")),
Scopes: u.P(strings.Join(codeCtx.Scopes, " ")),
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 {
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)
@@ -237,12 +238,14 @@ func authClientCredential(ctx *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.T
// todo 检查 scope
// 生成会话
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
session := &m.Session{
IP: u.X(ctx.IP()),
UA: u.X(ctx.Get(fiber.HeaderUserAgent)),
IP: ip,
UA: u.X(ua),
ClientID: &auth.Client.ID,
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) {
ip, _ := orm.ParseInet(ctx.Get(core.HeaderUserIP))
ua := ctx.Get(core.HeaderUserUA)
var user *m.User
err := q.Q.Transaction(func(tx *q.Query) (err error) {
switch req.LoginType {
@@ -267,7 +273,7 @@ func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (
user = &m.User{
Phone: req.Username,
Username: u.P(req.Username),
Status: int32(user2.StatusEnabled),
Status: m.UserStatusEnabled,
}
}
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)
return core.NewBizErr("账号无法登录")
}
// 更新用户的登录时间
user.LastLogin = u.P(orm.LocalDateTime(time.Now()))
user.LastLoginHost = u.X(ctx.IP())
user.LastLoginAgent = u.X(ctx.Get(fiber.HeaderUserAgent))
user.LastLogin = u.P(time.Now())
user.LastLoginIP = ip
user.LastLoginUA = u.X(ua)
if err := tx.User.Save(user); err != nil {
return err
}
@@ -306,17 +312,17 @@ func authPassword(ctx *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (
// 生成会话
session := &m.Session{
IP: u.X(ctx.IP()),
UA: u.X(ctx.Get(fiber.HeaderUserAgent)),
IP: ip,
UA: u.X(ua),
UserID: &user.ID,
ClientID: &auth.Client.ID,
Scopes_: u.X(req.Scope),
Scopes: u.X(req.Scope),
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 {
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)
@@ -340,10 +346,10 @@ func authRefreshToken(_ *fiber.Ctx, _ *AuthCtx, req *TokenReq, now time.Time) (*
// 生成令牌
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 {
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
import (
"platform/web/domains/client"
m "platform/web/models"
"github.com/gofiber/fiber/v2"
@@ -40,8 +39,7 @@ func (a *AuthCtx) PermitSecretClient(scopes ...string) (*AuthCtx, error) {
if a.Client == nil {
return a, ErrAuthenticateForbidden
}
spec := client.Spec(a.Client.Spec)
if spec != client.SpecApi && spec != client.SpecWeb {
if a.Client.Spec != m.ClientSpecAPI && a.Client.Spec != m.ClientSpecWeb {
return a, ErrAuthenticateForbidden
}
if !a.checkScopes(scopes...) {
@@ -50,16 +48,14 @@ func (a *AuthCtx) PermitSecretClient(scopes ...string) (*AuthCtx, error) {
return a, nil
}
func (a *AuthCtx) PermitInternalClient(scopes ...string) (*AuthCtx, error) {
func (a *AuthCtx) PermitOfficialClient(scopes ...string) (*AuthCtx, error) {
if a.Client == nil {
return a, ErrAuthenticateForbidden
}
spec := client.Spec(a.Client.Spec)
if spec != client.SpecApi && spec != client.SpecWeb {
if a.Client.Spec != m.ClientSpecAPI && a.Client.Spec != m.ClientSpecWeb {
return a, ErrAuthenticateForbidden
}
cType := client.Type(a.Client.Type)
if cType != client.TypeInternal {
if a.Client.Type != m.ClientTypeOfficial {
return a, ErrAuthenticateForbidden
}
if !a.checkScopes(scopes...) {

View File

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

View File

@@ -6,49 +6,6 @@ import (
"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 {
msg string
err error
@@ -109,5 +66,3 @@ type ServErr struct{ Err }
func NewServErr(msg string, err ...error) *ServErr {
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 (
"encoding/json"
"github.com/hibiken/asynq"
"log/slog"
"github.com/hibiken/asynq"
)
const RemoveChannel = "channel:remove"
func NewRemoveChannel(ids []int32) *asynq.Task {
bytes, err := json.Marshal(ids)
type RemoveChannelData struct {
Batch string `json:"batch"`
IDs []int32 `json:"ids"`
}
func NewRemoveChannel(data RemoveChannelData) *asynq.Task {
bytes, err := json.Marshal(data)
if err != nil {
slog.Error("序列化删除通道任务失败", "error", err)
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 (
"encoding/json"
"log/slog"
trade2 "platform/web/domains/trade"
m "platform/web/models"
"github.com/hibiken/asynq"
)
@@ -12,7 +13,7 @@ const CancelTrade = "trade:update"
type CancelTradeData struct {
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 {

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",
}
//goland:noinspection GoMixedReceiverTypes
func (ldt *LocalDateTime) Scan(value interface{}) (err error) {
func (ldt *LocalDateTime) Scan(value any) (err error) {
var t time.Time
if strValue, ok := value.(string); ok {
var timeValue time.Time
@@ -50,35 +49,26 @@ func (ldt *LocalDateTime) Scan(value interface{}) (err error) {
return
}
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) Value() (driver.Value, error) {
return time.Time(ldt).Local(), nil
}
// GormDataType gorm common data type
//
//goland:noinspection GoMixedReceiverTy
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) GormDataType() string {
return "ldt"
}
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) GobEncode() ([]byte, error) {
return time.Time(ldt).GobEncode()
}
//goland:noinspection GoMixedReceiverTypes
func (ldt *LocalDateTime) GobDecode(b []byte) error {
return (*time.Time)(ldt).GobDecode(b)
}
//goland:noinspection GoMixedReceiverTypes
func (ldt LocalDateTime) MarshalJSON() ([]byte, error) {
return time.Time(ldt).MarshalJSON()
}
//goland:noinspection GoMixedReceiverTypes
func (ldt *LocalDateTime) UnmarshalJSON(b []byte) error {
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 (
"platform/web/auth"
"platform/web/core"
"platform/web/globals/orm"
q "platform/web/queries"
"time"
@@ -39,13 +38,13 @@ func ListBill(c *fiber.Ctx) error {
Where(q.Bill.UserID.Eq(authCtx.User.ID))
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 {
do.Where(q.Bill.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Bill.CreatedAt.Gte(*req.CreateAfter))
}
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 != "" {
do.Where(q.Bill.BillNo.Eq(*req.BillNo))

View File

@@ -1,11 +1,11 @@
package handlers
import (
"fmt"
"net/netip"
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
channel2 "platform/web/domains/channel"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
@@ -40,18 +40,18 @@ func ListChannels(c *fiber.Ctx) error {
Where(q.Channel.UserID.Eq(authContext.User.ID))
switch req.AuthType {
case s.ChannelAuthTypeIp:
cond.Where(q.Channel.AuthIP.Is(true))
cond.Where(q.Channel.Whitelists.IsNotNull())
case s.ChannelAuthTypePass:
cond.Where(q.Channel.AuthPass.Is(true))
cond.Where(q.Channel.Username.IsNotNull(), q.Channel.Password.IsNotNull())
default:
break
}
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 {
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 {
ResourceId int32 `json:"resource_id" 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"`
Prov string `json:"prov"`
City string `json:"city"`
Isp string `json:"isp"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *int `json:"isp"`
}
type CreateChannelRespItem struct {
Proto channel2.Protocol `json:"-"`
Host string `json:"host"`
Port int32 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
Proto int `json:"-"`
Host string `json:"host"`
Port uint16 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
}
func CreateChannel(c *fiber.Ctx) error {
@@ -115,48 +115,32 @@ func CreateChannel(c *fiber.Ctx) error {
return err
}
// 检查用户其他权限
user := authCtx.User
if user.IDToken == nil || *user.IDToken == "" {
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)
if err := c.BodyParser(req); err != nil {
return err
}
var isp string
switch req.Isp {
case "1":
isp = "电信"
case "2":
isp = "联通"
case "3":
isp = "移动"
ip, err := netip.ParseAddr(c.Get(core.HeaderUserIP))
if err != nil {
return core.NewBizErr("解析请求头客户端 IP 地址失败", err)
}
// 创建通道
result, err := s.Channel.CreateChannel(
c,
result, err := s.Channel.CreateChannels(
ip,
user.ID,
req.ResourceId,
req.Protocol,
req.AuthType,
req.AuthType == s.ChannelAuthTypeIp,
req.AuthType == s.ChannelAuthTypePass,
req.Count,
s.EdgeFilter{
Isp: isp,
Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
Prov: req.Prov,
City: req.City,
},
@@ -170,8 +154,8 @@ func CreateChannel(c *fiber.Ctx) error {
for i, channel := range result {
resp[i] = &CreateChannelRespItem{
Proto: req.Protocol,
Host: channel.ProxyHost,
Port: channel.ProxyPort,
Host: channel.Proxy.IP.String(),
Port: channel.Port,
}
if req.AuthType == s.ChannelAuthTypePass {
resp[i].Username = channel.Username
@@ -188,12 +172,13 @@ type CreateChannelResultType string
// region RemoveChannels
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 {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
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 {
return err
}

View File

@@ -1,20 +1,8 @@
package handlers
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"
"gorm.io/gen/field"
"gorm.io/gorm"
"github.com/gofiber/fiber/v2"
)
@@ -29,80 +17,82 @@ type RegisterEdgeResp struct {
}
func AssignEdge(c *fiber.Ctx) (err 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,
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.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 {
@@ -120,36 +110,40 @@ type AllEdgesAvailableRespItem struct {
}
func AllEdgesAvailable(c *fiber.Ctx) (err error) {
// 检查权限
_, err = auth.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// 验证请求参数
var req = new(AllEdgesAvailableReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// // 检查权限
// _, err = auth.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// 获取可用的转发服务
infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter)
if err != nil {
return err
}
// // 验证请求参数
// var req = new(AllEdgesAvailableReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// 返回结果
var edges = make([]AllEdgesAvailableRespItem, len(infos))
for i, info := range infos {
edges[i] = AllEdgesAvailableRespItem{
Ip: info.Host,
Port: u.Z(info.ProxyPort),
Isp: edge2.ISP(info.Isp).String(),
Prov: info.Prov,
City: info.City,
Status: info.Status,
}
}
return c.JSON(edges)
// // 获取可用的转发服务
// infos, err := s.Edge.AllEdges(req.Count, req.EdgeFilter)
// if err != nil {
// return err
// }
// // 返回结果
// var edges = make([]AllEdgesAvailableRespItem, len(infos))
// for i, info := range infos {
// edges[i] = AllEdgesAvailableRespItem{
// Ip: info.Host,
// Port: u.Z(info.ProxyPort),
// 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
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"`
IdenNo string `json:"iden_no" validate:"required"`
}
@@ -173,7 +173,7 @@ func IdentifyCallback(c *fiber.Ctx) error {
q.User.Name,
).
Updates(m.User{
IDType: info.Type,
IDType: m.UserIDType(info.Type),
IDNo: &info.IdNo,
IDToken: &info.Token,
Name: &info.Name,
@@ -202,7 +202,7 @@ func idenKey(id string) string {
type idenInfo struct {
Uid int32 `json:"uid"`
Type int32 `json:"type"`
Type int `json:"type"`
Name string `json:"name"`
IdNo string `json:"id_no"`
Token string `json:"token"`

View File

@@ -1,26 +1,9 @@
package handlers
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"
"github.com/gofiber/fiber/v2"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
)
// region 报告上线
@@ -38,103 +21,106 @@ type ProxyReportOnlineResp struct {
}
func ProxyReportOnline(c *fiber.Ctx) (err 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,
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{
// 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
@@ -146,36 +132,40 @@ type ProxyReportOfflineReq struct {
}
func ProxyReportOffline(c *fiber.Ctx) (err error) {
// 检查接口权限
_, err = auth2.GetAuthCtx(c).PermitSecretClient()
if err != nil {
return err
}
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
// 验证请求参数
var req = new(ProxyReportOfflineReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// // 检查接口权限
// _, err = auth2.GetAuthCtx(c).PermitSecretClient()
// if err != nil {
// return err
// }
// 下线转发服务
_, err = q.Proxy.
Where(q.Proxy.ID.Eq(req.Id)).
UpdateSimple(q.Proxy.Status.Value(0))
if err != nil {
return err
}
// // 验证请求参数
// var req = new(ProxyReportOfflineReq)
// err = g.Validator.Validate(c, req)
// if err != nil {
// return err
// }
// 下线所有相关的边缘节点
_, err = q.Edge.
Where(q.Edge.ProxyID.Eq(req.Id)).
UpdateSimple(q.Edge.Status.Value(0))
if err != nil {
return err
}
// // 下线转发服务
// _, err = q.Proxy.
// Where(q.Proxy.ID.Eq(req.Id)).
// UpdateSimple(q.Proxy.Status.Value(0))
// if err != nil {
// 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
@@ -188,157 +178,161 @@ type ProxyReportUpdateReq struct {
}
func ProxyReportUpdate(c *fiber.Ctx) (err error) {
// 检查接口权限
_, 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
return c.JSON(map[string]any{
"error": "接口暂不可用",
})
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

View File

@@ -4,9 +4,8 @@ import (
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
resource2 "platform/web/domains/resource"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
@@ -43,7 +42,7 @@ func ListResourceShort(c *fiber.Ctx) error {
// 查询套餐列表
do := q.Resource.Where(
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 != "" {
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))
}
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 {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
}
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 {
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 {
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).
@@ -124,7 +123,7 @@ func ListResourceLong(c *fiber.Ctx) error {
// 查询套餐列表
do := q.Resource.Where(
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 != "" {
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))
}
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 {
do.Where(q.Resource.CreatedAt.Gte(orm.LocalDateTime(*req.CreateAfter)))
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
}
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 {
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 {
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).
@@ -202,27 +201,27 @@ func AllActiveResource(c *fiber.Ctx) error {
q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.Active.Is(true),
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(
short.Type.Eq(int32(resource2.ModeTime)),
short.Expire.Gte(orm.LocalDateTime(now)),
short.Type.Eq(int(m.ResourceModeTime)),
short.Expire.Gte(now),
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.Type.Eq(int32(resource2.ModeCount)),
short.Type.Eq(int(m.ResourceModeQuota)),
short.Quota.GtCol(short.Used),
),
).Or(
q.Resource.Type.Eq(int32(resource2.TypeLong)),
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
q.ResourceLong.As(q.Resource.Long.Name()).Where(
long.Type.Eq(int32(resource2.ModeTime)),
long.Expire.Gte(orm.LocalDateTime(now)),
long.Type.Eq(int(m.ResourceModeTime)),
long.Expire.Gte(now),
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.Type.Eq(int32(resource2.ModeCount)),
long.Type.Eq(int(m.ResourceModeQuota)),
long.Quota.GtCol(long.Used),
),
),
@@ -282,23 +281,23 @@ func StatisticResourceFree(c *fiber.Ctx) error {
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 {
shortCount++
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 {
longCount++
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 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++
shortDailyFreeSum += int(resource.Short.DailyLimit)
} 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 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++
longDailyFreeSum += int(resource.Long.DailyLimit)
} 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))
}
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 {
do.Where(q.LogsUserUsage.Time.Lte(orm.LocalDateTime(*req.TimeBefore)))
do.Where(q.LogsUserUsage.Time.Lte(*req.TimeBefore))
}
var data = new(StatisticResourceUsageResp)

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
package handlers
import (
"net"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
"time"
@@ -89,7 +90,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
return err
}
err = secureAddr(req.Host)
ip, err := secureAddr(req.Host)
if err != nil {
return err
}
@@ -97,7 +98,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
// 创建白名单
err = q.Whitelist.Create(&m.Whitelist{
UserID: authCtx.User.ID,
Host: req.Host,
IP: u.Z(ip),
Remark: &req.Remark,
})
return nil
@@ -125,17 +126,21 @@ func UpdateWhitelist(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "id is required")
}
ip, err := secureAddr(req.Host)
if err != nil {
return err
}
// 更新白名单
_, err = q.Whitelist.
Where(
q.Whitelist.ID.Eq(req.ID),
q.Whitelist.UserID.Eq(authCtx.User.ID),
).
Updates(&m.Whitelist{
ID: req.ID,
Host: req.Host,
Remark: &req.Remark,
})
UpdateSimple(
q.Whitelist.IP.Value(ip),
q.Whitelist.Remark.Value(req.Remark),
)
if err != nil {
return err
}
@@ -186,13 +191,13 @@ func RemoveWhitelist(c *fiber.Ctx) error {
return nil
}
func secureAddr(str string) error {
var addr = net.ParseIP(str)
if addr == nil {
return fiber.NewError(fiber.StatusBadRequest, "IP 解析失败")
func secureAddr(str string) (*orm.Inet, error) {
ip, err := orm.ParseInet(str)
if err != nil {
return nil, err
}
if env.RunMode == env.RunModeDev || addr.IsGlobalUnicast() {
return nil
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
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()
_admin.ALL = field.NewAsterisk(tableName)
_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.Password = field.NewString(tableName, "password")
_admin.Name = field.NewString(tableName, "name")
_admin.Avatar = field.NewString(tableName, "avatar")
_admin.Phone = field.NewString(tableName, "phone")
_admin.Email = field.NewString(tableName, "email")
_admin.Status = field.NewInt32(tableName, "status")
_admin.LastLogin = field.NewField(tableName, "last_login")
_admin.LastLoginHost = field.NewString(tableName, "last_login_host")
_admin.LastLoginAgent = field.NewString(tableName, "last_login_agent")
_admin.CreatedAt = field.NewField(tableName, "created_at")
_admin.UpdatedAt = field.NewField(tableName, "updated_at")
_admin.DeletedAt = field.NewField(tableName, "deleted_at")
_admin.Status = field.NewInt(tableName, "status")
_admin.LastLogin = field.NewTime(tableName, "last_login")
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
_admin.fillFieldMap()
@@ -50,21 +50,21 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
type admin struct {
adminDo
ALL field.Asterisk
ID field.Int32 // 管理员ID
Username field.String // 用户名
Password field.String // 密码
Name field.String // 真实姓名
Avatar field.String // 头像URL
Phone field.String // 手机号码
Email field.String // 邮箱
Status field.Int32 // 状态0-禁用1-正常
LastLogin field.Field // 最后登录时间
LastLoginHost field.String // 最后登录地址
LastLoginAgent field.String // 最后登录代理
CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Username field.String
Password field.String
Name field.String
Avatar field.String
Phone field.String
Email field.String
Status field.Int
LastLogin field.Time
LastLoginIP field.Field
LastLoginUA field.String
fieldMap map[string]field.Expr
}
@@ -82,19 +82,19 @@ func (a admin) As(alias string) *admin {
func (a *admin) updateTableName(table string) *admin {
a.ALL = field.NewAsterisk(table)
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.Password = field.NewString(table, "password")
a.Name = field.NewString(table, "name")
a.Avatar = field.NewString(table, "avatar")
a.Phone = field.NewString(table, "phone")
a.Email = field.NewString(table, "email")
a.Status = field.NewInt32(table, "status")
a.LastLogin = field.NewField(table, "last_login")
a.LastLoginHost = field.NewString(table, "last_login_host")
a.LastLoginAgent = field.NewString(table, "last_login_agent")
a.CreatedAt = field.NewField(table, "created_at")
a.UpdatedAt = field.NewField(table, "updated_at")
a.DeletedAt = field.NewField(table, "deleted_at")
a.Status = field.NewInt(table, "status")
a.LastLogin = field.NewTime(table, "last_login")
a.LastLoginIP = field.NewField(table, "last_login_ip")
a.LastLoginUA = field.NewString(table, "last_login_ua")
a.fillFieldMap()
@@ -113,6 +113,9 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 14)
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["password"] = a.Password
a.fieldMap["name"] = a.Name
@@ -121,11 +124,8 @@ func (a *admin) fillFieldMap() {
a.fieldMap["email"] = a.Email
a.fieldMap["status"] = a.Status
a.fieldMap["last_login"] = a.LastLogin
a.fieldMap["last_login_host"] = a.LastLoginHost
a.fieldMap["last_login_agent"] = a.LastLoginAgent
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
a.fieldMap["deleted_at"] = a.DeletedAt
a.fieldMap["last_login_ip"] = a.LastLoginIP
a.fieldMap["last_login_ua"] = a.LastLoginUA
}
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()
_adminRole.ALL = field.NewAsterisk(tableName)
_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.Description = field.NewString(tableName, "description")
_adminRole.Active = field.NewBool(tableName, "active")
_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()
@@ -45,14 +45,14 @@ type adminRole struct {
adminRoleDo
ALL field.Asterisk
ID field.Int32 // 管理员角色ID
Name field.String // 角色名称
Description field.String // 角色描述
Active field.Bool // 是否激活
Sort field.Int32 // 排序
CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Name field.String
Description field.String
Active field.Bool
Sort field.Int32
fieldMap map[string]field.Expr
}
@@ -70,13 +70,13 @@ func (a adminRole) As(alias string) *adminRole {
func (a *adminRole) updateTableName(table string) *adminRole {
a.ALL = field.NewAsterisk(table)
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.Description = field.NewString(table, "description")
a.Active = field.NewBool(table, "active")
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()
@@ -95,13 +95,13 @@ func (a *adminRole) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
func (a *adminRole) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 8)
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["description"] = a.Description
a.fieldMap["active"] = a.Active
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 {

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