48 Commits

Author SHA1 Message Date
9b3546b45f 修复逻辑问题 2026-04-16 14:24:59 +08:00
b8c8c7d7b1 新增产品信息用户查询接口 2026-04-14 15:06:08 +08:00
58b8849d8d 完善填充数据 & 修复余额变动查询问题 2026-04-13 16:57:31 +08:00
46d326638b 完善环境变量与初始化数据 2026-04-13 11:00:46 +08:00
cfbe751af7 修复账单与交易接口问题 & 管理员权限的身份限制 2026-04-13 10:21:44 +08:00
624a5ff2c0 余额变动接口 2026-04-11 10:19:43 +08:00
7d7b979b66 重构优惠券表结构与功能 2026-04-09 15:05:13 +08:00
3040b10eed 产品和权限编码格式整理优化 2026-04-08 17:08:08 +08:00
3fb3a8026f 新增指定用户查询接口 & 接口权限细分 2026-04-08 13:38:00 +08:00
fccb83c0e5 最低价格约束 2026-04-07 15:43:18 +08:00
c684523cb8 产品套餐状态管理 2026-04-07 13:24:50 +08:00
62c624c88e 更新构建脚本 2026-04-02 17:48:59 +08:00
4481c581e9 恢复余额功能 & 管理员修改余额功能 2026-04-02 10:39:33 +08:00
22cb2d50d3 整体优化完善接口与数据权限检查 2026-03-28 17:53:37 +08:00
51c377964d 实现管理员的用户增删改功能 2026-03-28 13:49:52 +08:00
7e8d824ba6 优化交易创建流程,客户管理新增折扣与来源字段及功能 2026-03-27 16:16:55 +08:00
75ad12efb3 完善套餐与账单接口 & 完善支付数据保存,记录实付价格并关联优惠券 2026-03-27 13:41:18 +08:00
5ffa151f58 完善定价与套餐关联表结构 2026-03-25 11:43:33 +08:00
c9995ef566 修复接口筛选问题 2026-03-24 14:44:54 +08:00
ad021f2faa 实现产品查询和修改接口 & 修复套餐查询接口问题 2026-03-23 17:50:47 +08:00
9f7160edfc 完善 admin 接口筛选机制 2026-03-23 14:26:10 +08:00
71f1c6f141 数据关联手机号查询 2026-03-20 14:37:41 +08:00
bb895eccdf 权限管理接口实现 2026-03-19 14:56:43 +08:00
9d996acf5f 支付功能动态化扩展 2026-03-18 13:07:06 +08:00
99853b8514 新增开发接口,可以清除实名信息 2026-03-17 14:19:08 +08:00
efce18e6f5 套餐查询提供状态筛选字段;实名认证回调结果提供页面 2026-03-13 17:03:32 +08:00
3dc9bc5b1d 补充微信支付官方手机端接口支持 2026-03-11 13:08:15 +08:00
7fe415de63 新增本地构建脚本 2026-02-27 16:44:31 +08:00
8e42fad8aa 修复修改密码时未验证手机号所属账号的问题 2026-02-27 16:28:28 +08:00
7a3c47f1d4 修复导致首次登录时注册失败的问题 2026-02-26 14:47:36 +08:00
dfbb3a9acc 管理员用户查询接口提供管理员详细信息 2026-01-09 18:52:48 +08:00
19fa8b381c 扩展令牌内省函数以支持多账号池 2026-01-09 17:22:02 +08:00
b7a9682552 实现管理员认领功能 2026-01-06 15:33:55 +08:00
f638baec64 实现基本管理员数据访问接口 2025-12-30 19:24:03 +08:00
1262a8dae4 重构认证授权模块,统一到 auth 包下 2025-12-29 10:18:01 +08:00
bf8f001a30 新增代理注册接口
Some checks failed
Docker / build (push) Has been cancelled
2025-12-22 17:31:31 +08:00
eac793becb 优化 sql 脚本,排除无关填充流程
Some checks failed
Docker / build (push) Has been cancelled
2025-12-20 18:47:21 +08:00
7bdbb7ddff 优化 Dockerfile
Some checks failed
Docker / build (push) Has been cancelled
2025-12-20 15:15:54 +08:00
c8fd4cf9ca 添加 otel 相关环境变量,优化部署配置
Some checks failed
Docker / build (push) Has been cancelled
2025-12-19 17:26:59 +08:00
2b190bd4e5 实现批次检查接口 & 修复白银接口 url 二次编码问题 2025-12-19 10:59:04 +08:00
8f2e71849f 实现用户咨询数据收集接口 2025-12-18 14:22:56 +08:00
0207720943 优化 model json 格式化注释 2025-12-15 14:48:30 +08:00
05fba68b3e 重构优化套餐数据结构,修复提取计数问题 2025-12-10 20:09:20 +08:00
c8c86081d9 修复删除空批次时的数组越界问题 2025-12-09 19:28:49 +08:00
983dbb4564 重构白银节点分配方式,使用手动接口精确配置节点 2025-12-08 17:59:40 +08:00
9e237be21e 修复通道注册失败问题 2025-12-05 18:57:52 +08:00
5649a03c47 优化通道关闭流程,只靠 batch id 索引通道 2025-12-05 18:31:30 +08:00
4a2dcabf58 优化全局 id 生成效率 2025-12-05 17:30:34 +08:00
136 changed files with 13733 additions and 2624 deletions

57
.env.example Normal file
View File

@@ -0,0 +1,57 @@
# 应用配置
RUN_MODE=development
DEBUG_HTTP_DUMP=false
# 数据库配置
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=app
DB_USERNAME=dev
DB_PASSWORD=dev
# redis 配置
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
# otel 配置
OTEL_HOST=127.0.0.1
OTEL_PORT=4317
# 白银节点
BAIYIN_CLOUD_URL=
BAIYIN_TOKEN_URL=
# 京东实名
IDEN_ACCESS_KEY=
IDEN_SECRET_KEY=
IDEN_CALLBACK_URL=
# 支付宝(暂时弃用,但是需要配置)
ALIPAY_APP_ID=
ALIPAY_APP_PRIVATE_KEY=
ALIPAY_PUBLIC_KEY=
ALIPAY_API_CERT=
# 微信支付(暂时弃用,但是需要配置)
WECHATPAY_APP_ID=
WECHATPAY_MCH_ID=
WECHATPAY_MCH_PRIVATE_KEY_SERIAL=
WECHATPAY_MCH_PRIVATE_KEY=
WECHATPAY_PUBLIC_KEY_ID=
WECHATPAY_PUBLIC_KEY=
WECHATPAY_API_CERT=
WECHATPAY_CALLBACK_URL=
# 阿里云
ALIYUN_ACCESS_KEY=
ALIYUN_ACCESS_KEY_SECRET=
ALIYUN_SMS_SIGNATURE=
ALIYUN_SMS_TEMPLATE_LOGIN=
# 商福通
SFTPAY_ENABLE=
SFTPAY_APP_ID=
SFTPAY_ROUTE_ID=
SFTPAY_APP_PRIVATE_KEY=
SFTPAY_PUBLIC_KEY=
SFTPAY_RETURN_URL=

View File

@@ -2,7 +2,7 @@ name: Docker
on: on:
push: push:
branches: [ "main" ] branches: ["v*"]
env: env:
REGISTRY: ghcr.io REGISTRY: ghcr.io
@@ -10,14 +10,12 @@ env:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
packages: write packages: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4

16
.vscode/launch.json vendored
View File

@@ -1,16 +0,0 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "main",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/main",
"cwd": "${workspaceFolder}"
}
]
}

13
.zed/debug.json Normal file
View File

@@ -0,0 +1,13 @@
// Project-local debug tasks
//
// For more documentation on how to configure debug tasks,
// see: https://zed.dev/docs/debugger
[
{
"label": "debug main",
"adapter": "Delve",
"request": "launch",
"mode": "debug",
"program": "./cmd/main"
}
]

View File

@@ -1,35 +1,31 @@
# 第一阶段:构建 # 第一阶段:构建
FROM golang:1.24.0 AS builder FROM golang:1.25.3 AS builder
ENV GOPROXY=https://goproxy.cn,direct
WORKDIR /build WORKDIR /build
# 复制Go模块文件 ENV GOPROXY=https://goproxy.cn,direct
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
# 复制源代码
COPY . . COPY . .
RUN go build -ldflags '-w -s' -o bin/platform_linux_amd64 cmd/main/main.go
# 编译
RUN GOOS=linux GOARCH=amd64 go build -ldflags '-w -s' -o bin/platform_linux_amd64 cmd/main/main.go
# 第二阶段:运行环境 # 第二阶段:运行环境
FROM ubuntu:24.04 AS runner FROM alpine:3.23 AS runner
WORKDIR /app WORKDIR /app
RUN apt-get update && apt-get install -y ca-certificates ENV TZ=Asia/Shanghai
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata
# 从构建阶段复制编译好的二进制文件
COPY --from=builder /build/bin/platform_linux_amd64 /app/platform COPY --from=builder /build/bin/platform_linux_amd64 /app/platform
# 设置可执行权限
RUN chmod +x /app/platform RUN chmod +x /app/platform
# 声明暴露端口
EXPOSE 8080 EXPOSE 8080
# 启动平台服务
CMD ["/app/platform"] CMD ["/app/platform"]

View File

@@ -1,49 +1,44 @@
## TODO ## TODO
trade/create 性能问题,缩短事务时间,考虑其他方式实现可靠分布式事务 交易信息持久化
需要确认以下 ID.GenSerial 的分布式并发安全性 用户请求需要检查数据权限
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具 用反射实现环境变量解析,以简化函数签名
端口资源池的 gc 实现 ---
标准化生产环境 cors 配置
底层调用集成 otel
- redis
- gorm
- 三方接口
分离 task 的客户端支持多进程prefork 必要!) 分离 task 的客户端支持多进程prefork 必要!)
调整目录结构: jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
``` 慢速请求底层调用埋点监控
- /core 核心概念
- /util 工具函数
- /models 模型 数据库转模型文件
- /queries 数据库层
- /clients 三方依赖的客户端实例
- /services 服务层
- /auth 认证相关,特化服务
- /app 应用相关,初始化日志,环境变量等
- /http 协议层http 服务
- /cmd 主函数
逐层向上依赖
cmd 调用 app, http 的初始化函数
http 调用 clients 的初始化函数
```
考虑一个方案限制接口请求速率,无侵入更好
冷数据迁移方案 冷数据迁移方案
proxy 网关更新接口可以传输更结构化的数据,直接区分不同类型以加快更新速度 ## 开发环境
### 更新表结构的流程
1. 编辑 `scripts/sql/init.sql` 文件,参照原有格式,需要注意:
- 先写 drop table if exists 语句,确保脚本可以幂等执行
- 编写 create table 语句,按需添加审计字段和软删除字段 (created_at, updated_at, deleted_at)
- 为有必要的字段添加索引
- 为数据表及其字段添加注释
- 在文件末尾创建数据表流程全部结束后,为字段添加外键
2. 建议用数据库工具检查差异并增量更新,或者手动增量更新
3. 创建 model 文件,并将其添加到 gen 代码中
4. 生成查询文件
### 权限管理
`web/core/scopes.go` 下定义了系统所有静态权限
新增系统权限需要在数据库中配套添加权限
前端也需要新增配套权限定义
## 业务逻辑 ## 业务逻辑
@@ -63,22 +58,20 @@ proxy 网关更新接口可以传输更结构化的数据,直接区分不同
### 节点分配与存储逻辑 ### 节点分配与存储逻辑
添加 提取
- 检查用户 ip 是否在白名单内
- 取用端口,不够则返回失败
- 将分配结果转写成配置发送到网关
- 保存通道信息和分配记录,其中通道信息以网关为主体,分配记录以用户为主体
- 添加异步任务,当时间结束后释放取用的端口并清空网关配置
删除: - 检查用户套餐与白名单
- 如果传入用户信息,检查要删除的连接是否属于该用户 - 选中代理
- 释放可用端口 - 找到当前可用端口最多的代理
- redis 脚本中检查,如果端口所属节点已下线则直接忽略 - 不考虑分割端口,不够加机器
- 提交清空配置到网关 - 获取可用端口
- 获取可用节点
- 生成批次号,提交到期释放任务
- 绑定节点与端口,保存到数据库
- 分别提交连接与配置请求
缩扩容 释放
- 通过调度任务实现缩扩容
- 每分钟检查一次全部配置,按代理分组 - 根据批次查出所有端口与相关节点
- 获取所有代理后备配置 - 分别提交断开与关闭请求
- 后备配置/当前配置 - 释放端口
- 当比例 < 1.5 或 > 3 时,重新更新为 2 倍

View File

@@ -51,6 +51,8 @@ func main() {
m.LogsUserUsage{}, m.LogsUserUsage{},
m.Permission{}, m.Permission{},
m.Product{}, m.Product{},
m.ProductSku{},
m.ProductSkuUser{},
m.Proxy{}, m.Proxy{},
m.Refund{}, m.Refund{},
m.Resource{}, m.Resource{},
@@ -61,6 +63,10 @@ func main() {
m.User{}, m.User{},
m.UserRole{}, m.UserRole{},
m.Whitelist{}, m.Whitelist{},
m.Inquiry{},
m.ProductDiscount{},
m.BalanceActivity{},
m.CouponUser{},
) )
g.Execute() g.Execute()
} }

View File

@@ -1,8 +1,8 @@
name: lanhu name: lanhu-platform
services: services:
postgres: postgres:
image: postgres:17 image: postgres:17.7
environment: environment:
POSTGRES_USER: ${DB_USERNAME} POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_PASSWORD: ${DB_PASSWORD}

View File

@@ -1,90 +0,0 @@
# Docker 部署说明
本文档说明如何使用 Docker 构建和部署平台服务。
## 构建镜像
在项目根目录下执行以下命令构建 Docker 镜像:
```bash
docker build -t platform:latest .
```
## 生产环境部署
由于涉及敏感的 `.pem` 证书文件,这些文件不包含在代码库或 Docker 镜像中,而是在运行时通过卷挂载的方式提供。
### 准备证书文件
1. 在生产服务器上创建一个目录用于存放证书文件,例如:`/path/to/certs/`
2. 将必要的证书文件放入此目录:
- `pub_key.pem`
- `apiclient_key.pem`
### 运行容器
使用以下命令运行容器,挂载证书目录:
```bash
docker run -d \
--name platform \
-p 8080:8080 \
-e APP_PORT=8080 \
-v /path/to/certs:/app/certs \
platform:latest
```
### 环境变量
可以通过环境变量来配置应用程序:
- `APP_PORT`: 应用监听的端口号(默认: 8080
- 其他应用相关的环境变量可以通过 `-e` 参数添加
### 证书路径配置
如果应用程序需要知道证书的路径,可以通过环境变量配置:
```bash
docker run -d \
--name platform \
-p 8080:8080 \
-e APP_PORT=8080 \
-e PUB_KEY_PATH=/app/certs/pub_key.pem \
-e APICLIENT_KEY_PATH=/app/certs/apiclient_key.pem \
-v /path/to/certs:/app/certs \
platform:latest
```
## 使用 Docker Compose
也可以通过 Docker Compose 进行部署,创建 `docker-compose.yml` 文件:
```yaml
version: '3'
services:
platform:
image: platform:latest
ports:
- "8080:8080"
environment:
- APP_PORT=8080
- PUB_KEY_PATH=/app/certs/pub_key.pem
- APICLIENT_KEY_PATH=/app/certs/apiclient_key.pem
volumes:
- /path/to/certs:/app/certs
restart: unless-stopped
```
然后执行:
```bash
docker-compose up -d
```
## 安全建议
1. 确保证书文件的权限设置正确,仅允许必要的用户访问
2. 在生产环境中考虑使用 Docker Secrets 或 Kubernetes Secrets 来管理敏感信息
3. 定期更新证书和密钥

3
go.mod
View File

@@ -11,6 +11,7 @@ require (
github.com/go-redsync/redsync/v4 v4.14.1 github.com/go-redsync/redsync/v4 v4.14.1
github.com/gofiber/contrib/otelfiber/v2 v2.2.3 github.com/gofiber/contrib/otelfiber/v2 v2.2.3
github.com/gofiber/fiber/v2 v2.52.10 github.com/gofiber/fiber/v2 v2.52.10
github.com/gofiber/template/html/v2 v2.1.3
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.1 github.com/hibiken/asynq v0.25.1
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
@@ -54,6 +55,8 @@ require (
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect

6
go.sum
View File

@@ -115,6 +115,12 @@ github.com/gofiber/contrib/otelfiber/v2 v2.2.3 h1:WKW1XezHFAoohGZwnvC0R8TFJcNkab
github.com/gofiber/contrib/otelfiber/v2 v2.2.3/go.mod h1:WdQ1tYbL83IYC6oBaWvKBMVGSAYvSTRuUWTcr0wK1T4= github.com/gofiber/contrib/otelfiber/v2 v2.2.3/go.mod h1:WdQ1tYbL83IYC6oBaWvKBMVGSAYvSTRuUWTcr0wK1T4=
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY= github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o=
github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=

14
pkg/env/env.go vendored
View File

@@ -20,11 +20,10 @@ const (
var ( var (
RunMode = RunModeProd RunMode = RunModeProd
LogLevel = slog.LevelDebug LogLevel = slog.LevelDebug
TradeExpire = 15 * 60 // 交易过期时间,单位秒。默认 15 分钟 TradeExpire = 15 * 60 // 交易过期时间,单位秒。默认 900 秒(15 分钟
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时 SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天 SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
DebugHttpDump = false // 是否打印请求和响应的原始数据 DebugHttpDump = false // 是否打印请求和响应的原始数据
DebugExternalChange = true // 是否实际执行外部非幂等接口调用,在开发调试时可以关闭,避免对外部数据产生影响
DbHost = "localhost" DbHost = "localhost"
DbPort = "5432" DbPort = "5432"
@@ -36,7 +35,10 @@ var (
RedisPort = "6379" RedisPort = "6379"
RedisPassword = "" RedisPassword = ""
BaiyinAddr = "http://103.139.212.110:9989" OtelHost string
OtelPort string
BaiyinCloudUrl string
BaiyinTokenUrl string BaiyinTokenUrl string
IdenCallbackUrl string IdenCallbackUrl string
@@ -103,7 +105,6 @@ func Init() {
errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil)) errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil))
errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil)) errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil))
errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil)) errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil))
errs = append(errs, parse(&DebugExternalChange, "DEBUG_EXTERNAL_CHANGE", true, nil))
errs = append(errs, parse(&DbHost, "DB_HOST", true, nil)) errs = append(errs, parse(&DbHost, "DB_HOST", true, nil))
errs = append(errs, parse(&DbPort, "DB_PORT", true, nil)) errs = append(errs, parse(&DbPort, "DB_PORT", true, nil))
@@ -115,7 +116,10 @@ func Init() {
errs = append(errs, parse(&RedisPort, "REDIS_PORT", true, nil)) errs = append(errs, parse(&RedisPort, "REDIS_PORT", true, nil))
errs = append(errs, parse(&RedisPassword, "REDIS_PASS", true, nil)) errs = append(errs, parse(&RedisPassword, "REDIS_PASS", true, nil))
errs = append(errs, parse(&BaiyinAddr, "BAIYIN_ADDR", true, nil)) errs = append(errs, parse(&OtelHost, "OTEL_HOST", true, nil))
errs = append(errs, parse(&OtelPort, "OTEL_PORT", true, nil))
errs = append(errs, parse(&BaiyinCloudUrl, "BAIYIN_CLOUD_URL", false, nil))
errs = append(errs, parse(&BaiyinTokenUrl, "BAIYIN_TOKEN_URL", false, nil)) errs = append(errs, parse(&BaiyinTokenUrl, "BAIYIN_TOKEN_URL", false, nil))
errs = append(errs, parse(&IdenCallbackUrl, "IDEN_CALLBACK_URL", false, nil)) errs = append(errs, parse(&IdenCallbackUrl, "IDEN_CALLBACK_URL", false, nil))

View File

@@ -17,11 +17,12 @@ func Else[T any](v *T, or T) T {
} }
} }
func ElseTo[A any, B any](a *A, f func(A) B) *B { // 三元表达式
if a == nil { func Ternary[T any](condition bool, trueValue T, falseValue T) T {
return nil if condition {
return trueValue
} else { } else {
return P(f(*a)) return falseValue
} }
} }
@@ -52,6 +53,18 @@ func X[T comparable](v T) *T {
return &v return &v
} }
// N 零值视为 nil
func N[T comparable](v *T) *T {
if v == nil {
return nil
}
var zero T
if *v == zero {
return nil
}
return v
}
// ==================== // ====================
// 数组 // 数组
// ==================== // ====================
@@ -73,7 +86,7 @@ func DateHead(date time.Time) time.Time {
return time.Date(y, m, d, 0, 0, 0, 0, date.Location()) return time.Date(y, m, d, 0, 0, 0, 0, date.Location())
} }
func DateFoot(date time.Time) time.Time { func DateTail(date time.Time) time.Time {
var y, m, d = date.Date() var y, m, d = date.Date()
return time.Date(y, m, d, 23, 59, 59, 999999999, date.Location()) return time.Date(y, m, d, 23, 59, 59, 999999999, date.Location())
} }
@@ -109,3 +122,21 @@ func CombineErrors(errs []error) error {
} }
return combinedErr return combinedErr
} }
// ====================
// 业务
// ====================
func MaskPhone(phone string) string {
if len(phone) < 11 {
return phone
}
return phone[:3] + "****" + phone[7:]
}
func MaskIdNo(idNo string) string {
if len(idNo) < 18 {
return idNo
}
return idNo[:3] + "*********" + idNo[14:]
}

16
publish.ps1 Normal file
View File

@@ -0,0 +1,16 @@
if (-not $args) {
Write-Error "需要指定版本号"
exit 1
}
$confrim = Read-Host "构建版本为 [platform:$($args[0])],是否继续?(y/n)"
if ($confrim -ne "y") {
Write-Host "已取消构建"
exit 0
}
docker build -t repo.lanhuip.com:8554/lanhu/platform:latest .
docker build -t repo.lanhuip.com:8554/lanhu/platform:$($args[0]) .
docker push repo.lanhuip.com:8554/lanhu/platform:latest
docker push repo.lanhuip.com:8554/lanhu/platform:$($args[0])

269
scripts/sql/fill.sql Normal file
View File

@@ -0,0 +1,269 @@
-- ====================
-- region 客户端
-- ====================
insert into client (type, spec, name, client_id, client_secret, redirect_uri) values (1, 3, 'web', 'web', '$2a$10$Ss12mXQgpYyo1CKIZ3URouDm.Lc2KcYJzsvEK2PTIXlv6fHQht45a', '');
insert into client (type, spec, name, client_id, client_secret, redirect_uri) values (1, 3, 'admin', 'admin', '$2a$10$dlfvX5Uf3iVsUWgwlb0Wt.oYsw/OEXgS.Aior3yoT63Ju7ZSsJr/2', '');
-- ====================
-- region 管理员
-- ====================
insert into admin (username, password, name, lock) values ('admin', '', '超级管理员', true);
-- ====================
-- region 产品
-- ====================
delete from product where true;
insert into product (code, name, description, sort) values ('short', '短效动态', '短效动态', 1);
insert into product (code, name, description, sort) values ('long', '长效动态', '长效动态', 2);
insert into product (code, name, description, sort) values ('static', '长效静态', '长效静态', 3);
-- ====================
-- region 套餐
-- ====================
delete from product_sku where true;
insert into product_sku (product_id, code, name, price, price_min, sort) values
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=3&expire=0', '短效动态包量 3 分钟', 10.00, 10.00, 1),
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=5&expire=0', '短效动态包量 5 分钟', 10.00, 10.00, 2),
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=10&expire=0', '短效动态包量 10 分钟', 10.00, 10.00, 3),
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=15&expire=0', '短效动态包量 15 分钟', 10.00, 10.00, 4),
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=30&expire=0', '短效动态包量 30 分钟', 10.00, 10.00, 5),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=7', '短效动态包时 3 分钟 7 天', 10.00, 10.00, 6),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=7', '短效动态包时 5 分钟 7 天', 10.00, 10.00, 7),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=7', '短效动态包时 10 分钟 7 天', 10.00, 10.00, 8),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=7', '短效动态包时 15 分钟 7 天', 10.00, 10.00, 9),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=7', '短效动态包时 30 分钟 7 天', 10.00, 10.00, 10),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=15', '短效动态包时 3 分钟 15 天', 10.00, 10.00, 11),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=15', '短效动态包时 5 分钟 15 天', 10.00, 10.00, 12),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=15', '短效动态包时 10 分钟 15 天', 10.00, 10.00, 13),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=15', '短效动态包时 15 分钟 15 天', 10.00, 10.00, 14),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=15', '短效动态包时 30 分钟 15 天', 10.00, 10.00, 15),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=30', '短效动态包时 3 分钟 30 天', 10.00, 10.00, 16),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=30', '短效动态包时 5 分钟 30 天', 10.00, 10.00, 17),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=30', '短效动态包时 10 分钟 30 天', 10.00, 10.00, 18),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=30', '短效动态包时 15 分钟 30 天', 10.00, 10.00, 19),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=30', '短效动态包时 30 分钟 30 天', 10.00, 10.00, 20),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=90', '短效动态包时 3 分钟 90 天', 10.00, 10.00, 21),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=90', '短效动态包时 5 分钟 90 天', 10.00, 10.00, 22),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=90', '短效动态包时 10 分钟 90 天', 10.00, 10.00, 23),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=90', '短效动态包时 15 分钟 90 天', 10.00, 10.00, 24),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=90', '短效动态包时 30 分钟 90 天', 10.00, 10.00, 25),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=180', '短效动态包时 3 分钟 180 天', 10.00, 10.00, 26),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=180', '短效动态包时 5 分钟 180 天', 10.00, 10.00, 27),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=180', '短效动态包时 10 分钟 180 天', 10.00, 10.00, 28),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=180', '短效动态包时 15 分钟 180 天', 10.00, 10.00, 29),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=180', '短效动态包时 30 分钟 180 天', 10.00, 10.00, 30),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=365', '短效动态包时 3 分钟 365 天', 10.00, 10.00, 31),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=365', '短效动态包时 5 分钟 365 天', 10.00, 10.00, 32),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=365', '短效动态包时 10 分钟 365 天', 10.00, 10.00, 33),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=365', '短效动态包时 15 分钟 365 天', 10.00, 10.00, 34),
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=365', '短效动态包时 30 分钟 365 天', 10.00, 10.00, 35)
;
insert into product_sku (product_id, code, name, price, price_min, sort) values
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=60&expire=0', '长效动态包量 1 小时', 10.00, 10.00, 1),
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=240&expire=0', '长效动态包量 4 小时', 10.00, 10.00, 2),
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=480&expire=0', '长效动态包量 8 小时', 10.00, 10.00, 3),
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=720&expire=0', '长效动态包量 12 小时', 10.00, 10.00, 4),
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=1440&expire=0', '长效动态包量 24 小时', 10.00, 10.00, 5),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=7', '长效动态包时 1 小时 7 天', 10.00, 10.00, 6),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=7', '长效动态包时 4 小时 7 天', 10.00, 10.00, 7),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=7', '长效动态包时 8 小时 7 天', 10.00, 10.00, 8),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=7', '长效动态包时 12 小时 7 天', 10.00, 10.00, 9),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=7', '长效动态包时 24 小时 7 天', 10.00, 10.00, 10),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=15', '长效动态包时 1 小时 15 天', 10.00, 10.00, 11),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=15', '长效动态包时 4 小时 15 天', 10.00, 10.00, 12),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=15', '长效动态包时 8 小时 15 天', 10.00, 10.00, 13),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=15', '长效动态包时 12 小时 15 天', 10.00, 10.00, 14),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=15', '长效动态包时 24 小时 15 天', 10.00, 10.00, 15),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=30', '长效动态包时 1 小时 30 天', 10.00, 10.00, 16),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=30', '长效动态包时 4 小时 30 天', 10.00, 10.00, 17),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=30', '长效动态包时 8 小时 30 天', 10.00, 10.00, 18),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=30', '长效动态包时 12 小时 30 天', 10.00, 10.00, 19),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=30', '长效动态包时 24 小时 30 天', 10.00, 10.00, 20),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=90', '长效动态包时 1 小时 90 天', 10.00, 10.00, 21),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=90', '长效动态包时 4 小时 90 天', 10.00, 10.00, 22),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=90', '长效动态包时 8 小时 90 天', 10.00, 10.00, 23),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=90', '长效动态包时 12 小时 90 天', 10.00, 10.00, 24),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=90', '长效动态包时 24 小时 90 天', 10.00, 10.00, 25),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=180', '长效动态包时 1 小时 180 天', 10.00, 10.00, 26),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=180', '长效动态包时 4 小时 180 天', 10.00, 10.00, 27),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=180', '长效动态包时 8 小时 180 天', 10.00, 10.00, 28),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=180', '长效动态包时 12 小时 180 天', 10.00, 10.00, 29),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=180','长效动态包时 24 小时 180 天', 10.00, 10.00, 30),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=365', '长效动态包时 1 小时 365 天', 10.00, 10.00, 31),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=365', '长效动态包时 4 小时 365 天', 10.00, 10.00, 32),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=365', '长效动态包时 8 小时 365 天', 10.00, 10.00, 33),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=365', '长效动态包时 12 小时 365 天', 10.00, 10.00, 34),
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=365','长效动态包时 24 小时 365 天', 10.00, 10.00, 35)
;
-- ====================
-- region 权限
-- ====================
delete from permission where true;
-- --------------------------
-- level 1
-- --------------------------
insert into permission (name, description, sort) values
('permission', '权限', 1),
('admin_role', '管理员角色', 2),
('admin', '管理员', 3),
('product', '产品', 4),
('product_sku', '产品套餐', 5),
('discount', '折扣', 6),
('resource', '用户套餐', 7),
('user', '用户', 8),
('coupon', '优惠券', 9),
('batch', '批次', 10),
('channel', 'IP', 11),
('trade', '交易', 12),
('bill', '账单', 13),
('balance_activity', '余额变动', 14);
-- --------------------------
-- level 2
-- --------------------------
-- permission 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'permission' and deleted_at is null), 'permission:read', '读取权限列表', 1),
((select id from permission where name = 'permission' and deleted_at is null), 'permission:write', '写入权限', 2);
-- admin_role 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:read', '读取管理员角色列表', 1),
((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:write', '写入管理员角色', 2);
-- admin 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'admin' and deleted_at is null), 'admin:read', '读取管理员列表', 1),
((select id from permission where name = 'admin' and deleted_at is null), 'admin:write', '写入管理员', 2);
-- product 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'product' and deleted_at is null), 'product:read', '读取产品列表', 1),
((select id from permission where name = 'product' and deleted_at is null), 'product:write', '写入产品', 2);
-- product_sku 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:read', '读取产品套餐列表', 1),
((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:write', '写入产品套餐', 2);
-- discount 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'discount' and deleted_at is null), 'discount:read', '读取折扣列表', 1),
((select id from permission where name = 'discount' and deleted_at is null), 'discount:write', '写入折扣', 2);
-- resource 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource' and deleted_at is null), 'resource:read', '读取用户套餐列表', 1),
((select id from permission where name = 'resource' and deleted_at is null), 'resource:write', '写入用户套餐', 2),
((select id from permission where name = 'resource' and deleted_at is null), 'resource:short', '短效动态套餐', 3),
((select id from permission where name = 'resource' and deleted_at is null), 'resource:long', '长效动态套餐', 4);
-- user 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'user' and deleted_at is null), 'user:read', '读取用户列表', 1),
((select id from permission where name = 'user' and deleted_at is null), 'user:write', '写入用户', 2);
-- coupon 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:read', '读取优惠券列表', 1),
((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:write', '写入优惠券', 2);
-- batch 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'batch' and deleted_at is null), 'batch:read', '读取批次列表', 1),
((select id from permission where name = 'batch' and deleted_at is null), 'batch:write', '写入批次', 2);
-- channel 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'channel' and deleted_at is null), 'channel:read', '读取 IP 列表', 1),
((select id from permission where name = 'channel' and deleted_at is null), 'channel:write', '写入 IP', 2);
-- trade 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'trade' and deleted_at is null), 'trade:read', '读取交易列表', 1),
((select id from permission where name = 'trade' and deleted_at is null), 'trade:write', '写入交易', 2);
-- bill 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'bill' and deleted_at is null), 'bill:read', '读取账单列表', 1),
((select id from permission where name = 'bill' and deleted_at is null), 'bill:write', '写入账单', 2);
-- balance_activity 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'balance_activity' and deleted_at is null), 'balance_activity:read', '读取余额变动列表', 1);
-- --------------------------
-- level 3
-- --------------------------
-- product_sku:write 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'product_sku:write' and deleted_at is null), 'product_sku:write:status', '更改产品套餐状态', 1);
-- resource:short 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource:short' and deleted_at is null), 'resource:short:read', '读取用户短效动态套餐列表', 1);
-- resource:long 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource:long' and deleted_at is null), 'resource:long:read', '读取用户长效动态套餐列表', 1);
-- user:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'user:read' and deleted_at is null), 'user:read:one', '读取单个用户', 1),
((select id from permission where name = 'user:read' and deleted_at is null), 'user:read:not_bind', '读取未绑定管理员的用户列表', 2);
-- user:write 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:balance', '写入用户余额', 1),
((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:bind', '用户认领', 2);
-- batch:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'batch:read' and deleted_at is null), 'batch:read:of_user', '读取指定用户的批次列表', 1);
-- channel:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'channel:read' and deleted_at is null), 'channel:read:of_user', '读取指定用户的 IP 列表', 1);
-- trade:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'trade:read' and deleted_at is null), 'trade:read:of_user', '读取指定用户的交易列表', 1);
-- bill:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'bill:read' and deleted_at is null), 'bill:read:of_user', '读取指定用户的账单列表', 1);
-- balance_activity:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'balance_activity:read' and deleted_at is null), 'balance_activity:read:of_user', '读取指定用户的余额变动列表', 1);
-- --------------------------
-- level 4
-- --------------------------
-- user:write:balance 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'user:write:balance' and deleted_at is null), 'user:write:balance:inc', '增加用户余额', 1),
((select id from permission where name = 'user:write:balance' and deleted_at is null), 'user:write:balance:dec', '减少用户余额', 2);
-- resource:short:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource:short:read' and deleted_at is null), 'resource:short:read:of_user', '读取指定用户的短效动态套餐列表', 1);
-- resource:long:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource:long:read' and deleted_at is null), 'resource:long:read:of_user', '读取指定用户的长效动态套餐列表', 1);
-- endregion

View File

@@ -109,74 +109,9 @@ comment on column logs_user_bandwidth.time is '记录时间';
-- endregion -- endregion
-- ==================== -- ====================
-- region 管理员信息 -- region 系统信息
-- ==================== -- ====================
-- admin
drop table if exists admin cascade;
create table admin (
id int generated by default as identity primary key,
username text not null,
password text not null,
name text,
avatar text,
phone text,
email text,
status int not null default 1,
last_login timestamptz,
last_login_ip inet,
last_login_ua text,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_admin_username on admin (username);
create index idx_admin_status on admin (status) where deleted_at is null;
create index idx_admin_created_at on admin (created_at) where deleted_at is null;
-- admin表字段注释
comment on table admin is '管理员表';
comment on column admin.id is '管理员ID';
comment on column admin.username is '用户名';
comment on column admin.password is '密码';
comment on column admin.name is '真实姓名';
comment on column admin.avatar is '头像URL';
comment on column admin.phone is '手机号码';
comment on column admin.email is '邮箱';
comment on column admin.status is '状态0-禁用1-正常';
comment on column admin.last_login is '最后登录时间';
comment on column admin.last_login_ip is '最后登录地址';
comment on column admin.last_login_ua is '最后登录代理';
comment on column admin.created_at is '创建时间';
comment on column admin.updated_at is '更新时间';
comment on column admin.deleted_at is '删除时间';
-- admin_role
drop table if exists admin_role cascade;
create table admin_role (
id int generated by default as identity primary key,
name text not null,
description text,
active bool default true,
sort int default 0,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_admin_role_name on admin_role (name) where deleted_at is null;
create index idx_admin_role_created_at on admin_role (created_at) where deleted_at is null;
-- admin_role表字段注释
comment on table admin_role is '管理员角色关联表';
comment on column admin_role.id is '管理员角色ID';
comment on column admin_role.name is '角色名称';
comment on column admin_role.description is '角色描述';
comment on column admin_role.active is '是否激活';
comment on column admin_role.sort is '排序';
comment on column admin_role.created_at is '创建时间';
comment on column admin_role.updated_at is '更新时间';
comment on column admin_role.deleted_at is '删除时间';
-- announcement -- announcement
drop table if exists announcement cascade; drop table if exists announcement cascade;
create table announcement ( create table announcement (
@@ -208,6 +143,112 @@ comment on column announcement.created_at is '创建时间';
comment on column announcement.updated_at is '更新时间'; comment on column announcement.updated_at is '更新时间';
comment on column announcement.deleted_at is '删除时间'; comment on column announcement.deleted_at is '删除时间';
-- inquiry
drop table if exists inquiry cascade;
create table inquiry (
id int generated by default as identity primary key,
company text,
name text,
phone text,
email text,
content text,
status int not null default 0,
remark text,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create index idx_inquiry_phone on inquiry (phone) where deleted_at is null;
create index idx_inquiry_status on inquiry (status) where deleted_at is null;
create index idx_inquiry_created_at on inquiry (created_at) where deleted_at is null;
-- inquiry表字段注释
comment on table inquiry is '用户咨询表';
comment on column inquiry.id is '咨询ID';
comment on column inquiry.name is '联系人姓名';
comment on column inquiry.phone is '联系电话';
comment on column inquiry.email is '联系邮箱';
comment on column inquiry.company is '公司名称';
comment on column inquiry.content is '咨询内容';
comment on column inquiry.status is '处理状态0-待处理1-已处理';
comment on column inquiry.remark is '备注';
comment on column inquiry.created_at is '创建时间';
comment on column inquiry.updated_at is '更新时间';
comment on column inquiry.deleted_at is '删除时间';
-- endregion
-- ====================
-- region 管理员信息
-- ====================
-- admin
drop table if exists admin cascade;
create table admin (
id int generated by default as identity primary key,
username text not null,
password text not null,
name text,
avatar text,
phone text,
email text,
status int not null default 1,
last_login timestamptz,
last_login_ip inet,
last_login_ua text,
lock bool not null default false,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_admin_username on admin (username) where deleted_at is null;
create index idx_admin_status on admin (status) where deleted_at is null;
create index idx_admin_created_at on admin (created_at) where deleted_at is null;
-- admin表字段注释
comment on table admin is '管理员表';
comment on column admin.id is '管理员ID';
comment on column admin.username is '用户名';
comment on column admin.password is '密码';
comment on column admin.name is '真实姓名';
comment on column admin.avatar is '头像URL';
comment on column admin.phone is '手机号码';
comment on column admin.email is '邮箱';
comment on column admin.status is '状态0-禁用1-正常';
comment on column admin.last_login is '最后登录时间';
comment on column admin.last_login_ip is '最后登录地址';
comment on column admin.last_login_ua is '最后登录代理';
comment on column admin.lock is '是否锁定编辑';
comment on column admin.created_at is '创建时间';
comment on column admin.updated_at is '更新时间';
comment on column admin.deleted_at is '删除时间';
-- admin_role
drop table if exists admin_role cascade;
create table admin_role (
id int generated by default as identity primary key,
name text not null,
description text,
active bool not null default true,
sort int not null default 0,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_admin_role_name on admin_role (name) where deleted_at is null;
create index idx_admin_role_created_at on admin_role (created_at) where deleted_at is null;
-- admin_role表字段注释
comment on table admin_role is '管理员角色关联表';
comment on column admin_role.id is '管理员角色ID';
comment on column admin_role.name is '角色名称';
comment on column admin_role.description is '角色描述';
comment on column admin_role.active is '是否激活';
comment on column admin_role.sort is '排序';
comment on column admin_role.created_at is '创建时间';
comment on column admin_role.updated_at is '更新时间';
comment on column admin_role.deleted_at is '删除时间';
-- endregion -- endregion
-- ==================== -- ====================
@@ -219,10 +260,12 @@ drop table if exists "user" cascade;
create table "user" ( create table "user" (
id int generated by default as identity primary key, id int generated by default as identity primary key,
admin_id int, admin_id int,
discount_id int,
phone text not null unique, phone text not null unique,
username text, username text,
email text, email text,
password text, password text,
source int default 0,
name text, name text,
avatar text, avatar text,
status int not null default 1, status int not null default 1,
@@ -240,6 +283,7 @@ create table "user" (
deleted_at timestamptz deleted_at timestamptz
); );
create index idx_user_admin_id on "user" (admin_id) where deleted_at is null; create index idx_user_admin_id on "user" (admin_id) where deleted_at is null;
create index idx_user_discount_id on "user" (discount_id) where deleted_at is null;
create unique index udx_user_phone on "user" (phone) where deleted_at is null; create unique index udx_user_phone on "user" (phone) where deleted_at is null;
create unique index udx_user_username on "user" (username) where deleted_at is null; create unique index udx_user_username on "user" (username) where deleted_at is null;
create unique index udx_user_email on "user" (email) where deleted_at is null; create unique index udx_user_email on "user" (email) where deleted_at is null;
@@ -249,9 +293,11 @@ create index idx_user_created_at on "user" (created_at) where deleted_at is null
comment on table "user" is '用户表'; comment on table "user" is '用户表';
comment on column "user".id is '用户ID'; comment on column "user".id is '用户ID';
comment on column "user".admin_id is '管理员ID'; comment on column "user".admin_id is '管理员ID';
comment on column "user".discount_id is '折扣ID';
comment on column "user".password is '用户密码'; comment on column "user".password is '用户密码';
comment on column "user".username is '用户名'; comment on column "user".username is '用户名';
comment on column "user".phone is '手机号码'; comment on column "user".phone is '手机号码';
comment on column "user".source is '用户来源0-官网注册1-管理员添加2-代理商注册3-代理商添加';
comment on column "user".name is '真实姓名'; comment on column "user".name is '真实姓名';
comment on column "user".avatar is '头像URL'; comment on column "user".avatar is '头像URL';
comment on column "user".status is '用户状态0-禁用1-正常'; comment on column "user".status is '用户状态0-禁用1-正常';
@@ -389,6 +435,7 @@ create table permission (
parent_id int, parent_id int,
name text not null, name text not null,
description text, description text,
sort int,
created_at timestamptz default current_timestamp, created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp, updated_at timestamptz default current_timestamp,
deleted_at timestamptz deleted_at timestamptz
@@ -403,6 +450,7 @@ comment on column permission.id is '权限ID';
comment on column permission.parent_id is '父权限ID'; comment on column permission.parent_id is '父权限ID';
comment on column permission.name is '权限名称'; comment on column permission.name is '权限名称';
comment on column permission.description is '权限描述'; comment on column permission.description is '权限描述';
comment on column permission.sort is '排序';
comment on column permission.created_at is '创建时间'; comment on column permission.created_at is '创建时间';
comment on column permission.updated_at is '更新时间'; comment on column permission.updated_at is '更新时间';
comment on column permission.deleted_at is '删除时间'; comment on column permission.deleted_at is '删除时间';
@@ -410,65 +458,65 @@ comment on column permission.deleted_at is '删除时间';
-- link_user_role -- link_user_role
drop table if exists link_user_role cascade; drop table if exists link_user_role cascade;
create table link_user_role ( create table link_user_role (
id int generated by default as identity primary key, id int generated by default as identity primary key,
user_id int not null, user_id int not null,
role_id int not null user_role_id int not null
); );
create index idx_link_user_role_user_id on link_user_role (user_id); create index idx_link_user_role_user_id on link_user_role (user_id);
create index idx_link_user_role_role_id on link_user_role (role_id); create index idx_link_user_role_role_id on link_user_role (user_role_id);
-- link_user_role表字段注释 -- link_user_role表字段注释
comment on table link_user_role is '用户角色关联表'; comment on table link_user_role is '用户角色关联表';
comment on column link_user_role.id is '关联ID'; comment on column link_user_role.id is '关联ID';
comment on column link_user_role.user_id is '用户ID'; comment on column link_user_role.user_id is '用户ID';
comment on column link_user_role.role_id is '角色ID'; comment on column link_user_role.user_role_id is '角色ID';
-- link_admin_role -- link_admin_role
drop table if exists link_admin_role cascade; drop table if exists link_admin_role cascade;
create table link_admin_role ( create table link_admin_role (
id int generated by default as identity primary key, id int generated by default as identity primary key,
admin_id int not null, admin_id int not null,
role_id int not null admin_role_id int not null
); );
create index idx_link_admin_role_admin_id on link_admin_role (admin_id); create index idx_link_admin_role_admin_id on link_admin_role (admin_id);
create index idx_link_admin_role_role_id on link_admin_role (role_id); create index idx_link_admin_role_role_id on link_admin_role (admin_role_id);
-- link_admin_role表字段注释 -- link_admin_role表字段注释
comment on table link_admin_role is '管理员角色关联表'; comment on table link_admin_role is '管理员角色关联表';
comment on column link_admin_role.id is '关联ID'; comment on column link_admin_role.id is '关联ID';
comment on column link_admin_role.admin_id is '管理员ID'; comment on column link_admin_role.admin_id is '管理员ID';
comment on column link_admin_role.role_id is '角色ID'; comment on column link_admin_role.admin_role_id is '角色ID';
-- link_user_role_permission -- link_user_role_permission
drop table if exists link_user_role_permission cascade; drop table if exists link_user_role_permission cascade;
create table link_user_role_permission ( create table link_user_role_permission (
id int generated by default as identity primary key, id int generated by default as identity primary key,
role_id int not null, user_role_id int not null,
permission_id int not null permission_id int not null
); );
create index idx_link_user_role_permission_role_id on link_user_role_permission (role_id); create index idx_link_user_role_permission_role_id on link_user_role_permission (user_role_id);
create index idx_link_user_role_permission_permission_id on link_user_role_permission (permission_id); create index idx_link_user_role_permission_permission_id on link_user_role_permission (permission_id);
-- link_user_role_permission表字段注释 -- link_user_role_permission表字段注释
comment on table link_user_role_permission is '用户角色权限关联表'; comment on table link_user_role_permission is '用户角色权限关联表';
comment on column link_user_role_permission.id is '关联ID'; comment on column link_user_role_permission.id is '关联ID';
comment on column link_user_role_permission.role_id is '角色ID'; comment on column link_user_role_permission.user_role_id is '角色ID';
comment on column link_user_role_permission.permission_id is '权限ID'; comment on column link_user_role_permission.permission_id is '权限ID';
-- link_admin_role_permission -- link_admin_role_permission
drop table if exists link_admin_role_permission cascade; drop table if exists link_admin_role_permission cascade;
create table link_admin_role_permission ( create table link_admin_role_permission (
id int generated by default as identity primary key, id int generated by default as identity primary key,
role_id int not null, admin_role_id int not null,
permission_id int not null permission_id int not null
); );
create index idx_link_admin_role_permission_role_id on link_admin_role_permission (role_id); create index idx_link_admin_role_permission_role_id on link_admin_role_permission (admin_role_id);
create index idx_link_admin_role_permission_permission_id on link_admin_role_permission (permission_id); create index idx_link_admin_role_permission_permission_id on link_admin_role_permission (permission_id);
-- link_admin_role_permission表字段注释 -- link_admin_role_permission表字段注释
comment on table link_admin_role_permission is '管理员角色权限关联表'; comment on table link_admin_role_permission is '管理员角色权限关联表';
comment on column link_admin_role_permission.id is '关联ID'; comment on column link_admin_role_permission.id is '关联ID';
comment on column link_admin_role_permission.role_id is '角色ID'; comment on column link_admin_role_permission.admin_role_id is '角色ID';
comment on column link_admin_role_permission.permission_id is '权限ID'; comment on column link_admin_role_permission.permission_id is '权限ID';
-- link_client_permission -- link_client_permission
@@ -497,13 +545,14 @@ comment on column link_client_permission.permission_id is '权限ID';
drop table if exists proxy cascade; drop table if exists proxy cascade;
create table proxy ( create table proxy (
id int generated by default as identity primary key, id int generated by default as identity primary key,
version int not null, version int not null,
mac text not null, mac text not null,
ip inet not null, ip inet not null,
host text,
secret text, secret text,
type int not null, type int not null,
status int not null, status int not null,
meta jsonb not null, meta jsonb,
created_at timestamptz default current_timestamp, created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp, updated_at timestamptz default current_timestamp,
deleted_at timestamptz deleted_at timestamptz
@@ -518,8 +567,9 @@ comment on column proxy.id is '代理服务ID';
comment on column proxy.version is '代理服务版本'; comment on column proxy.version is '代理服务版本';
comment on column proxy.mac is '代理服务名称'; comment on column proxy.mac is '代理服务名称';
comment on column proxy.ip is '代理服务地址'; comment on column proxy.ip is '代理服务地址';
comment on column proxy.type is '代理服务类型1-自有2-白银'; comment on column proxy.host is '代理服务域名';
comment on column proxy.secret is '代理服务密钥'; comment on column proxy.secret is '代理服务密钥';
comment on column proxy.type is '代理服务类型1-自有2-白银';
comment on column proxy.status is '代理服务状态0-离线1-在线'; comment on column proxy.status is '代理服务状态0-离线1-在线';
comment on column proxy.meta is '代理服务元信息'; comment on column proxy.meta is '代理服务元信息';
comment on column proxy.created_at is '创建时间'; comment on column proxy.created_at is '创建时间';
@@ -600,8 +650,10 @@ create table channel (
resource_id int not null, resource_id int not null,
batch_no text not null, batch_no text not null,
proxy_id int not null, proxy_id int not null,
host text not null,
port int not null, port int not null,
edge_id int, edge_id int,
edge_ref text,
filter_isp int, filter_isp int,
filter_prov text, filter_prov text,
filter_city text, filter_city text,
@@ -626,8 +678,10 @@ comment on column channel.user_id is '用户ID';
comment on column channel.resource_id is '套餐ID'; comment on column channel.resource_id is '套餐ID';
comment on column channel.batch_no is '批次编号'; comment on column channel.batch_no is '批次编号';
comment on column channel.proxy_id is '代理ID'; comment on column channel.proxy_id is '代理ID';
comment on column channel.host is '代理主机(快照)';
comment on column channel.port is '代理端口'; comment on column channel.port is '代理端口';
comment on column channel.edge_id is '节点ID手动配置'; comment on column channel.edge_id is '节点ID手动配置';
comment on column channel.edge_ref is '外部节点引用用于索引没有ID的外部非受控节点';
comment on column channel.filter_isp is '运营商过滤(自动配置):参考 edge.isp'; comment on column channel.filter_isp is '运营商过滤(自动配置):参考 edge.isp';
comment on column channel.filter_prov is '省份过滤(自动配置)'; comment on column channel.filter_prov is '省份过滤(自动配置)';
comment on column channel.filter_city is '城市过滤(自动配置)'; comment on column channel.filter_city is '城市过滤(自动配置)';
@@ -675,14 +729,93 @@ comment on column product.created_at is '创建时间';
comment on column product.updated_at is '更新时间'; comment on column product.updated_at is '更新时间';
comment on column product.deleted_at is '删除时间'; comment on column product.deleted_at is '删除时间';
-- product_discount
drop table if exists product_discount cascade;
create table product_discount (
id int generated by default as identity primary key,
name text,
discount int,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
-- product_discount表字段注释
comment on table product_discount is '产品折扣表';
comment on column product_discount.id is 'ID';
comment on column product_discount.name is '折扣名称';
comment on column product_discount.discount is '折扣0 - 100 的整数,表示 xx 折';
comment on column product_discount.created_at is '创建时间';
comment on column product_discount.updated_at is '更新时间';
comment on column product_discount.deleted_at is '删除时间';
-- product_sku
drop table if exists product_sku cascade;
create table product_sku (
id int generated by default as identity primary key,
product_id int not null,
discount_id int,
code text not null unique,
name text not null,
price decimal not null,
price_min decimal not null,
status int not null default 1,
sort int not null default 0,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create index idx_product_sku_product_id on product_sku (product_id) where deleted_at is null;
create index idx_product_sku_discount_id on product_sku (discount_id) where deleted_at is null;
create unique index idx_product_sku_code on product_sku (code) where deleted_at is null;
-- product_sku表字段注释
comment on table product_sku is '产品SKU表';
comment on column product_sku.id is 'SKU ID';
comment on column product_sku.product_id is '产品ID';
comment on column product_sku.discount_id is '折扣ID';
comment on column product_sku.code is 'SKU 代码:格式为 key=value,key=value,...其中key:value 是 SKU 的属性,多个属性用逗号分隔';
comment on column product_sku.name is 'SKU 可读名称';
comment on column product_sku.price_min is '最低价格';
comment on column product_sku.status is 'SKU状态0-禁用1-正常';
comment on column product_sku.sort is '排序';
comment on column product_sku.created_at is '创建时间';
comment on column product_sku.updated_at is '更新时间';
comment on column product_sku.deleted_at is '删除时间';
-- product_sku_user
drop table if exists product_sku_user cascade;
create table product_sku_user (
id int generated by default as identity primary key,
user_id int not null,
product_sku_id int not null,
discount_id int,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp
);
create index idx_product_sku_user_user_id on product_sku_user (user_id);
create index idx_product_sku_user_product_sku_id on product_sku_user (product_sku_id);
create index idx_product_sku_user_discount_id on product_sku_user (discount_id);
-- product_sku_user表字段注释
comment on table product_sku_user is '用户产品SKU表';
comment on column product_sku_user.id is 'ID';
comment on column product_sku_user.user_id is '用户ID';
comment on column product_sku_user.product_sku_id is '产品SKU ID';
comment on column product_sku_user.discount_id is '折扣ID';
comment on column product_sku_user.created_at is '创建时间';
comment on column product_sku_user.updated_at is '更新时间';
-- resource -- resource
drop table if exists resource cascade; drop table if exists resource cascade;
create table resource ( create table resource (
id int generated by default as identity primary key, id int generated by default as identity primary key,
user_id int not null, user_id int not null,
resource_no text, resource_no text not null,
active bool not null default true, code text,
type int not null, type int not null,
active bool not null default true,
created_at timestamptz default current_timestamp, created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp, updated_at timestamptz default current_timestamp,
deleted_at timestamptz deleted_at timestamptz
@@ -690,6 +823,7 @@ create table resource (
create unique index udx_resource_resource_no on resource (resource_no); create unique index udx_resource_resource_no on resource (resource_no);
create index idx_resource_user_id on resource (user_id) where deleted_at is null; create index idx_resource_user_id on resource (user_id) where deleted_at is null;
create index idx_resource_created_at on resource (created_at) where deleted_at is null; create index idx_resource_created_at on resource (created_at) where deleted_at is null;
create index idx_resource_code on resource (code) where deleted_at is null;
-- resource表字段注释 -- resource表字段注释
comment on table resource is '套餐表'; comment on table resource is '套餐表';
@@ -698,6 +832,7 @@ comment on column resource.user_id is '用户ID';
comment on column resource.resource_no is '套餐编号'; comment on column resource.resource_no is '套餐编号';
comment on column resource.active is '套餐状态'; comment on column resource.active is '套餐状态';
comment on column resource.type is '套餐类型1-短效动态2-长效动态'; comment on column resource.type is '套餐类型1-短效动态2-长效动态';
comment on column resource.code is '产品编码';
comment on column resource.created_at is '创建时间'; comment on column resource.created_at is '创建时间';
comment on column resource.updated_at is '更新时间'; comment on column resource.updated_at is '更新时间';
comment on column resource.deleted_at is '删除时间'; comment on column resource.deleted_at is '删除时间';
@@ -707,58 +842,60 @@ drop table if exists resource_short cascade;
create table resource_short ( create table resource_short (
id int generated by default as identity primary key, id int generated by default as identity primary key,
resource_id int not null, resource_id int not null,
type int not null, code text,
live int not null, live int not null,
expire timestamptz, type int not null,
quota int, quota int not null,
expire_at timestamptz,
used int not null default 0, used int not null default 0,
daily_limit int not null default 0, daily int not null default 0,
daily_used int not null default 0, last_at timestamptz
daily_last timestamptz
); );
create index idx_resource_short_resource_id on resource_short (resource_id); create index idx_resource_short_resource_id on resource_short (resource_id);
create index idx_resource_short_code on resource_short (code);
-- resource_short表字段注释 -- resource_short表字段注释
comment on table resource_short is '短效动态套餐表'; comment on table resource_short is '短效动态套餐表';
comment on column resource_short.id is 'ID'; comment on column resource_short.id is 'ID';
comment on column resource_short.resource_id is '套餐ID'; comment on column resource_short.resource_id is '套餐ID';
comment on column resource_short.type is '套餐类型1-包时2-包量'; comment on column resource_short.code is '产品套餐编码';
comment on column resource_short.live is '可用时长(秒)'; comment on column resource_short.live is '可用时长(秒)';
comment on column resource_short.quota is '配额数'; comment on column resource_short.type is '套餐类型1-包时2-包';
comment on column resource_short.used is '已用数量'; comment on column resource_short.quota is '每日配额(包时)或总配额(包量)';
comment on column resource_short.expire is '过期时间'; comment on column resource_short.expire_at is '套餐过期时间,包时模式可用';
comment on column resource_short.daily_limit is '每日限制'; comment on column resource_short.used is '总用量';
comment on column resource_short.daily_used is '今日已用数'; comment on column resource_short.daily is '当日用';
comment on column resource_short.daily_last is '今日最后使用时间'; comment on column resource_short.last_at is '最后使用时间';
-- resource_long -- resource_long
drop table if exists resource_long cascade; drop table if exists resource_long cascade;
create table resource_long ( create table resource_long (
id int generated by default as identity primary key, id int generated by default as identity primary key,
resource_id int not null, resource_id int not null,
type int not null, code text,
live int not null, live int not null,
expire timestamptz, type int not null,
quota int, quota int not null,
expire_at timestamptz,
used int not null default 0, used int not null default 0,
daily_limit int not null default 0, daily int not null default 0,
daily_used int not null default 0, last_at timestamptz
daily_last timestamptz
); );
create index idx_resource_long_resource_id on resource_long (resource_id); create index idx_resource_long_resource_id on resource_long (resource_id);
create index idx_resource_long_code on resource_long (code);
-- resource_long表字段注释 -- resource_long表字段注释
comment on table resource_long is '长效动态套餐表'; comment on table resource_long is '长效动态套餐表';
comment on column resource_long.id is 'ID'; comment on column resource_long.id is 'ID';
comment on column resource_long.resource_id is '套餐ID'; comment on column resource_long.resource_id is '套餐ID';
comment on column resource_long.code is '产品套餐编码';
comment on column resource_long.live is '可用时长(小时)';
comment on column resource_long.type is '套餐类型1-包时2-包量'; comment on column resource_long.type is '套餐类型1-包时2-包量';
comment on column resource_long.live is '可用时长(天)'; comment on column resource_long.quota is '每日配额(包时)或总配额(包量)';
comment on column resource_long.quota is '配额数量'; comment on column resource_long.expire_at is '套餐过期时间,包时模式可用';
comment on column resource_long.used is '已用数'; comment on column resource_long.used is '总用';
comment on column resource_long.expire is '过期时间'; comment on column resource_long.daily is '当日用量';
comment on column resource_long.daily_limit is '每日限制'; comment on column resource_long.last_at is '最后使用时间';
comment on column resource_long.daily_used is '今日已用数量';
comment on column resource_long.daily_last is '今日最后使用时间';
-- endregion -- endregion
@@ -854,10 +991,12 @@ create table bill (
trade_id int, trade_id int,
resource_id int, resource_id int,
refund_id int, refund_id int,
coupon_user_id int,
bill_no text not null, bill_no text not null,
info text, info text,
type int not null, type int not null,
amount decimal(12, 2) not null default 0, amount decimal(12, 2) not null default 0,
actual decimal(12, 2) not null default 0,
created_at timestamptz default current_timestamp, created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp, updated_at timestamptz default current_timestamp,
deleted_at timestamptz deleted_at timestamptz
@@ -867,6 +1006,7 @@ create index idx_bill_user_id on bill (user_id) where deleted_at is null;
create index idx_bill_trade_id on bill (trade_id) where deleted_at is null; create index idx_bill_trade_id on bill (trade_id) where deleted_at is null;
create index idx_bill_resource_id on bill (resource_id) where deleted_at is null; create index idx_bill_resource_id on bill (resource_id) where deleted_at is null;
create index idx_bill_refund_id on bill (refund_id) where deleted_at is null; create index idx_bill_refund_id on bill (refund_id) where deleted_at is null;
create index idx_bill_coupon_id on bill (coupon_user_id) where deleted_at is null;
create index idx_bill_created_at on bill (created_at) where deleted_at is null; create index idx_bill_created_at on bill (created_at) where deleted_at is null;
-- bill表字段注释 -- bill表字段注释
@@ -876,47 +1016,102 @@ comment on column bill.user_id is '用户ID';
comment on column bill.trade_id is '订单ID'; comment on column bill.trade_id is '订单ID';
comment on column bill.resource_id is '套餐ID'; comment on column bill.resource_id is '套餐ID';
comment on column bill.refund_id is '退款ID'; comment on column bill.refund_id is '退款ID';
comment on column bill.coupon_user_id is '优惠券发放ID';
comment on column bill.bill_no is '易读账单号'; comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息'; comment on column bill.info is '产品可读信息';
comment on column bill.type is '账单类型1-消费2-退款3-充值'; comment on column bill.type is '账单类型1-消费2-退款3-充值';
comment on column bill.amount is '账单金额'; comment on column bill.amount is '应付金额';
comment on column bill.actual is '实付金额';
comment on column bill.created_at is '创建时间'; comment on column bill.created_at is '创建时间';
comment on column bill.updated_at is '更新时间'; comment on column bill.updated_at is '更新时间';
comment on column bill.deleted_at is '删除时间'; comment on column bill.deleted_at is '删除时间';
-- balance_activity 余额变动记录
drop table if exists balance_activity cascade;
create table balance_activity (
id int generated by default as identity primary key,
user_id int not null,
bill_id int,
admin_id int,
amount text not null,
balance_prev text not null,
balance_curr text not null,
remark text,
created_at timestamptz default current_timestamp
);
create index idx_balance_activity_user_id on balance_activity (user_id);
create index idx_balance_activity_bill_id on balance_activity (bill_id);
create index idx_balance_activity_admin_id on balance_activity (admin_id);
create index idx_balance_activity_created_at on balance_activity (created_at);
-- balance_activity表字段注释
comment on table balance_activity is '余额变动记录表';
comment on column balance_activity.id is '记录ID';
comment on column balance_activity.user_id is '用户ID';
comment on column balance_activity.bill_id is '账单ID';
comment on column balance_activity.admin_id is '管理员ID';
comment on column balance_activity.amount is '变动金额';
comment on column balance_activity.balance_prev is '变动前余额';
comment on column balance_activity.balance_curr is '变动后余额';
comment on column balance_activity.remark is '备注';
comment on column balance_activity.created_at is '创建时间';
-- coupon 优惠券 -- coupon 优惠券
drop table if exists coupon cascade; drop table if exists coupon cascade;
create table coupon ( create table coupon (
id int generated by default as identity primary key, id int generated by default as identity primary key,
user_id int, name text not null,
code text not null, amount decimal(12, 2) not null default 0,
remark text, min_amount decimal(12, 2) not null default 0,
amount decimal(12, 2) not null default 0, count int not null default 0,
min_amount decimal(12, 2) not null default 0, status int not null default 1,
status int not null default 0, expire_type int not null default 0,
expire_at timestamptz, expire_at timestamptz,
created_at timestamptz default current_timestamp, expire_in int,
updated_at timestamptz default current_timestamp, created_at timestamptz default current_timestamp,
deleted_at timestamptz updated_at timestamptz default current_timestamp,
deleted_at timestamptz
); );
create index idx_coupon_user_id on coupon (user_id) where deleted_at is null;
create index idx_coupon_code on coupon (code) where deleted_at is null;
create index idx_coupon_created_at on coupon (created_at) where deleted_at is null;
-- coupon表字段注释 -- coupon表字段注释
comment on table coupon is '优惠券表'; comment on table coupon is '优惠券表';
comment on column coupon.id is '优惠券ID'; comment on column coupon.id is '优惠券ID';
comment on column coupon.user_id is '用户ID'; comment on column coupon.name is '优惠券名称';
comment on column coupon.code is '优惠券代码';
comment on column coupon.remark is '优惠券备注';
comment on column coupon.amount is '优惠券金额'; comment on column coupon.amount is '优惠券金额';
comment on column coupon.min_amount is '最低消费金额'; comment on column coupon.min_amount is '最低消费金额';
comment on column coupon.status is '优惠券状态0-未使用1-已使用2-已过期'; comment on column coupon.count is '优惠券数量';
comment on column coupon.expire_at is '过期时间'; comment on column coupon.status is '优惠券状态0-禁用1-正常';
comment on column coupon.expire_type is '过期类型0-不过期1-固定日期2-相对日期(从发放时间算起)';
comment on column coupon.expire_at is '过期时间,固定日期必填';
comment on column coupon.expire_in is '过期时长(天),相对日期必填';
comment on column coupon.created_at is '创建时间'; comment on column coupon.created_at is '创建时间';
comment on column coupon.updated_at is '更新时间'; comment on column coupon.updated_at is '更新时间';
comment on column coupon.deleted_at is '删除时间'; comment on column coupon.deleted_at is '删除时间';
-- coupon_user 优惠券发放
drop table if exists coupon_user cascade;
create table coupon_user (
id int generated by default as identity primary key,
coupon_id int not null,
user_id int not null,
status int not null default 0,
expire_at timestamptz,
used_at timestamptz,
created_at timestamptz default current_timestamp
);
create index idx_coupon_user_coupon_id on coupon_user (coupon_id);
create index idx_coupon_user_user_id on coupon_user (user_id);
-- coupon_user表字段注释
comment on table coupon_user is '优惠券发放表';
comment on column coupon_user.id is '记录ID';
comment on column coupon_user.coupon_id is '优惠券ID';
comment on column coupon_user.user_id is '用户ID';
comment on column coupon_user.status is '使用状态0-未使用1-已使用';
comment on column coupon_user.expire_at is '过期时间';
comment on column coupon_user.used_at is '使用时间';
comment on column coupon_user.created_at is '创建时间';
-- endregion -- endregion
-- ==================== -- ====================
@@ -926,6 +1121,8 @@ comment on column coupon.deleted_at is '删除时间';
-- user表外键 -- user表外键
alter table "user" alter table "user"
add constraint fk_user_admin_id foreign key (admin_id) references admin (id) on delete set null; add constraint fk_user_admin_id foreign key (admin_id) references admin (id) on delete set null;
alter table "user"
add constraint fk_user_discount_id foreign key (discount_id) references product_discount (id) on delete set null;
-- session表外键 -- session表外键
alter table session alter table session
@@ -941,23 +1138,23 @@ alter table permission
alter table link_user_role alter table link_user_role
add constraint fk_link_user_role_user_id foreign key (user_id) references "user" (id) on delete cascade; add constraint fk_link_user_role_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table link_user_role alter table link_user_role
add constraint fk_link_user_role_role_id foreign key (role_id) references user_role (id) on delete cascade; add constraint fk_link_user_role_role_id foreign key (user_role_id) references user_role (id) on delete cascade;
-- link_admin_role表外键 -- link_admin_role表外键
alter table link_admin_role alter table link_admin_role
add constraint fk_link_admin_role_admin_id foreign key (admin_id) references admin (id) on delete cascade; add constraint fk_link_admin_role_admin_id foreign key (admin_id) references admin (id) on delete cascade;
alter table link_admin_role alter table link_admin_role
add constraint fk_link_admin_role_role_id foreign key (role_id) references admin_role (id) on delete cascade; add constraint fk_link_admin_role_role_id foreign key (admin_role_id) references admin_role (id) on delete cascade;
-- link_user_role_permission表外键 -- link_user_role_permission表外键
alter table link_user_role_permission alter table link_user_role_permission
add constraint fk_link_user_role_permission_role_id foreign key (role_id) references user_role (id) on delete cascade; add constraint fk_link_user_role_permission_role_id foreign key (user_role_id) references user_role (id) on delete cascade;
alter table link_user_role_permission alter table link_user_role_permission
add constraint fk_link_user_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade; add constraint fk_link_user_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;
-- link_admin_role_permission表外键 -- link_admin_role_permission表外键
alter table link_admin_role_permission alter table link_admin_role_permission
add constraint fk_link_admin_role_permission_role_id foreign key (role_id) references admin_role (id) on delete cascade; add constraint fk_link_admin_role_permission_role_id foreign key (admin_role_id) references admin_role (id) on delete cascade;
alter table link_admin_role_permission alter table link_admin_role_permission
add constraint fk_link_admin_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade; add constraint fk_link_admin_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;
@@ -984,14 +1181,20 @@ alter table channel
-- resource表外键 -- resource表外键
alter table resource alter table resource
add constraint fk_resource_user_id foreign key (user_id) references "user" (id) on delete cascade; add constraint fk_resource_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table resource
add constraint fk_product_code foreign key (code) references product (code) on update cascade on delete restrict;
-- resource_short表外键 -- resource_short表外键
alter table resource_short alter table resource_short
add constraint fk_resource_short_resource_id foreign key (resource_id) references resource (id) on delete cascade; add constraint fk_resource_short_resource_id foreign key (resource_id) references resource (id) on delete cascade;
alter table resource_short
add constraint fk_resource_short_code foreign key (code) references product_sku (code) on update cascade on delete restrict;
-- resource_long表外键 -- resource_long表外键
alter table resource_long alter table resource_long
add constraint fk_resource_long_resource_id foreign key (resource_id) references resource (id) on delete cascade; add constraint fk_resource_long_resource_id foreign key (resource_id) references resource (id) on delete cascade;
alter table resource_long
add constraint fk_resource_long_code foreign key (code) references product_sku (code) on update cascade on delete restrict;
-- trade表外键 -- trade表外键
alter table trade alter table trade
@@ -1012,22 +1215,33 @@ alter table bill
add constraint fk_bill_resource_id foreign key (resource_id) references resource (id) on delete set null; add constraint fk_bill_resource_id foreign key (resource_id) references resource (id) on delete set null;
alter table bill alter table bill
add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null; add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null;
alter table bill
add constraint fk_bill_coupon_id foreign key (coupon_user_id) references coupon_user (id) on delete set null;
-- coupon表外键 -- coupon_user表外键
alter table coupon alter table coupon_user
add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade; add constraint fk_coupon_user_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table coupon_user
-- endregion add constraint fk_coupon_user_coupon_id foreign key (coupon_id) references coupon (id) on delete cascade;
-- ==================== -- product_sku表外键
-- region 填充数据 alter table product_sku
-- ==================== add constraint fk_product_sku_product_id foreign key (product_id) references product (id) on delete cascade;
alter table product_sku
insert into client ( add constraint fk_product_sku_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;
client_id, client_secret, redirect_uri, spec, name, type
) -- product_sku_user表外键
values ( alter table product_sku_user
'web', '$2a$10$Ss12mXQgpYyo1CKIZ3URouDm.Lc2KcYJzsvEK2PTIXlv6fHQht45a', '', 3, 'web', 1 add constraint fk_product_sku_user_user_id foreign key (user_id) references "user" (id) on delete cascade;
); alter table product_sku_user
add constraint fk_product_sku_user_product_sku_id foreign key (product_sku_id) references product_sku (id) on delete cascade;
alter table product_sku_user
add constraint fk_product_sku_user_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;
--balance_activity表外键
alter table balance_activity
add constraint fk_balance_activity_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table balance_activity
add constraint fk_balance_activity_bill_id foreign key (bill_id) references bill (id) on delete set null;
-- endregion -- endregion

81
scripts/sql/update.sql Normal file
View File

@@ -0,0 +1,81 @@
-- ====================
-- region 套餐更新
-- ====================
-- --------------------------
-- 短效动态
-- --------------------------
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 3 分钟', price = 10.00, price_min = 10.00, sort = 1 where code = 'mode=quota&live=3&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 5 分钟', price = 10.00, price_min = 10.00, sort = 2 where code = 'mode=quota&live=5&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 10 分钟', price = 10.00, price_min = 10.00, sort = 3 where code = 'mode=quota&live=10&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 15 分钟', price = 10.00, price_min = 10.00, sort = 4 where code = 'mode=quota&live=15&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 30 分钟', price = 10.00, price_min = 10.00, sort = 5 where code = 'mode=quota&live=30&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 7 天', price = 10.00, price_min = 10.00, sort = 6 where code = 'mode=time&live=3&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 7 天', price = 10.00, price_min = 10.00, sort = 7 where code = 'mode=time&live=5&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 7 天', price = 10.00, price_min = 10.00, sort = 8 where code = 'mode=time&live=10&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 7 天', price = 10.00, price_min = 10.00, sort = 9 where code = 'mode=time&live=15&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 7 天', price = 10.00, price_min = 10.00, sort = 10 where code = 'mode=time&live=30&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 15 天', price = 10.00, price_min = 10.00, sort = 11 where code = 'mode=time&live=3&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 15 天', price = 10.00, price_min = 10.00, sort = 12 where code = 'mode=time&live=5&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 15 天', price = 10.00, price_min = 10.00, sort = 13 where code = 'mode=time&live=10&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 15 天', price = 10.00, price_min = 10.00, sort = 14 where code = 'mode=time&live=15&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 15 天', price = 10.00, price_min = 10.00, sort = 15 where code = 'mode=time&live=30&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 30 天', price = 10.00, price_min = 10.00, sort = 16 where code = 'mode=time&live=3&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 30 天', price = 10.00, price_min = 10.00, sort = 17 where code = 'mode=time&live=5&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 30 天', price = 10.00, price_min = 10.00, sort = 18 where code = 'mode=time&live=10&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 30 天', price = 10.00, price_min = 10.00, sort = 19 where code = 'mode=time&live=15&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 30 天', price = 10.00, price_min = 10.00, sort = 20 where code = 'mode=time&live=30&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 90 天', price = 10.00, price_min = 10.00, sort = 21 where code = 'mode=time&live=3&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 90 天', price = 10.00, price_min = 10.00, sort = 22 where code = 'mode=time&live=5&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 90 天', price = 10.00, price_min = 10.00, sort = 23 where code = 'mode=time&live=10&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 90 天', price = 10.00, price_min = 10.00, sort = 24 where code = 'mode=time&live=15&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 90 天', price = 10.00, price_min = 10.00, sort = 25 where code = 'mode=time&live=30&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 180 天', price = 10.00, price_min = 10.00, sort = 26 where code = 'mode=time&live=3&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 180 天', price = 10.00, price_min = 10.00, sort = 27 where code = 'mode=time&live=5&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 180 天', price = 10.00, price_min = 10.00, sort = 28 where code = 'mode=time&live=10&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 180 天', price = 10.00, price_min = 10.00, sort = 29 where code = 'mode=time&live=15&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 180 天', price = 10.00, price_min = 10.00, sort = 30 where code = 'mode=time&live=30&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 365 天', price = 10.00, price_min = 10.00, sort = 31 where code = 'mode=time&live=3&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 365 天', price = 10.00, price_min = 10.00, sort = 32 where code = 'mode=time&live=5&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 365 天', price = 10.00, price_min = 10.00, sort = 33 where code = 'mode=time&live=10&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 365 天', price = 10.00, price_min = 10.00, sort = 34 where code = 'mode=time&live=15&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 365 天', price = 10.00, price_min = 10.00, sort = 35 where code = 'mode=time&live=30&expire=365' and deleted_at is null;
-- --------------------------
-- 长效动态
-- --------------------------
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 1 小时', price = 10.00, price_min = 10.00, sort = 1 where code = 'mode=quota&live=60&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 4 小时', price = 10.00, price_min = 10.00, sort = 2 where code = 'mode=quota&live=240&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 8 小时', price = 10.00, price_min = 10.00, sort = 3 where code = 'mode=quota&live=480&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 12 小时', price = 10.00, price_min = 10.00, sort = 4 where code = 'mode=quota&live=720&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 24 小时', price = 10.00, price_min = 10.00, sort = 5 where code = 'mode=quota&live=1440&expire=0' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 7 天', price = 10.00, price_min = 10.00, sort = 6 where code = 'mode=time&live=60&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 7 天', price = 10.00, price_min = 10.00, sort = 7 where code = 'mode=time&live=240&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 7 天', price = 10.00, price_min = 10.00, sort = 8 where code = 'mode=time&live=480&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 7 天', price = 10.00, price_min = 10.00, sort = 9 where code = 'mode=time&live=720&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 7 天', price = 10.00, price_min = 10.00, sort = 10 where code = 'mode=time&live=1440&expire=7' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 15 天', price = 10.00, price_min = 10.00, sort = 11 where code = 'mode=time&live=60&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 15 天', price = 10.00, price_min = 10.00, sort = 12 where code = 'mode=time&live=240&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 15 天', price = 10.00, price_min = 10.00, sort = 13 where code = 'mode=time&live=480&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 15 天', price = 10.00, price_min = 10.00, sort = 14 where code = 'mode=time&live=720&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 15 天', price = 10.00, price_min = 10.00, sort = 15 where code = 'mode=time&live=1440&expire=15' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 30 天', price = 10.00, price_min = 10.00, sort = 16 where code = 'mode=time&live=60&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 30 天', price = 10.00, price_min = 10.00, sort = 17 where code = 'mode=time&live=240&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 30 天', price = 10.00, price_min = 10.00, sort = 18 where code = 'mode=time&live=480&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 30 天', price = 10.00, price_min = 10.00, sort = 19 where code = 'mode=time&live=720&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 30 天', price = 10.00, price_min = 10.00, sort = 20 where code = 'mode=time&live=1440&expire=30' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 90 天', price = 10.00, price_min = 10.00, sort = 21 where code = 'mode=time&live=60&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 90 天', price = 10.00, price_min = 10.00, sort = 22 where code = 'mode=time&live=240&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 90 天', price = 10.00, price_min = 10.00, sort = 23 where code = 'mode=time&live=480&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 90 天', price = 10.00, price_min = 10.00, sort = 24 where code = 'mode=time&live=720&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 90 天', price = 10.00, price_min = 10.00, sort = 25 where code = 'mode=time&live=1440&expire=90' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 180 天', price = 10.00, price_min = 10.00, sort = 26 where code = 'mode=time&live=60&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 180 天', price = 10.00, price_min = 10.00, sort = 27 where code = 'mode=time&live=240&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 180 天', price = 10.00, price_min = 10.00, sort = 28 where code = 'mode=time&live=480&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 180 天', price = 10.00, price_min = 10.00, sort = 29 where code = 'mode=time&live=720&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 180 天', price = 10.00, price_min = 10.00, sort = 30 where code = 'mode=time&live=1440&expire=180' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 365 天', price = 10.00, price_min = 10.00, sort = 31 where code = 'mode=time&live=60&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 365 天', price = 10.00, price_min = 10.00, sort = 32 where code = 'mode=time&live=240&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 365 天', price = 10.00, price_min = 10.00, sort = 33 where code = 'mode=time&live=480&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 365 天', price = 10.00, price_min = 10.00, sort = 34 where code = 'mode=time&live=720&expire=365' and deleted_at is null;
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 365 天', price = 10.00, price_min = 10.00, sort = 35 where code = 'mode=time&live=1440&expire=365' and deleted_at is null;

176
web/auth/account.go Normal file
View File

@@ -0,0 +1,176 @@
package auth
import (
"context"
"errors"
"log/slog"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"golang.org/x/crypto/bcrypt"
)
func authClient(clientId string, clientSecrets ...string) (*m.Client, error) {
// 获取客户端信息
client, err := q.Client.
Preload(q.Client.Permissions).
Where(
q.Client.ClientID.Eq(clientId),
q.Client.Status.Eq(1)).
Take()
if err != nil {
return nil, err
}
// 检查客户端密钥
if client.Spec == m.ClientSpecWeb || client.Spec == m.ClientSpecAPI {
if len(clientSecrets) == 0 {
return nil, errors.New("客户端密钥错误")
}
clientSecret := clientSecrets[0]
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
return nil, errors.New("客户端密钥错误")
}
}
// 组织授权信息(一次性请求)
return client, nil
}
func authUser(loginType PwdLoginType, username, password string) (user *m.User, err error) {
switch loginType {
case PwdLoginByPhone:
user, err = authUserBySms(q.Q, username, password)
if err != nil {
return nil, err
}
if user == nil {
user = &m.User{
Phone: username,
Status: m.UserStatusEnabled,
}
}
case PwdLoginByEmail:
user, err = authUserByEmail(q.Q, username, password)
if err != nil {
return nil, err
}
case PwdLoginByPassword:
user, err = authUserByPassword(q.Q, username, password)
if err != nil {
return nil, err
}
default:
return nil, ErrAuthorizeInvalidRequest
}
// 账户状态
if user.Status == m.UserStatusDisabled {
return nil, core.NewBizErr("账号已禁用")
}
return user, nil
}
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
// 验证验证码
err := s.Verifier.VerifySms(context.Background(), username, code, s.VerifierSmsPurposeLogin)
if err != nil {
return nil, core.NewBizErr("短信认证失败", err)
}
// 查找用户
return tx.User.Where(tx.User.Phone.Eq(username)).Take()
}
func authUserByEmail(tx *q.Query, username, code string) (*m.User, error) {
return nil, core.NewServErr("邮箱登录不可用")
}
func authUserByPassword(tx *q.Query, username, password string) (*m.User, error) {
user, err := tx.User.
Where(tx.User.Phone.Eq(username)).
Or(tx.User.Email.Eq(username)).
Or(tx.User.Username.Eq(username)).
Take()
if err != nil {
slog.Debug("查找用户失败", "error", err)
return nil, core.NewBizErr("用户不存在或密码错误")
}
// 验证密码
if user.Password == nil || *user.Password == "" {
slog.Debug("用户未设置密码", "username", username)
return nil, core.NewBizErr("用户不存在或密码错误")
}
if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)) != nil {
slog.Debug("密码验证失败", "username", username)
return nil, core.NewBizErr("用户不存在或密码错误")
}
return user, nil
}
func authAdmin(loginType PwdLoginType, username, password string) (admin *m.Admin, err error) {
switch loginType {
case PwdLoginByPhone, PwdLoginByEmail:
return nil, core.NewServErr("不支持的登录方式:" + string(loginType))
case PwdLoginByPassword:
admin, err = authAdminByPassword(q.Q, username, password)
if err != nil {
return nil, err
}
default:
return nil, ErrAuthorizeInvalidRequest
}
// 账户状态
if admin.Status == m.AdminStatusDisabled {
return nil, core.NewBizErr("账号已禁用")
}
return admin, nil
}
func authAdminByPassword(tx *q.Query, username, password string) (*m.Admin, error) {
admin, err := tx.Admin.Where(tx.Admin.Username.Eq(username)).Take()
if err != nil {
return nil, core.NewBizErr("账号不存在或密码错误")
}
// 验证密码
if admin.Password == "" {
return nil, core.NewBizErr("账号不存在或密码错误")
}
if bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(password)) != nil {
return nil, core.NewBizErr("账号不存在或密码错误")
}
return admin, nil
}
func adminScopes(admin *m.Admin) ([]string, error) {
var scopes []struct{ Name string }
err := q.Admin.
LeftJoin(q.LinkAdminRole, q.LinkAdminRole.AdminID.EqCol(q.Admin.ID)).
LeftJoin(q.LinkAdminRolePermission, q.LinkAdminRolePermission.RoleID.EqCol(q.LinkAdminRole.RoleID)).
LeftJoin(q.Permission, q.Permission.ID.EqCol(q.LinkAdminRolePermission.PermissionID)).
Where(q.Admin.ID.Eq(admin.ID)).
Select(q.Permission.Name).
Scan(&scopes)
if err != nil {
return nil, err
}
scopeNames := make([]string, 0, len(scopes))
for _, scope := range scopes {
if scope.Name == "" {
continue
}
scopeNames = append(scopeNames, scope.Name)
}
return scopeNames, nil
}

View File

@@ -2,6 +2,7 @@ package auth
import ( import (
m "platform/web/models" m "platform/web/models"
"strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -12,7 +13,6 @@ type AuthCtx struct {
Client *m.Client `json:"client,omitempty"` Client *m.Client `json:"client,omitempty"`
Scopes []string `json:"scopes,omitempty"` Scopes []string `json:"scopes,omitempty"`
Session *m.Session `json:"session,omitempty"` Session *m.Session `json:"session,omitempty"`
smap map[string]struct{}
} }
func (a *AuthCtx) PermitUser(scopes ...string) (*AuthCtx, error) { func (a *AuthCtx) PermitUser(scopes ...string) (*AuthCtx, error) {
@@ -68,14 +68,11 @@ func (a *AuthCtx) checkScopes(scopes ...string) bool {
if len(scopes) == 0 || len(a.Scopes) == 0 { if len(scopes) == 0 || len(a.Scopes) == 0 {
return true return true
} }
if len(a.smap) == 0 && len(a.Scopes) > 0 {
for _, scope := range scopes {
a.smap[scope] = struct{}{}
}
}
for _, scope := range scopes { for _, scope := range scopes {
if _, ok := a.smap[scope]; ok { for _, prefix := range a.Scopes {
return true if strings.HasPrefix(scope, prefix) {
return true
}
} }
} }
return false return false

View File

@@ -6,10 +6,8 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"log/slog"
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u" "platform/pkg/u"
"platform/web/core"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm" "platform/web/globals/orm"
m "platform/web/models" m "platform/web/models"
@@ -22,67 +20,52 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type GrantType string // AuthorizeGet 授权端点
func AuthorizeGet(ctx *fiber.Ctx) error {
const ( // 检查请求
GrantAuthorizationCode = GrantType("authorization_code") // 授权码模式 req := new(AuthorizeGetReq)
GrantClientCredentials = GrantType("client_credentials") // 客户端凭证模式 if err := g.Validator.ParseQuery(ctx, req); err != nil {
GrantRefreshToken = GrantType("refresh_token") // 刷新令牌模式 return err
GrantPassword = GrantType("password") // 密码模式(私有扩展) }
)
type PasswordGrantType string // 检查客户端
client, err := authClient(req.ClientID)
if err != nil {
return err
}
const ( if client.RedirectURI == nil || *client.RedirectURI != req.RedirectURI {
GrantPasswordSecret = PasswordGrantType("password") // 账号密码 return errors.New("客户端重定向URI错误")
GrantPasswordPhone = PasswordGrantType("phone_code") // 手机验证码 }
GrantPasswordEmail = PasswordGrantType("email_code") // 邮箱验证码
)
type TokenReq struct { // todo 检查 scope
GrantType GrantType `json:"grant_type" form:"grant_type"`
ClientID string `json:"client_id" form:"client_id"` // 授权确认页面
ClientSecret string `json:"client_secret" form:"client_secret"` return nil
Scope string `json:"scope" form:"scope"`
GrantCodeData
GrantClientData
GrantRefreshData
GrantPasswordData
} }
type GrantCodeData struct { type AuthorizeGetReq struct {
Code string `json:"code" form:"code"` ResponseType string `json:"response_type" validate:"eq=code"`
RedirectURI string `json:"redirect_uri" form:"redirect_uri"` ClientID string `json:"client_id" validate:"required"`
CodeVerifier string `json:"code_verifier" form:"code_verifier"` RedirectURI string `json:"redirect_uri" validate:"required"`
Scope string `json:"scope"`
State string `json:"state"`
} }
type GrantClientData struct { func AuthorizePost(ctx *fiber.Ctx) error {
// todo 解析用户授权的范围
return nil
} }
type GrantRefreshData struct { type AuthorizePostReq struct {
RefreshToken string `json:"refresh_token" form:"refresh_token"` Accept bool `json:"accept"`
} Scope string `json:"scope"`
type GrantPasswordData struct {
LoginType PasswordGrantType `json:"login_type" form:"login_type"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
Remember bool `json:"remember" form:"remember"`
}
type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope,omitempty"`
}
type TokenErrResp struct {
Error string `json:"error"`
Description string `json:"error_description,omitempty"`
} }
// Token 令牌端点
func Token(c *fiber.Ctx) error { func Token(c *fiber.Ctx) error {
now := time.Now() now := time.Now()
@@ -165,6 +148,75 @@ func Token(c *fiber.Ctx) error {
}) })
} }
type TokenReq struct {
GrantType GrantType `json:"grant_type" form:"grant_type"`
ClientID string `json:"client_id" form:"client_id"`
ClientSecret string `json:"client_secret" form:"client_secret"`
Scope string `json:"scope" form:"scope"`
GrantCodeData
GrantClientData
GrantRefreshData
GrantPasswordData
}
type GrantCodeData struct {
Code string `json:"code" form:"code"`
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
}
type GrantClientData struct {
}
type GrantRefreshData struct {
RefreshToken string `json:"refresh_token" form:"refresh_token"`
}
type GrantPasswordData struct {
LoginType PwdLoginType `json:"login_type" form:"login_type"`
LoginPool PwdLoginPool `json:"login_pool" form:"login_pool"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
Remember bool `json:"remember" form:"remember"`
}
type GrantType string
const (
GrantAuthorizationCode = GrantType("authorization_code") // 授权码模式
GrantClientCredentials = GrantType("client_credentials") // 客户端凭证模式
GrantRefreshToken = GrantType("refresh_token") // 刷新令牌模式
GrantPassword = GrantType("password") // 密码模式(私有扩展)
)
type PwdLoginType string
const (
PwdLoginByPassword = PwdLoginType("password") // 账号密码
PwdLoginByPhone = PwdLoginType("phone_code") // 手机验证码
PwdLoginByEmail = PwdLoginType("email_code") // 邮箱验证码
)
type PwdLoginPool string
const (
PwdLoginAsUser = PwdLoginPool("user") // 用户池
PwdLoginAsAdmin = PwdLoginPool("admin") // 管理员池
)
type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
Scope string `json:"scope,omitempty"`
}
type TokenErrResp struct {
Error string `json:"error"`
Description string `json:"error_description,omitempty"`
}
func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) { func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m.Session, error) {
// 检查 code 获取用户授权信息 // 检查 code 获取用户授权信息
@@ -226,7 +278,7 @@ func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second)) session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
} }
err = SaveSession(session) err = SaveSession(q.Q, session)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -236,6 +288,7 @@ func authAuthorizationCode(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.
func authClientCredential(c *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.Time) (*m.Session, error) { func authClientCredential(c *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.Time) (*m.Session, error) {
// todo 检查 scope // todo 检查 scope
scopes := strings.Join(auth.Scopes, " ")
// 生成会话 // 生成会话
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常 ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
@@ -246,10 +299,11 @@ func authClientCredential(c *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.Tim
ClientID: &auth.Client.ID, ClientID: &auth.Client.ID,
AccessToken: uuid.NewString(), AccessToken: uuid.NewString(),
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second), AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
Scopes: &scopes,
} }
// 保存会话 // 保存会话
err := SaveSession(session) err := SaveSession(q.Q, session)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -261,71 +315,95 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常 ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
ua := u.X(c.Get(fiber.HeaderUserAgent)) ua := u.X(c.Get(fiber.HeaderUserAgent))
// 分池认证
var err error
var user *m.User var user *m.User
err := q.Q.Transaction(func(tx *q.Query) (err error) { var admin *m.Admin
switch req.LoginType {
case GrantPasswordPhone:
user, err = authUserBySms(tx, req.Username, req.Password)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if user == nil {
user = &m.User{
Phone: req.Username,
Username: u.P(req.Username),
Status: m.UserStatusEnabled,
}
}
case GrantPasswordEmail:
user, err = authUserByEmail(tx, req.Username, req.Password)
if err != nil {
return err
}
case GrantPasswordSecret:
user, err = authUserByPassword(tx, req.Username, req.Password)
if err != nil {
return err
}
default:
return ErrAuthorizeInvalidRequest
}
// 账户状态 var scopes []string
if user.Status == m.UserStatusDisabled {
slog.Debug("账户状态异常", "username", req.Username, "status", user.Status) pool := req.LoginPool
return core.NewBizErr("账号无法登录") if pool == "" {
pool = PwdLoginAsUser
}
switch pool {
case PwdLoginAsUser:
user, err = authUser(req.LoginType, req.Username, req.Password)
if err != nil {
if req.LoginType != PwdLoginByPhone || !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
// 手机号首次登录的自动创建用户
user = &m.User{
Phone: req.Username,
Status: m.UserStatusEnabled,
}
} }
// 更新用户的登录时间 // 更新用户的登录时间
user.LastLogin = u.P(time.Now()) user.LastLogin = u.P(time.Now())
user.LastLoginIP = ip user.LastLoginIP = ip
user.LastLoginUA = ua user.LastLoginUA = ua
if err := tx.User.Save(user); err != nil {
return err case PwdLoginAsAdmin:
admin, err = authAdmin(req.LoginType, req.Username, req.Password)
if err != nil {
return nil, err
}
scopes, err = adminScopes(admin)
if err != nil {
return nil, err
} }
return nil // 非锁定管理员,不允许为空权限
}) if !admin.Lock && (len(scopes) == 0) {
if err != nil { return nil, ErrAuthorizeInvalidScope // 没有配置权限
return nil, err }
// 更新管理员登录时间
admin.LastLogin = u.P(time.Now())
admin.LastLoginIP = ip
admin.LastLoginUA = ua
default:
return nil, ErrAuthorizeInvalidRequest
} }
// 生成会话 // 生成会话
session := &m.Session{ session := &m.Session{
IP: ip, IP: ip,
UA: ua, UA: ua,
UserID: &user.ID,
ClientID: &auth.Client.ID, ClientID: &auth.Client.ID,
Scopes: u.X(req.Scope), Scopes: u.X(strings.Join(scopes, " ")),
AccessToken: uuid.NewString(), AccessToken: uuid.NewString(),
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second), AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
} }
if req.Remember { if req.Remember {
session.RefreshToken = u.P(uuid.NewString()) session.RefreshToken = u.P(uuid.NewString())
session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second)) session.RefreshTokenExpires = u.P(now.Add(time.Duration(env.SessionRefreshExpire) * time.Second))
} }
err = SaveSession(session) // 保存用户更新和会话
err = q.Q.Transaction(func(tx *q.Query) error {
if user != nil {
if err := tx.User.Save(user); err != nil {
return err
}
session.UserID = &user.ID
}
if admin != nil {
if err := tx.Admin.Save(admin); err != nil {
return err
}
session.AdminID = &admin.ID
}
if err := SaveSession(tx, session); err != nil {
return err
}
return nil
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -353,7 +431,7 @@ func authRefreshToken(_ *fiber.Ctx, _ *AuthCtx, req *TokenReq, now time.Time) (*
} }
// 保存令牌 // 保存令牌
err = SaveSession(session) err = SaveSession(q.Q, session)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -394,12 +472,111 @@ func sendError(c *fiber.Ctx, err error, description ...string) error {
return err return err
} }
func Revoke() error { // Revoke 令牌撤销端点
func Revoke(ctx *fiber.Ctx) error {
_, err := GetAuthCtx(ctx).PermitUser()
if err != nil {
// 用户未登录
return nil
}
// 解析请求参数
req := new(RevokeReq)
if err := ctx.BodyParser(req); err != nil {
return err
}
// 删除会话
err = RemoveSession(ctx.Context(), req.AccessToken, req.RefreshToken)
if err != nil {
return err
}
return nil return nil
} }
func Introspect() error { type RevokeReq struct {
return nil AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
// Introspect 令牌检查端点
func Introspect(ctx *fiber.Ctx) error {
authCtx := GetAuthCtx(ctx)
// 尝试验证用户权限
if _, err := authCtx.PermitUser(); err == nil {
return introspectUser(ctx, authCtx)
}
// 尝试验证管理员权限
if _, err := authCtx.PermitAdmin(); err == nil {
return introspectAdmin(ctx, authCtx)
}
return ErrAuthenticateForbidden
}
// introspectUser 获取并返回用户信息
func introspectUser(ctx *fiber.Ctx, authCtx *AuthCtx) error {
// 获取用户信息
profile, err := q.User.
Where(q.User.ID.Eq(authCtx.User.ID)).
Omit(q.User.DeletedAt).
Take()
if err != nil {
return err
}
// 检查用户是否设置了密码
hasPassword := false
if profile.Password != nil && *profile.Password != "" {
hasPassword = true
profile.Password = nil // 不返回密码
}
// 掩码敏感信息
if profile.Phone != "" {
profile.Phone = u.MaskPhone(profile.Phone)
}
if profile.IDNo != nil && *profile.IDNo != "" {
profile.IDNo = u.P(u.MaskIdNo(*profile.IDNo))
}
return ctx.JSON(struct {
m.User
HasPassword bool `json:"has_password"` // 是否设置了密码
}{*profile, hasPassword})
}
// introspectAdmin 获取并返回管理员信息
func introspectAdmin(ctx *fiber.Ctx, authCtx *AuthCtx) error {
// 获取管理员信息
profile, err := q.Admin.
Preload(q.Admin.Roles, q.Admin.Roles.Permissions).
Where(q.Admin.ID.Eq(authCtx.Admin.ID)).
Omit(q.Admin.DeletedAt, q.Admin.Password).
Take()
if err != nil {
return err
}
// 整理权限列表
scopes := make(map[string]struct{}, 0)
for _, role := range profile.Roles {
for _, permission := range role.Permissions {
scopes[permission.Name] = struct{}{}
}
}
list := make([]string, 0, len(scopes))
for scope := range scopes {
list = append(list, scope)
}
return ctx.JSON(struct {
*m.Admin
Scopes []string `json:"scopes"`
}{profile, list})
} }
type CodeContext struct { type CodeContext struct {

View File

@@ -6,15 +6,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"log/slog" "log/slog"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"strings" "strings"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"golang.org/x/crypto/bcrypt"
) )
func Authenticate() fiber.Handler { func Authenticate() fiber.Handler {
@@ -118,72 +113,14 @@ func authBasic(_ context.Context, token string) (*AuthCtx, error) {
return nil, fmt.Errorf("客户端认证失败:%w", err) return nil, fmt.Errorf("客户端认证失败:%w", err)
} }
return &AuthCtx{ scopes := []string{}
Client: client, if client.Permissions != nil {
Scopes: []string{}, for _, p := range client.Permissions {
}, nil scopes = append(scopes, p.Name)
}
func authClient(clientId, clientSecret string) (*m.Client, error) {
// 获取客户端信息
client, err := q.Client.
Where(
q.Client.ClientID.Eq(clientId),
q.Client.Status.Eq(1)).
Take()
if err != nil {
return nil, err
}
// 检查客户端密钥
if client.Spec == m.ClientSpecWeb || client.Spec == m.ClientSpecAPI {
if bcrypt.CompareHashAndPassword([]byte(client.ClientSecret), []byte(clientSecret)) != nil {
return nil, errors.New("客户端密钥错误")
} }
} }
return &AuthCtx{
// todo 查询客户端关联权限 Client: client,
Scopes: scopes,
// 组织授权信息(一次性请求) }, nil
return client, nil
}
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
// 验证验证码
err := s.Verifier.VerifySms(context.Background(), username, code)
if err != nil {
return nil, core.NewBizErr("短信认证失败:%w", err)
}
// 查找用户
return tx.User.Where(tx.User.Phone.Eq(username)).Take()
}
func authUserByEmail(tx *q.Query, username, code string) (*m.User, error) {
return nil, core.NewServErr("邮箱登录不可用")
}
func authUserByPassword(tx *q.Query, username, password string) (*m.User, error) {
user, err := tx.User.
Where(tx.User.Phone.Eq(username)).
Or(tx.User.Email.Eq(username)).
Or(tx.User.Username.Eq(username)).
Take()
if err != nil {
slog.Debug("查找用户失败", "error", err)
return nil, core.NewBizErr("用户不存在或密码错误")
}
// 验证密码
if user.Password == nil || *user.Password == "" {
slog.Debug("用户未设置密码", "username", username)
return nil, core.NewBizErr("用户不存在或密码错误")
}
if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)) != nil {
slog.Debug("密码验证失败", "username", username)
return nil, core.NewBizErr("用户不存在或密码错误")
}
return user, nil
} }

View File

@@ -29,8 +29,8 @@ func FindSessionByRefresh(refreshToken string, now time.Time) (*m.Session, error
).First() ).First()
} }
func SaveSession(session *m.Session) error { func SaveSession(tx *q.Query, session *m.Session) error {
return q.Session.Save(session) return tx.Session.Save(session)
} }
func RemoveSession(ctx context.Context, accessToken string, refreshToken string) error { func RemoveSession(ctx context.Context, accessToken string, refreshToken string) error {

View File

@@ -19,7 +19,6 @@ type Err struct {
func (e *Err) Error() string { func (e *Err) Error() string {
if e.err != nil { if e.err != nil {
slog.Debug(fmt.Sprintf("%s: %s", e.msg, e.err.Error())) slog.Debug(fmt.Sprintf("%s: %s", e.msg, e.err.Error()))
return e.msg
} }
return e.msg return e.msg
} }

View File

@@ -1,5 +1,17 @@
package core package core
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"platform/pkg/env"
"platform/pkg/u"
"reflect"
"strconv"
"strings"
)
// PageReq 分页请求参数 // PageReq 分页请求参数
type PageReq struct { type PageReq struct {
RawPage int `json:"page"` RawPage int `json:"page"`
@@ -38,3 +50,88 @@ type PageResp struct {
Size int `json:"size"` Size int `json:"size"`
List any `json:"list"` List any `json:"list"`
} }
// Fetch 发送HTTP请求并返回响应
func Fetch(req *http.Request) (*http.Response, error) {
if env.DebugHttpDump {
str, err := httputil.DumpRequest(req, true)
if err != nil {
return nil, err
}
fmt.Printf("===== REQUEST ===== %s\n", req.URL)
fmt.Println(string(str))
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if env.DebugHttpDump {
str, err := httputil.DumpResponse(resp, true)
if err != nil {
return nil, err
}
fmt.Printf("===== RESPONSE ===== %s\n", req.URL)
fmt.Println(string(str))
}
return resp, nil
}
func Query(in any) url.Values {
out := url.Values{}
if in == nil {
return out
}
ref := reflect.ValueOf(in)
if ref.Kind() == reflect.Pointer {
ref = ref.Elem()
}
if ref.Kind() != reflect.Struct {
return out
}
for i := 0; i < ref.NumField(); i++ {
field := ref.Type().Field(i)
value := ref.Field(i)
if field.Type.Kind() == reflect.Pointer {
if value.IsNil() {
continue
}
value = value.Elem()
}
name := field.Name
tags := strings.Split(field.Tag.Get("query"), ",")
if len(tags) > 0 && tags[0] != "" {
name = tags[0]
}
switch value := value.Interface().(type) {
case string:
out.Add(name, value)
case int:
out.Add(name, strconv.Itoa(value))
case bool:
if tags[1] == "b2i" {
out.Add(name, u.Ternary(value, "1", "0"))
} else {
out.Add(name, strconv.FormatBool(value))
}
default:
out.Add(name, fmt.Sprintf("%v", value))
}
}
return out
}
// 数据请求
type IdReq struct {
Id int32 `json:"id"`
}

View File

@@ -12,10 +12,10 @@ type IModel interface {
} }
type Model struct { type Model struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"` ID int32 `json:"id,omitzero" gorm:"column:id;primaryKey"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` CreatedAt time.Time `json:"created_at,omitzero" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"` UpdatedAt time.Time `json:"updated_at,omitzero" gorm:"column:updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at"` DeletedAt gorm.DeletedAt `json:"-" gorm:"column:deleted_at"`
} }
func (m *Model) GetID() int32 { func (m *Model) GetID() int32 {

8
web/core/product.go Normal file
View File

@@ -0,0 +1,8 @@
package core
type ProductCode string
var (
ProductShort ProductCode = "short"
ProductLong ProductCode = "long"
)

79
web/core/scopes.go Normal file
View File

@@ -0,0 +1,79 @@
package core
const (
ScopePermission = string("permission") // 权限
ScopePermissionRead = string("permission:read") // 读取权限列表
ScopePermissionWrite = string("permission:write") // 写入权限
ScopeAdminRole = string("admin_role") // 管理员角色
ScopeAdminRoleRead = string("admin_role:read") // 读取管理员角色列表
ScopeAdminRoleWrite = string("admin_role:write") // 写入管理员角色
ScopeAdmin = string("admin") // 管理员
ScopeAdminRead = string("admin:read") // 读取管理员列表
ScopeAdminWrite = string("admin:write") // 写入管理员
ScopeProduct = string("product") // 产品
ScopeProductRead = string("product:read") // 读取产品列表
ScopeProductWrite = string("product:write") // 写入产品
ScopeProductSku = string("product_sku") // 产品套餐
ScopeProductSkuRead = string("product_sku:read") // 读取产品套餐列表
ScopeProductSkuWrite = string("product_sku:write") // 写入产品套餐
ScopeProductSkuWriteStatus = string("product_sku:write:status") // 更改产品套餐状态
ScopeDiscount = string("discount") // 折扣
ScopeDiscountRead = string("discount:read") // 读取折扣列表
ScopeDiscountWrite = string("discount:write") // 写入折扣
ScopeResource = string("resource") // 用户套餐
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
ScopeResourceWrite = string("resource:write") // 写入用户套餐
ScopeResourceShort = string("resource:short") // 短效动态套餐
ScopeResourceShortRead = string("resource:short:read") // 读取用户短效动态套餐列表
ScopeResourceShortReadOfUser = string("resource:short:read:of_user") // 读取指定用户的短效动态套餐列表
ScopeResourceLong = string("resource:long") // 长效动态套餐
ScopeResourceLongRead = string("resource:long:read") // 读取用户长效动态套餐列表
ScopeResourceLongReadOfUser = string("resource:long:read:of_user") // 读取指定用户的长效动态套餐列表
ScopeUser = string("user") // 用户
ScopeUserRead = string("user:read") // 读取用户列表
ScopeUserReadOne = string("user:read:one") // 读取单个用户
ScopeUserReadNotBind = string("user:read:not_bind") // 读取未绑定管理员的用户列表
ScopeUserWrite = string("user:write") // 写入用户
ScopeUserWriteBalance = string("user:write:balance") // 写入用户余额
ScopeUserWriteBalanceInc = string("user:write:balance:inc") // 增加用户余额
ScopeUserWriteBalanceDec = string("user:write:balance:dec") // 减少用户余额
ScopeUserWriteBind = string("user:write:bind") // 用户认领
ScopeCoupon = string("coupon") // 优惠券
ScopeCouponRead = string("coupon:read") // 读取优惠券列表
ScopeCouponWrite = string("coupon:write") // 写入优惠券
ScopeBatch = string("batch") // 批次
ScopeBatchRead = string("batch:read") // 读取批次列表
ScopeBatchReadOfUser = string("batch:read:of_user") // 读取指定用户的批次列表
ScopeBatchWrite = string("batch:write") // 写入批次
ScopeChannel = string("channel") // IP
ScopeChannelRead = string("channel:read") // 读取 IP 列表
ScopeChannelReadOfUser = string("channel:read:of_user") // 读取指定用户的 IP 列表
ScopeChannelWrite = string("channel:write") // 写入 IP
ScopeTrade = string("trade") // 交易
ScopeTradeRead = string("trade:read") // 读取交易列表
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表
ScopeTradeWrite = string("trade:write") // 写入交易
ScopeTradeWriteComplete = string("trade:write:complete") // 完成交易
ScopeBill = string("bill") // 账单
ScopeBillRead = string("bill:read") // 读取账单列表
ScopeBillReadOfUser = string("bill:read:of_user") // 读取指定用户的账单列表
ScopeBillWrite = string("bill:write") // 写入账单
ScopeBalanceActivity = string("balance_activity") // 余额变动
ScopeBalanceActivityRead = string("balance_activity:read") // 读取余额变动列表
ScopeBalanceActivityReadOfUser = string("balance_activity:read:of_user") // 读取指定用户的余额变动列表
)

View File

@@ -1,11 +1,14 @@
package web package web
import ( import (
"encoding/json"
"errors" "errors"
"fmt"
"log/slog" "log/slog"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
"reflect" "reflect"
"time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -19,6 +22,9 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
var authErr auth.AuthErr var authErr auth.AuthErr
var bizErr *core.BizErr var bizErr *core.BizErr
var servErr *core.ServErr var servErr *core.ServErr
var timeErr *time.ParseError
var jsonErr *json.UnmarshalTypeError
var jsonSyntaxErr *json.SyntaxError
switch { switch {
@@ -48,11 +54,32 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
code = fiber.StatusInternalServerError code = fiber.StatusInternalServerError
message = err.Error() message = err.Error()
case errors.As(err, &timeErr):
code = fiber.StatusBadRequest
message = fmt.Sprintf("时间格式不正确,传入值为 %s检查传参是否为时间类型", timeErr.Value)
case errors.As(err, &jsonErr):
code = fiber.StatusBadRequest
message = fmt.Sprintf("参数 %s 类型不正确,传入类型为 %s正确类型应该为 %s", jsonErr.Field, jsonErr.Value, jsonErr.Type.Name())
case errors.As(err, &jsonSyntaxErr):
code = fiber.StatusBadRequest
message = "参数格式不正确,检查传参是否为 JSON 格式"
// 所有未手动声明的错误类型 // 所有未手动声明的错误类型
default: default:
slog.Warn("未处理的异常", slog.String("type", reflect.TypeOf(err).Name()), slog.String("error", err.Error())) t := reflect.TypeOf(err)
for {
if t.Kind() == reflect.Pointer {
t = t.Elem()
continue
}
break
}
slog.Warn("未处理的异常", slog.String("type", t.String()), slog.String("error", err.Error()))
} }
slog.Warn(message)
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(code).SendString(message) return c.Status(code).SendString(message)
} }

View File

@@ -1,24 +1,11 @@
package events package events
import ( import (
"encoding/json"
"log/slog"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
) )
const RemoveChannel = "channel:remove" const RemoveChannel = "channel:remove"
type RemoveChannelData struct { func NewRemoveChannel(batch string) *asynq.Task {
Batch string `json:"batch"` return asynq.NewTask(RemoveChannel, []byte(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
}
return asynq.NewTask(RemoveChannel, bytes)
} }

View File

@@ -9,18 +9,23 @@ import (
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
) )
const CompleteTrade = "trade:update" const CloseTrade = "trade:update"
type CompleteTradeData struct { type CloseTradeData struct {
UserId int32 `json:"user_id" validate:"required"`
TradeNo string `json:"trade_no" validate:"required"` TradeNo string `json:"trade_no" validate:"required"`
Method m.TradeMethod `json:"method" validate:"required"` Method m.TradeMethod `json:"method" validate:"required"`
} }
func NewCancelTrade(data CompleteTradeData) *asynq.Task { func NewCloseTradeTask(uid int32, tradeNo string, method m.TradeMethod) *asynq.Task {
bytes, err := json.Marshal(data) bytes, err := json.Marshal(CloseTradeData{
UserId: uid,
TradeNo: tradeNo,
Method: method,
})
if err != nil { if err != nil {
slog.Error("序列化更新交易任务失败", "error", err) slog.Error("序列化更新交易任务失败", "error", err)
return nil return nil
} }
return asynq.NewTask(CompleteTrade, bytes) return asynq.NewTask(CloseTrade, bytes)
} }

View File

@@ -10,6 +10,7 @@ import (
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"platform/pkg/env" "platform/pkg/env"
"platform/web/core"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -17,18 +18,12 @@ import (
// CloudClient 定义云服务接口 // CloudClient 定义云服务接口
type CloudClient interface { type CloudClient interface {
CloudEdges(param CloudEdgesReq) (*CloudEdgesResp, error) CloudEdges(param *CloudEdgesReq) (*CloudEdgesResp, error)
CloudConnect(param CloudConnectReq) error CloudConnect(param *CloudConnectReq) error
CloudDisconnect(param CloudDisconnectReq) (int, error) CloudDisconnect(param *CloudDisconnectReq) (int, error)
CloudAutoQuery() (CloudConnectResp, error) CloudAutoQuery() (CloudConnectResp, error)
} }
// GatewayClient 定义网关接口
type GatewayClient interface {
GatewayPortConfigs(params []PortConfigsReq) error
GatewayPortActive(param ...PortActiveReq) (map[string]PortData, error)
}
type cloud struct { type cloud struct {
url string url string
} }
@@ -37,59 +32,14 @@ var Cloud CloudClient
func initBaiyin() error { func initBaiyin() error {
Cloud = &cloud{ Cloud = &cloud{
url: env.BaiyinAddr, url: env.BaiyinCloudUrl,
} }
return nil return nil
} }
type AutoConfig struct { // cloud:/edges 筛选查询边缘节点
Province string `json:"province"` func (c *cloud) CloudEdges(param *CloudEdgesReq) (*CloudEdgesResp, error) {
City string `json:"city"` resp, err := c.requestCloud("GET", "/edges?"+core.Query(param).Encode(), "")
Isp string `json:"isp"`
Count int `json:"count"`
}
// region cloud:/edges
type CloudEdgesReq struct {
Province string
City string
Isp string
Offset int
Limit int
}
type CloudEdgesResp struct {
Edges []Edge `json:"edges"`
Total int `json:"total"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type Edge struct {
EdgesId int `json:"edges_id"`
Province string `json:"province"`
City string `json:"city"`
Isp string `json:"isp"`
Ip string `json:"ip"`
Rtt int `json:"rtt"`
PacketLoss int `json:"packet_loss"`
}
func (c *cloud) CloudEdges(param CloudEdgesReq) (*CloudEdgesResp, error) {
data := strings.Builder{}
data.WriteString("province=")
data.WriteString(param.Province)
data.WriteString("&city=")
data.WriteString(param.City)
data.WriteString("&isp=")
data.WriteString(param.Isp)
data.WriteString("&offset=")
data.WriteString(strconv.Itoa(param.Offset))
data.WriteString("&limit=")
data.WriteString(strconv.Itoa(param.Limit))
resp, err := c.requestCloud("GET", "/edges?"+data.String(), "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -115,17 +65,46 @@ func (c *cloud) CloudEdges(param CloudEdgesReq) (*CloudEdgesResp, error) {
return &result, nil return &result, nil
} }
// endregion type CloudEdgesReq struct {
Province *string `query:"province"`
// region cloud:/connect City *string `query:"city"`
Isp *string `query:"isp"`
type CloudConnectReq struct { Offset *int `query:"offset"`
Uuid string `json:"uuid"` Limit *int `query:"limit"`
Edge []string `json:"edge,omitempty"` NoRepeat *bool `query:"norepeat,b2i"`
AutoConfig []AutoConfig `json:"auto_config,omitempty"` NoDayRepeat *bool `query:"nodayrepeat,b2i"`
IpUnchangedTime *int `query:"ip_unchanged_time"` // 单位秒
ActiveTime *int `query:"active_time"` // 单位秒
// 排序方式,可选值:
// - create_time_asc 设备创建时间顺序
// - create_time_desc 设备创建时间倒序
// - ip_unchanged_time_asc ip持续没变化时间顺序
// - ip_unchanged_time_desc ip持续没变化时间倒序
// - active_time_asc 连续活跃时间顺序
// - active_time_desc 连续活跃时间倒序
// - rand 随机排序 (默认)
Sort *string `query:"sort"`
} }
func (c *cloud) CloudConnect(param CloudConnectReq) error { type CloudEdgesResp struct {
Edges []Edge `json:"edges"`
Total int `json:"total"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type Edge struct {
EdgeID string `json:"edge_id"`
Province string `json:"province"`
City string `json:"city"`
Isp string `json:"isp"`
Ip string `json:"ip"`
Rtt int `json:"rtt"`
PacketLoss int `json:"packet_loss"`
}
// cloud:/connect 连接边缘节点到网关
func (c *cloud) CloudConnect(param *CloudConnectReq) error {
data, err := json.Marshal(param) data, err := json.Marshal(param)
if err != nil { if err != nil {
return err return err
@@ -162,25 +141,21 @@ func (c *cloud) CloudConnect(param CloudConnectReq) error {
return nil return nil
} }
// endregion type CloudConnectReq struct {
Uuid string `json:"uuid"`
// region cloud:/disconnect Edge *[]string `json:"edge,omitempty"`
AutoConfig *[]AutoConfig `json:"auto_config,omitempty"`
type CloudDisconnectReq struct {
Uuid string `json:"uuid"`
Edge []string `json:"edge,omitempty"`
Config []Config `json:"auto_config,omitempty"`
} }
type Config struct { type AutoConfig struct {
Province string `json:"province"` Province string `json:"province"`
City string `json:"city"` City string `json:"city"`
Isp string `json:"isp"` Isp string `json:"isp"`
Count int `json:"count"` Count int `json:"count"`
Online bool `json:"online"`
} }
func (c *cloud) CloudDisconnect(param CloudDisconnectReq) (int, error) { // cloud:/disconnect 解除连接边缘节点到网关
func (c *cloud) CloudDisconnect(param *CloudDisconnectReq) (int, error) {
data, err := json.Marshal(param) data, err := json.Marshal(param)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -217,12 +192,21 @@ func (c *cloud) CloudDisconnect(param CloudDisconnectReq) (int, error) {
return int(result["disconnected_edges"].(float64)), nil return int(result["disconnected_edges"].(float64)), nil
} }
// endregion type CloudDisconnectReq struct {
Uuid string `json:"uuid"`
Edge *[]string `json:"edge,omitempty"`
Config *[]Config `json:"auto_config,omitempty"`
}
// region cloud:/auto_query type Config struct {
Province string `json:"province"`
type CloudConnectResp map[string][]AutoConfig City string `json:"city"`
Isp string `json:"isp"`
Count int `json:"count"`
Online bool `json:"online"`
}
// cloud:/auto_query 自动连接配置查询
func (c *cloud) CloudAutoQuery() (CloudConnectResp, error) { func (c *cloud) CloudAutoQuery() (CloudConnectResp, error) {
resp, err := c.requestCloud("GET", "/auto_query", "") resp, err := c.requestCloud("GET", "/auto_query", "")
if err != nil { if err != nil {
@@ -250,7 +234,7 @@ func (c *cloud) CloudAutoQuery() (CloudConnectResp, error) {
return result, nil return result, nil
} }
// endregion type CloudConnectResp map[string][]AutoConfig
func (c *cloud) requestCloud(method string, url string, data string) (*http.Response, error) { func (c *cloud) requestCloud(method string, url string, data string) (*http.Response, error) {
@@ -263,7 +247,7 @@ func (c *cloud) requestCloud(method string, url string, data string) (*http.Resp
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
var resp *http.Response var resp *http.Response
for i := 0; i < 2; i++ { for i := range 2 {
token, err := c.token(i == 1) token, err := c.token(i == 1)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -304,7 +288,7 @@ func (c *cloud) requestCloud(method string, url string, data string) (*http.Resp
func (c *cloud) token(refresh bool) (string, error) { func (c *cloud) token(refresh bool) (string, error) {
// redis 获取令牌 // redis 获取令牌
if !refresh { if !refresh {
token, err := Redis.Get(context.Background(), "remote:token").Result() token, err := Redis.Get(context.Background(), BaiyinToken).Result()
if err == nil && token != "" { if err == nil && token != "" {
return token, nil return token, nil
} }
@@ -338,7 +322,7 @@ func (c *cloud) token(refresh bool) (string, error) {
var result map[string]any var result map[string]any
err = json.Unmarshal(body, &result) err = json.Unmarshal(body, &result)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("解析响应 [%s] 失败: %w", string(body), err)
} }
if result["code"].(float64) != 1 { if result["code"].(float64) != 1 {
@@ -347,7 +331,7 @@ func (c *cloud) token(refresh bool) (string, error) {
// redis 设置令牌 // redis 设置令牌
token := result["token"].(string) token := result["token"].(string)
err = Redis.Set(context.Background(), "remote:token", token, 1*time.Hour).Err() err = Redis.Set(context.Background(), BaiyinToken, token, 1*time.Hour).Err()
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -355,6 +339,15 @@ func (c *cloud) token(refresh bool) (string, error) {
return token, nil return token, nil
} }
const BaiyinToken = "clients:baiyin:token"
// GatewayClient 定义网关接口
type GatewayClient interface {
GatewayPortConfigs(params []*PortConfigsReq) error
GatewayPortActive(param ...*PortActiveReq) (map[string]PortData, error)
GatewayEdge(params *GatewayEdgeReq) (map[string]GatewayEdgeInfo, error)
}
type gateway struct { type gateway struct {
url string url string
username string username string
@@ -373,6 +366,68 @@ func NewGateway(url, username, password string) GatewayClient {
return GatewayInitializer(url, username, password) return GatewayInitializer(url, username, password)
} }
type GatewayEdgeReq struct {
EdgeID *string `query:"edge_id"`
Province *string `query:"province"`
City *string `query:"city"`
Isp *string `query:"isp"`
Connected *bool `query:"connected"`
Assigned *bool `query:"assigned"`
GetRand *int `query:"getRand"`
IpUnchangedTimeStart *int `query:"ip_unchanged_time_start"`
IpUnchangedTimeEnd *int `query:"ip_unchanged_time_end"`
OnlineTimeStart *int `query:"online_time_start"`
OnlineTimeEnd *int `query:"online_time_end"`
Rtt *int `query:"rtt"`
MinRtt *int `query:"min_rtt"`
RttBaidu *int `query:"rtt_baidu"`
PacketLoss *int `query:"packet_loss"`
PacketLossBaidu *int `query:"packet_loss_baidu"`
IP *string `query:"ip"`
Limit *int `query:"limit"`
Offset *int `query:"offset"`
}
type GatewayEdgeResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data map[string]GatewayEdgeInfo `json:"data"`
Total int `json:"total"`
}
type GatewayEdgeInfo struct {
IP string `json:"ip"`
Connected bool `json:"connected"`
Assigned bool `json:"assigned"`
AssignedTo string `json:"assignedto"`
PacketLoss int `json:"packet_loss"`
PacketLossBaidu int `json:"packet_loss_baidu"`
Rtt int `json:"rtt"`
RttBaidu int `json:"rtt_baidu"`
OfflineTime int `json:"offline_time"`
OnlineTime int `json:"online_time"`
IpUnchangedTime int `json:"ip_unchanged_time"`
}
func (c *gateway) GatewayEdge(req *GatewayEdgeReq) (map[string]GatewayEdgeInfo, error) {
resp, err := c.get("/edge", core.Query(req))
if err != nil {
return nil, fmt.Errorf("查询可用节点失败:%w", err)
}
defer resp.Body.Close()
body := new(GatewayEdgeResp)
if err = json.NewDecoder(resp.Body).Decode(body); err != nil {
return nil, fmt.Errorf("解析响应内容失败:%w", err)
}
if body.Code != 0 {
return nil, fmt.Errorf("接口业务响应异常: %d %s", body.Code, body.Msg)
}
return body.Data, nil
}
// region gateway:/port/configs // region gateway:/port/configs
type PortConfigsReq struct { type PortConfigsReq struct {
@@ -395,7 +450,7 @@ type AutoEdgeConfig struct {
PacketLoss int `json:"packet_loss,omitempty"` PacketLoss int `json:"packet_loss,omitempty"`
} }
func (c *gateway) GatewayPortConfigs(params []PortConfigsReq) error { func (c *gateway) GatewayPortConfigs(params []*PortConfigsReq) error {
if len(params) == 0 { if len(params) == 0 {
return errors.New("params is empty") return errors.New("params is empty")
} }
@@ -461,10 +516,10 @@ type PortData struct {
Userpass string `json:"userpass"` Userpass string `json:"userpass"`
} }
func (c *gateway) GatewayPortActive(param ...PortActiveReq) (map[string]PortData, error) { func (c *gateway) GatewayPortActive(param ...*PortActiveReq) (map[string]PortData, error) {
_param := PortActiveReq{} _param := PortActiveReq{}
if len(param) != 0 { if len(param) != 0 && param[0] != nil {
_param = param[0] _param = *param[0]
} }
path := strings.Builder{} path := strings.Builder{}
@@ -520,38 +575,33 @@ func (c *gateway) GatewayPortActive(param ...PortActiveReq) (map[string]PortData
// endregion // endregion
func (c *gateway) get(url string, params url.Values) (*http.Response, error) {
url = fmt.Sprintf("http://%s:%s@%s:9990%s?%s", c.username, c.password, c.url, url, params.Encode())
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败:%w", err)
}
res, err := core.Fetch(req)
if err != nil {
return nil, fmt.Errorf("获取数据失败:%w", err)
}
if res.StatusCode != http.StatusOK {
bytes, _ := io.ReadAll(res.Body)
return nil, fmt.Errorf("接口响应异常: %d %s", res.StatusCode, string(bytes))
}
return res, nil
}
func (c *gateway) requestGateway(method string, url string, data string) (*http.Response, error) { func (c *gateway) requestGateway(method string, url string, data string) (*http.Response, error) {
//goland:noinspection ALL
url = fmt.Sprintf("http://%s:%s@%s:9990%s", c.username, c.password, c.url, url) url = fmt.Sprintf("http://%s:%s@%s:9990%s", c.username, c.password, c.url, url)
req, err := http.NewRequest(method, url, strings.NewReader(data)) req, err := http.NewRequest(method, url, strings.NewReader(data))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
if env.DebugHttpDump { return core.Fetch(req)
str, err := httputil.DumpRequest(req, true)
if err != nil {
return nil, err
}
fmt.Println("==============================")
fmt.Println(string(str))
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if env.DebugHttpDump {
str, err := httputil.DumpResponse(resp, true)
if err != nil {
return nil, err
}
fmt.Println("------------------------------")
fmt.Println(string(str))
}
return resp, nil
} }

30
web/globals/orm/timez.go Normal file
View File

@@ -0,0 +1,30 @@
package orm
import (
"fmt"
"time"
)
type DateTime struct {
time.Time
}
func (dt *DateTime) Scan(value any) error {
switch v := value.(type) {
case time.Time:
dt.Time = v
case string:
t, err := time.Parse(time.RFC3339, v)
if err != nil {
return err
}
dt.Time = t
default:
return fmt.Errorf("unsupported type: %T", value)
}
return nil
}
func (dt DateTime) Value() (any, error) {
return dt.Time.Format(time.RFC3339), nil
}

View File

@@ -3,6 +3,7 @@ package globals
import ( import (
"context" "context"
"fmt" "fmt"
"platform/pkg/env"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
@@ -16,8 +17,13 @@ import (
var tp *trace.TracerProvider var tp *trace.TracerProvider
func initOtel(ctx context.Context) error { func initOtel(ctx context.Context) error {
if env.OtelHost == "" || env.OtelPort == "" {
return nil
}
addr := env.OtelHost + ":" + env.OtelPort
exporter, err := otlptracegrpc.New(ctx, exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("localhost:4317"), otlptracegrpc.WithEndpoint(addr),
otlptracegrpc.WithInsecure(), otlptracegrpc.WithInsecure(),
) )
if err != nil { if err != nil {

View File

@@ -10,6 +10,7 @@ import (
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify" "github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"github.com/wechatpay-apiv3/wechatpay-go/core/option" "github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/partnerpayments/h5"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
"github.com/wechatpay-apiv3/wechatpay-go/utils" "github.com/wechatpay-apiv3/wechatpay-go/utils"
) )
@@ -18,6 +19,7 @@ var WechatPay *WechatPayClient
type WechatPayClient struct { type WechatPayClient struct {
Native *native.NativeApiService Native *native.NativeApiService
H5 *h5.H5ApiService
Notify *notify.Handler Notify *notify.Handler
} }
@@ -71,6 +73,7 @@ func initWechatPay() error {
// 创建 WechatPay 服务 // 创建 WechatPay 服务
WechatPay = &WechatPayClient{ WechatPay = &WechatPayClient{
Native: &native.NativeApiService{Client: client}, Native: &native.NativeApiService{Client: client},
H5: &h5.H5ApiService{Client: client},
Notify: handler, Notify: handler,
} }
return nil return nil

106
web/handlers/admin.go Normal file
View File

@@ -0,0 +1,106 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func PageAdminByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRead)
if err != nil {
return err
}
var req PageAdminsReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.Admin.Page(req.PageReq)
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageAdminsReq struct {
core.PageReq
}
func AllAdminByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRead)
if err != nil {
return err
}
list, err := s.Admin.All()
if err != nil {
return err
}
return c.JSON(list)
}
func CreateAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminWrite)
if err != nil {
return err
}
var req s.CreateAdmin
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.Admin.Create(&req); err != nil {
return err
}
return c.JSON(nil)
}
func UpdateAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminWrite)
if err != nil {
return err
}
var req s.UpdateAdmin
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.Admin.Update(&req); err != nil {
return err
}
return c.JSON(nil)
}
func RemoveAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.Admin.Remove(req.Id); err != nil {
return err
}
return c.JSON(nil)
}

103
web/handlers/admin_role.go Normal file
View File

@@ -0,0 +1,103 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func AllAdminRoleByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRoleRead)
if err != nil {
return err
}
list, err := s.AdminRole.ListRoles()
if err != nil {
return err
}
return c.JSON(list)
}
func PageAdminRoleByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRoleRead)
if err != nil {
return err
}
var req PageAdminRolesReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.AdminRole.PageRoles(req.PageReq)
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageAdminRolesReq struct {
core.PageReq
}
func CreateAdminRole(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRoleWrite)
if err != nil {
return err
}
var req s.CreateAdminRole
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.AdminRole.CreateAdminRole(&req); err != nil {
return err
}
return c.JSON(nil)
}
func UpdateAdminRole(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRoleWrite)
if err != nil {
return err
}
var req s.UpdateAdminRole
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.AdminRole.UpdateAdminRole(&req); err != nil {
return err
}
return c.JSON(nil)
}
func RemoveAdminRole(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRoleWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.AdminRole.RemoveAdminRole(req.Id); err != nil {
return err
}
return c.JSON(nil)
}

View File

@@ -1,97 +0,0 @@
package handlers
import (
"platform/pkg/u"
auth2 "platform/web/auth"
m "platform/web/models"
q "platform/web/queries"
"github.com/gofiber/fiber/v2"
)
// region /revoke
type RevokeReq struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
func Revoke(c *fiber.Ctx) error {
_, err := auth2.GetAuthCtx(c).PermitUser()
if err != nil {
// 用户未登录
return nil
}
// 解析请求参数
req := new(RevokeReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 删除会话
err = auth2.RemoveSession(c.Context(), req.AccessToken, req.RefreshToken)
if err != nil {
return err
}
return nil
}
// endregion
// region /profile
type IntrospectResp struct {
m.User
HasPassword bool `json:"has_password"` // 是否设置了密码
}
func Introspect(c *fiber.Ctx) error {
// 验证权限
authCtx, err := auth2.GetAuthCtx(c).PermitUser()
if err != nil {
return err
}
// 获取用户信息
profile, err := q.User.
Where(q.User.ID.Eq(authCtx.User.ID)).
Omit(q.User.DeletedAt).
Take()
if err != nil {
return err
}
// 检查用户是否设置了密码
hasPassword := false
if profile.Password != nil && *profile.Password != "" {
hasPassword = true
profile.Password = nil // 不返回密码
}
// 掩码敏感信息
if profile.Phone != "" {
profile.Phone = maskPhone(profile.Phone)
}
if profile.IDNo != nil && *profile.IDNo != "" {
profile.IDNo = u.P(maskIdNo(*profile.IDNo))
}
return c.JSON(IntrospectResp{*profile, hasPassword})
}
func maskPhone(phone string) string {
if len(phone) < 11 {
return phone
}
return phone[:3] + "****" + phone[7:]
}
func maskIdNo(idNo string) string {
if len(idNo) < 18 {
return idNo
}
return idNo[:3] + "*********" + idNo[14:]
}
// endregion

View File

@@ -0,0 +1,137 @@
package handlers
import (
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
q "platform/web/queries"
"time"
"github.com/gofiber/fiber/v2"
)
// PageBalanceActivityByAdmin 分页查询所有余额变动记录
func PageBalanceActivityByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBalanceActivityRead)
if err != nil {
return err
}
// 解析请求参数
req := new(PageBalanceActivityByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构造查询条件
do := q.BalanceActivity.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.BillNo != nil {
do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.BalanceActivity.CreatedAt.Lte(t))
}
// 查询余额变动列表
list, total, err := q.BalanceActivity.Debug().
Joins(q.BalanceActivity.User, q.BalanceActivity.Admin, q.BalanceActivity.Bill).
Select(
q.BalanceActivity.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.Admin.As("Admin").Name.As("Admin__name"),
q.Bill.As("Bill").BillNo.As("Bill__bill_no"),
).
Where(do).
Order(q.BalanceActivity.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return core.NewBizErr("获取数据失败", err)
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBalanceActivityByAdminReq struct {
core.PageReq
UserPhone *string `json:"user_phone,omitempty"`
BillNo *string `json:"bill_no,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
}
// PageBalanceActivityOfUserByAdmin 分页查询指定用户的余额变动记录
func PageBalanceActivityOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBalanceActivityReadOfUser)
if err != nil {
return err
}
// 解析请求参数
req := new(PageBalanceActivityOfUserByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构造查询条件
do := q.BalanceActivity.Where(q.BalanceActivity.UserID.Eq(req.UserID))
if req.BillNo != nil {
do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.BalanceActivity.CreatedAt.Lte(t))
}
// 查询余额变动列表
list, total, err := q.BalanceActivity.
Joins(q.BalanceActivity.Admin, q.BalanceActivity.Bill).
Select(
q.BalanceActivity.ALL,
q.Admin.As("Admin").Name.As("Admin__name"),
q.Bill.As("Bill").BillNo.As("Bill__bill_no"),
).
Where(do).
Order(q.BalanceActivity.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return core.NewBizErr("获取数据失败", err)
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBalanceActivityOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
BillNo *string `json:"bill_no,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
}

202
web/handlers/batch.go Normal file
View File

@@ -0,0 +1,202 @@
package handlers
import (
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
c "platform/web/core"
g "platform/web/globals"
q "platform/web/queries"
"time"
"github.com/gofiber/fiber/v2"
)
// PageBatch 分页查询套餐提取记录
func PageBatch(ctx *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(ctx).PermitUser()
if err != nil {
return err
}
// 解析请求参数
req := new(PageResourceBatchReq)
if err := g.Validator.ParseBody(ctx, req); err != nil {
return err
}
// 查询批次
conds := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(authCtx.User.ID))
if req.TimeStart != nil {
conds.Where(q.LogsUserUsage.Time.Gte(*req.TimeStart))
}
if req.TimeEnd != nil {
conds.Where(q.LogsUserUsage.Time.Lte(*req.TimeEnd))
}
list, total, err := q.LogsUserUsage.Where(conds).
Order(q.LogsUserUsage.Time.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return core.NewBizErr("获取数据失败", err)
}
// 返回数据
return ctx.JSON(c.PageResp{
Total: int(total),
List: list,
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceBatchReq struct {
c.PageReq
TimeStart *time.Time `json:"time_start"`
TimeEnd *time.Time `json:"time_end"`
}
// PageBatchByAdmin 分页查询所有提取记录
func PageBatchByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBatchRead)
if err != nil {
return err
}
var req PageBatchByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.LogsUserUsage.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
}
if req.Prov != nil {
do = do.Where(q.LogsUserUsage.Prov.Eq(*req.Prov))
}
if req.City != nil {
do = do.Where(q.LogsUserUsage.City.Eq(*req.City))
}
if req.Isp != nil {
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.LogsUserUsage.Time.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.LogsUserUsage.Time.Lte(time))
}
list, total, err := q.LogsUserUsage.
Joins(q.LogsUserUsage.User, q.LogsUserUsage.Resource).
Select(
q.LogsUserUsage.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.LogsUserUsage.Time.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBatchByAdminReq struct {
c.PageReq
UserPhone *string `json:"user_phone"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *string `json:"isp"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// PageBatchOfUserByAdmin 分页查询指定用户的提取记录
func PageBatchOfUserByAdmin(ctx *fiber.Ctx) error {
_, err := auth.GetAuthCtx(ctx).PermitAdmin(core.ScopeBatchReadOfUser)
if err != nil {
return err
}
var req PageBatchOfUserByAdminReq
if err = g.Validator.ParseBody(ctx, &req); err != nil {
return err
}
do := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
}
if req.Prov != nil {
do = do.Where(q.LogsUserUsage.Prov.Eq(*req.Prov))
}
if req.City != nil {
do = do.Where(q.LogsUserUsage.City.Eq(*req.City))
}
if req.Isp != nil {
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.LogsUserUsage.Time.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.LogsUserUsage.Time.Lte(t))
}
list, total, err := q.LogsUserUsage.
Joins(q.LogsUserUsage.User, q.LogsUserUsage.Resource).
Select(
q.LogsUserUsage.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.LogsUserUsage.Time.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return ctx.JSON(core.NewBizErr("获取数据失败", err))
}
return ctx.JSON(c.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBatchOfUserByAdminReq struct {
c.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *string `json:"isp"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}

View File

@@ -1,22 +1,188 @@
package handlers package handlers
import ( import (
"platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
g "platform/web/globals"
q "platform/web/queries" q "platform/web/queries"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
// region ListBill // PageBillByAdmin 分页查询全部账单
func PageBillByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBillRead)
if err != nil {
return err
}
type ListBillReq struct { // 解析请求参数
req := new(PageBillByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构造查询条件
do := q.Bill.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.TradeInnerNo != nil {
do = do.Where(q.Trade.As("Trade").InnerNo.Eq(*req.TradeInnerNo))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BillNo != nil {
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Bill.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateHead(*req.CreatedAtEnd)
do = do.Where(q.Bill.CreatedAt.Lte(time))
}
if req.ProductCode != nil {
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
}
if req.SkuCode != nil {
do = do.Where(q.Bill.
Where(q.ResourceShort.As("Resource__Short").Code.Eq(*req.SkuCode)).
Or(q.ResourceLong.As("Resource__Long").Code.Eq(*req.SkuCode)))
}
// 查询用户列表
list, total, err := q.Bill.
Joins(
q.Bill.User,
q.Bill.Resource,
q.Bill.Trade,
q.Bill.Resource.Short,
q.Bill.Resource.Long,
).
Select(
q.Bill.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.Bill.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBillByAdminReq struct {
core.PageReq core.PageReq
BillNo *string `json:"bill_no"` UserPhone *string `json:"user_phone,omitempty"`
Type *int `json:"type"` TradeInnerNo *string `json:"trade_inner_no,omitempty"`
CreateAfter *time.Time `json:"create_after"` ResourceNo *string `json:"resource_no,omitempty"`
CreateBefore *time.Time `json:"create_before"` BillNo *string `json:"bill_no,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
ProductCode *string `json:"product_code,omitempty"`
SkuCode *string `json:"sku_code,omitempty"`
}
// PageBillOfUserByAdmin 分页查询指定用户账单
func PageBillOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBillReadOfUser)
if err != nil {
return err
}
// 解析请求参数
req := new(PageBillOfUserByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构造查询条件
do := q.Bill.Where(q.Bill.UserID.Eq(req.UserID))
if req.TradeInnerNo != nil {
do = do.Where(q.Trade.As("Trade").InnerNo.Eq(*req.TradeInnerNo))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BillNo != nil {
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Bill.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateHead(*req.CreatedAtEnd)
do = do.Where(q.Bill.CreatedAt.Lte(time))
}
if req.ProductCode != nil {
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
}
if req.SkuCode != nil {
do = do.Where(q.Bill.
Where(q.ResourceShort.As("Resource__Short").Code.Eq(*req.SkuCode)).
Or(q.ResourceLong.As("Resource__Long").Code.Eq(*req.SkuCode)))
}
// 查询账单列表
list, total, err := q.Bill.
Joins(
q.Bill.Resource,
q.Bill.Trade,
q.Bill.Resource.Short,
q.Bill.Resource.Long,
).
Select(
q.Bill.ALL,
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.Bill.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBillOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
TradeInnerNo *string `json:"trade_inner_no,omitempty"`
ResourceNo *string `json:"resource_no,omitempty"`
BillNo *string `json:"bill_no,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
ProductCode *string `json:"product_code,omitempty"`
SkuCode *string `json:"sku_code,omitempty"`
} }
// ListBill 获取账单列表 // ListBill 获取账单列表
@@ -79,4 +245,10 @@ func ListBill(c *fiber.Ctx) error {
}) })
} }
// endregion type ListBillReq struct {
core.PageReq
BillNo *string `json:"bill_no"`
Type *int `json:"type"`
CreateAfter *time.Time `json:"create_after"`
CreateBefore *time.Time `json:"create_before"`
}

View File

@@ -6,6 +6,7 @@ import (
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
g "platform/web/globals" g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
@@ -14,16 +15,92 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
// region ListChannels // PageChannelByAdmin 分页查询所有通道
func PageChannelByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelRead)
if err != nil {
return err
}
type ListChannelsReq struct { // 解析请求参数
core.PageReq var req PageChannelsByAdminReq
AuthType s.ChannelAuthType `json:"auth_type"` if err := g.Validator.ParseBody(c, &req); err != nil {
ExpireAfter *time.Time `json:"expire_after"` return err
ExpireBefore *time.Time `json:"expire_before"` }
// 构建查询条件
do := q.Channel.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.Channel.BatchNo.Eq(*req.BatchNo))
}
if req.ProxyHost != nil {
do = do.Where(q.Channel.Host.Eq(*req.ProxyHost))
}
if req.ProxyPort != nil {
do = do.Where(q.Channel.Port.Eq(*req.ProxyPort))
}
if req.NodeIP != nil {
ip, err := orm.ParseInet(*req.NodeIP)
if err != nil {
return core.NewBizErr("查询参数 ip 格式不正确")
}
do = do.Where(q.Channel.IP.Eq(ip))
}
if req.ExpiredAtStart != nil {
time := u.DateHead(*req.ExpiredAtStart)
do = do.Where(q.Channel.ExpiredAt.Gte(time))
}
if req.ExpiredAtEnd != nil {
time := u.DateHead(*req.ExpiredAtEnd)
do = do.Where(q.Channel.ExpiredAt.Lte(time))
}
// 查询通道列表
list, total, err := q.Channel.
Joins(q.Channel.User, q.Channel.Resource).
Select(
q.Channel.ALL,
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Channel.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
} }
func ListChannels(c *fiber.Ctx) error { type PageChannelsByAdminReq struct {
core.PageReq
UserPhone *string `json:"user_phone"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
ProxyHost *string `json:"proxy_host"`
ProxyPort *uint16 `json:"proxy_port"`
NodeIP *string `json:"node_ip" validator:"omitempty,ip"`
ExpiredAtStart *time.Time `json:"expired_at_start"`
ExpiredAtEnd *time.Time `json:"expired_at_end"`
}
// ListChannel 分页查询当前用户通道
func ListChannel(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authContext, err := auth.GetAuthCtx(c).PermitUser() authContext, err := auth.GetAuthCtx(c).PermitUser()
if err != nil { if err != nil {
@@ -57,7 +134,6 @@ func ListChannels(c *fiber.Ctx) error {
// 查询数据 // 查询数据
channels, err := q.Channel. channels, err := q.Channel.
Preload(q.Channel.Proxy).
Where(cond). Where(cond).
Order(q.Channel.CreatedAt.Desc()). Order(q.Channel.CreatedAt.Desc()).
Offset(req.GetOffset()). Offset(req.GetOffset()).
@@ -87,9 +163,69 @@ func ListChannels(c *fiber.Ctx) error {
}) })
} }
// endregion type ListChannelsReq struct {
core.PageReq
AuthType s.ChannelAuthType `json:"auth_type"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
}
// region CreateChannel // CreateChannel 创建新通道
func CreateChannel(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitUser()
if err != nil {
return err
}
// 解析参数
req := new(CreateChannelReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return core.NewBizErr("解析参数失败", err)
}
ip, err := netip.ParseAddr(c.IP())
if err != nil {
return core.NewBizErr("获取客户端地址失败", err)
}
// 创建通道
var isp *m.EdgeISP
if req.Isp != nil {
isp = u.X(m.ToEdgeISP(*req.Isp))
}
result, err := s.Channel.CreateChannels(
ip,
req.ResourceId,
req.AuthType == s.ChannelAuthTypeIp,
req.AuthType == s.ChannelAuthTypePass,
req.Count,
s.EdgeFilter{
Isp: isp,
Prov: req.Prov,
City: req.City,
},
)
if err != nil {
return err
}
// 返回结果
var resp = make([]*CreateChannelRespItem, len(result))
for i, channel := range result {
resp[i] = &CreateChannelRespItem{
Proto: req.Protocol,
Host: channel.Host,
Port: channel.Port,
}
if req.AuthType == s.ChannelAuthTypePass {
resp[i].Username = channel.Username
resp[i].Password = channel.Password
}
}
return c.JSON(resp)
}
type CreateChannelReq struct { type CreateChannelReq struct {
ResourceId int32 `json:"resource_id" validate:"required"` ResourceId int32 `json:"resource_id" validate:"required"`
@@ -109,63 +245,7 @@ type CreateChannelRespItem struct {
Password *string `json:"password,omitempty"` Password *string `json:"password,omitempty"`
} }
func CreateChannel(c *fiber.Ctx) error { // RemoveChannels 删除通道
// 解析参数
req := new(CreateChannelReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return core.NewBizErr("解析参数失败", err)
}
ip, err := netip.ParseAddr(c.IP())
if err != nil {
return core.NewBizErr("获取客户端地址失败", err)
}
// 创建通道
result, err := s.Channel.CreateChannels(
ip,
req.ResourceId,
req.AuthType == s.ChannelAuthTypeIp,
req.AuthType == s.ChannelAuthTypePass,
req.Count,
s.EdgeFilter{
Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
Prov: req.Prov,
City: req.City,
},
)
if err != nil {
return err
}
// 返回结果
var resp = make([]*CreateChannelRespItem, len(result))
for i, channel := range result {
resp[i] = &CreateChannelRespItem{
Proto: req.Protocol,
Host: channel.Proxy.IP.String(),
Port: channel.Port,
}
if req.AuthType == s.ChannelAuthTypePass {
resp[i].Username = channel.Username
resp[i].Password = channel.Password
}
}
return c.JSON(resp)
}
type CreateChannelResultType string
// endregion
// region RemoveChannels
type RemoveChannelsReq struct {
Batch string `json:"batch" validate:"required"`
Ids []int32 `json:"ids" validate:"required"`
}
func RemoveChannels(c *fiber.Ctx) error { func RemoveChannels(c *fiber.Ctx) error {
// 检查权限 // 检查权限
_, err := auth.GetAuthCtx(c).PermitOfficialClient() _, err := auth.GetAuthCtx(c).PermitOfficialClient()
@@ -180,7 +260,7 @@ func RemoveChannels(c *fiber.Ctx) error {
} }
// 删除通道 // 删除通道
err = s.Channel.RemoveChannels(req.Batch, req.Ids) err = s.Channel.RemoveChannels(req.Batch)
if err != nil { if err != nil {
return err return err
} }
@@ -188,4 +268,79 @@ func RemoveChannels(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK) return c.SendStatus(fiber.StatusOK)
} }
// endregion type RemoveChannelsReq struct {
Batch string `json:"batch" validate:"required"`
}
// PageChannelOfUserByAdmin 分页查询指定用户的通道
func PageChannelOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelReadOfUser)
if err != nil {
return err
}
// 解析请求参数
var req PageChannelOfUserByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 构建查询条件
do := q.Channel.Where(q.Channel.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.Channel.BatchNo.Eq(*req.BatchNo))
}
if req.ProxyHost != nil {
do = do.Where(q.Channel.Host.Eq(*req.ProxyHost))
}
if req.ProxyPort != nil {
do = do.Where(q.Channel.Port.Eq(*req.ProxyPort))
}
if req.ExpiredAtStart != nil {
t := u.DateHead(*req.ExpiredAtStart)
do = do.Where(q.Channel.ExpiredAt.Gte(t))
}
if req.ExpiredAtEnd != nil {
t := u.DateHead(*req.ExpiredAtEnd)
do = do.Where(q.Channel.ExpiredAt.Lte(t))
}
// 查询通道列表
list, total, err := q.Channel.
Joins(q.Channel.User, q.Channel.Resource).
Select(
q.Channel.ALL,
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Channel.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageChannelOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
ProxyHost *string `json:"proxy_host"`
ProxyPort *uint16 `json:"proxy_port"`
ExpiredAtStart *time.Time `json:"expired_at_start"`
ExpiredAtEnd *time.Time `json:"expired_at_end"`
}

105
web/handlers/coupon.go Normal file
View File

@@ -0,0 +1,105 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func PageCouponByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponRead)
if err != nil {
return err
}
var req core.PageReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.Coupon.Page(&req)
if err != nil {
return err
}
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: list,
})
}
func AllCouponByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponRead)
if err != nil {
return err
}
list, err := s.Coupon.All()
if err != nil {
return err
}
return c.JSON(list)
}
func CreateCoupon(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponWrite)
if err != nil {
return err
}
var req s.CreateCouponData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Coupon.Create(req)
if err != nil {
return err
}
return nil
}
func UpdateCoupon(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponWrite)
if err != nil {
return err
}
var req s.UpdateCouponData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Coupon.Update(req)
if err != nil {
return err
}
return nil
}
func DeleteCoupon(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Coupon.Delete(req.Id)
if err != nil {
return err
}
return nil
}

View File

@@ -18,19 +18,7 @@ import (
jdclient "github.com/jdcloud-api/jdcloud-sdk-go/services/cloudauth/client" jdclient "github.com/jdcloud-api/jdcloud-sdk-go/services/cloudauth/client"
) )
// region Identify // Identify 发起实名认证
type IdentifyReq struct {
Type int `json:"type" validate:"required,oneof=1 2"`
Name string `json:"name" validate:"required"`
IdenNo string `json:"iden_no" validate:"required"`
}
type IdentifyRes struct {
Identified bool `json:"identified"`
Target string `json:"target"`
}
func Identify(c *fiber.Ctx) error { func Identify(c *fiber.Ctx) error {
// 检查权限 // 检查权限
@@ -99,7 +87,21 @@ func Identify(c *fiber.Ctx) error {
}) })
} }
// endregion type IdentifyReq struct {
Type int `json:"type" validate:"required,oneof=1 2"`
Name string `json:"name" validate:"required"`
IdenNo string `json:"iden_no" validate:"required"`
}
type IdentifyRes struct {
Identified bool `json:"identified"`
Target string `json:"target"`
}
type idenResultData struct {
Success bool
Message string
}
// IdentifyCallbackNew 更新用户实名认证状态 // IdentifyCallbackNew 更新用户实名认证状态
func IdentifyCallbackNew(c *fiber.Ctx) error { func IdentifyCallbackNew(c *fiber.Ctx) error {
@@ -110,18 +112,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
Success bool `json:"success" validate:"required"` Success bool `json:"success" validate:"required"`
}) })
if err := c.QueryParser(req); err != nil { if err := c.QueryParser(req); err != nil {
return core.NewBizErr("解析请求参数失败", err) return renderIdenResult(c, false, "解析请求参数失败")
} }
// 获取 token // 获取 token
infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes() infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes()
if err != nil { if err != nil {
return core.NewBizErr("实名认证状态已失效", err) return renderIdenResult(c, false, "实名认证状态已失效,请重新发起认证")
} }
info := idenInfo{} info := idenInfo{}
err = json.Unmarshal(infoStr, &info) if err = json.Unmarshal(infoStr, &info); err != nil {
if err != nil { return renderIdenResult(c, false, "解析实名认证信息失败,请重新发起认证")
return core.NewServErr("解析实名认证信息失败", err)
} }
// 获取认证结果 // 获取认证结果
@@ -131,17 +132,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
info.Token, info.Token,
)) ))
if err != nil { if err != nil {
return core.NewServErr("获取实名认证结果失败", err) return renderIdenResult(c, false, "获取实名认证结果失败,请重新发起认证")
} }
if resp.Error.Code != 0 { if resp.Error.Code != 0 {
return core.NewServErr(fmt.Sprintf("获取实名认证结果失败: %s", resp.Error.Message)) return renderIdenResult(c, false, fmt.Sprintf("获取实名认证结果失败%s", resp.Error.Message))
} }
if resp.Result.H5Result != "ok" || resp.Result.SmResult != "ok" || resp.Result.RxResult != "ok" { if resp.Result.H5Result != "ok" || resp.Result.SmResult != "ok" || resp.Result.RxResult != "ok" {
return core.NewBizErr(fmt.Sprintf("实名认证失败: %s", resp.Result.Desc)) return renderIdenResult(c, false, fmt.Sprintf("实名认证未通过:%s", resp.Result.Desc))
} }
// 更新用户实名认证状态 // 更新用户实名认证状态
_, err = q.User.Debug(). _, err = q.User.
Where(q.User.ID.Eq(info.Uid)). Where(q.User.ID.Eq(info.Uid)).
UpdateSimple( UpdateSimple(
q.User.IDType.Value(info.Type), q.User.IDType.Value(info.Type),
@@ -150,11 +151,41 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
q.User.IDToken.Value(info.Token), q.User.IDToken.Value(info.Token),
) )
if err != nil { if err != nil {
return core.NewServErr("更新用户实名信息失败", err) return renderIdenResult(c, false, "保存实名认证信息失败,请联系客服处理")
} }
// 返回结果页面 // 返回结果页面
return c.SendString("🎉认证成功!现在可以安全关闭这个页面") return renderIdenResult(c, true, "实名认证成功,请在扫码页面点击按钮完成认证")
}
func renderIdenResult(c *fiber.Ctx, success bool, message string) error {
return c.Render("views/iden-result", idenResultData{
Success: success,
Message: message,
})
}
// DebugIdentifyClear 清除用户实名认证状态(调试用)
func DebugIdentifyClear(c *fiber.Ctx) error {
phone := c.Params("phone")
if phone == "" {
return core.NewServErr("需要提供手机号")
}
_, err := q.User.
Where(
q.User.Phone.Eq(phone),
).
UpdateSimple(
q.User.IDType.Value(0),
q.User.IDNo.Value(""),
q.User.IDToken.Value(""),
)
if err != nil {
return core.NewServErr("清除实名认证失败")
}
return c.SendString("实名信息已清除")
} }
func idenKey(id string) string { func idenKey(id string) string {

48
web/handlers/inquiry.go Normal file
View File

@@ -0,0 +1,48 @@
package handlers
import (
"platform/pkg/u"
"platform/web/core"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
"github.com/gofiber/fiber/v2"
)
// region CreateInquiry
type CreateInquiryRequest struct {
Company string `json:"company" validate:"omitempty,max=200"`
Name string `json:"name" validate:"required,max=100"`
Phone string `json:"phone" validate:"required,max=20"`
Email string `json:"email" validate:"omitempty,email,max=100"`
Content string `json:"content" validate:"required,max=1000"`
}
func CreateInquiry(c *fiber.Ctx) error {
// 解析请求参数
req := new(CreateInquiryRequest)
err := g.Validator.ParseBody(c, req)
if err != nil {
return err
}
// 创建咨询记录
err = q.Inquiry.Create(&m.Inquiry{
Company: u.X(req.Company),
Name: u.X(req.Name),
Phone: u.X(req.Phone),
Email: u.X(req.Email),
Content: u.X(req.Content),
Status: m.InquiryStatusPending,
})
if err != nil {
return core.NewServErr("提交咨询失败", err)
}
return c.SendStatus(fiber.StatusNoContent)
}
// endregion

View File

@@ -0,0 +1,53 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func AllPermissionByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopePermissionRead)
if err != nil {
return err
}
list, err := s.Permission.ListPermissions()
if err != nil {
return err
}
return c.JSON(list)
}
func PagePermissionByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopePermissionRead)
if err != nil {
return err
}
var req PagePermissionByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 获取权限列表
list, total, err := s.Permission.PagePermissions(req.PageReq)
if err != nil {
return core.NewServErr("获取权限列表失败")
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PagePermissionByAdminReq struct {
core.PageReq
}

255
web/handlers/product.go Normal file
View File

@@ -0,0 +1,255 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func AllProductByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductRead)
if err != nil {
return err
}
// 解析请求参数
// var req AllProductsByAdminReq
// if err := g.Validator.ParseBody(c, &req); err != nil {
// return err
// }
// 查询产品
products, err := s.Product.AllProducts()
if err != nil {
return err
}
return c.JSON(products)
}
type AllProductsByAdminReq struct {
}
func AllProduct(c *fiber.Ctx) error {
infos, err := s.Product.AllProductSaleInfos()
if err != nil {
return err
}
return c.JSON(infos)
}
func CreateProduct(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
if err != nil {
return err
}
var req s.CreateProductData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Product.CreateProduct(&req)
if err != nil {
return err
}
return nil
}
func UpdateProduct(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
if err != nil {
return err
}
var req s.UpdateProductData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Product.UpdateProduct(&req)
if err != nil {
return err
}
return nil
}
func DeleteProduct(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.Product.DeleteProduct(req.Id)
if err != nil {
return err
}
return nil
}
func AllProductSkuByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuRead)
if err != nil {
return err
}
var req AllProductSkuByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, err := s.ProductSku.All(req.Code)
if err != nil {
return err
}
return c.JSON(list)
}
type AllProductSkuByAdminReq struct {
Code string `json:"product_code"`
}
func PageProductSkuByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuRead)
if err != nil {
return err
}
var req PageProductSkuByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.ProductSku.Page(&req.PageReq, req.ProductId)
if err != nil {
return err
}
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: list,
})
}
type PageProductSkuByAdminReq struct {
core.PageReq
ProductId *int32 `json:"product_id"`
}
func CreateProductSku(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
var req s.CreateProductSkuData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.Create(req)
if err != nil {
return err
}
return nil
}
func UpdateProductSku(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
var req s.UpdateProductSkuData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.Update(req)
if err != nil {
return err
}
return nil
}
func UpdateProductStatusSku(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
type Params struct {
ID int32 `json:"id"`
Status int32 `json:"status"`
}
var req Params
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.Update(s.UpdateProductSkuData{
ID: req.ID,
Status: &req.Status,
})
if err != nil {
return err
}
return nil
}
func BatchUpdateProductSkuDiscount(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
var req s.BatchUpdateSkuDiscountData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.BatchUpdateDiscount(req)
if err != nil {
return err
}
return nil
}
func DeleteProductSku(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.Delete(req.Id)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,105 @@
package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
)
func PageDiscountByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeDiscountRead)
if err != nil {
return err
}
var req core.PageReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
list, total, err := s.ProductDiscount.Page(&req)
if err != nil {
return err
}
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: list,
})
}
func AllDiscountByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeDiscountRead)
if err != nil {
return err
}
list, err := s.ProductDiscount.All()
if err != nil {
return err
}
return c.JSON(list)
}
func CreateDiscount(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeDiscountWrite)
if err != nil {
return err
}
var req s.CreateProductDiscountData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductDiscount.Create(req)
if err != nil {
return err
}
return nil
}
func UpdateDiscount(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeDiscountWrite)
if err != nil {
return err
}
var req s.UpdateProductDiscountData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductDiscount.Update(req)
if err != nil {
return err
}
return nil
}
func DeleteDiscount(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeDiscountWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductDiscount.Delete(req.Id)
if err != nil {
return err
}
return nil
}

View File

@@ -1,10 +1,11 @@
package handlers package handlers
import ( import (
"log/slog"
"net/netip" "net/netip"
"platform/pkg/env" "platform/pkg/env"
g "platform/web/globals" "platform/web/auth"
"platform/web/core"
"platform/web/globals"
s "platform/web/services" s "platform/web/services"
"time" "time"
@@ -16,37 +17,48 @@ func DebugRegisterProxyBaiYin(c *fiber.Ctx) error {
return fiber.ErrNotFound return fiber.ErrNotFound
} }
ok, err := g.Redis.SetNX(c.Context(), "debug:channel:register:127.0.0.1", true, 0).Result() err := s.Proxy.RegisterBaiyin("1a:2b:3c:4d:5e:6f", netip.AddrFrom4([4]byte{127, 0, 0, 1}), "test", "test")
if err != nil { if err != nil {
return err return core.NewServErr("注册失败", err)
}
slog.Info("注册代理", "ok", ok)
if !ok {
return fiber.ErrConflict
}
err = s.Proxy.RegisterBaiyin("1a:2b:3c:4d:5e:6f", netip.AddrFrom4([4]byte{127, 0, 0, 1}), "test", "test")
if err != nil {
return err
} }
return nil return nil
} }
// 注册白银代理网关
func ProxyRegisterBaiYin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
return err
}
req := new(RegisterProxyBaiyinReq)
err = globals.Validator.ParseBody(c, req)
if err != nil {
return err
}
addr, err := netip.ParseAddr(req.IP)
if err != nil {
return core.NewServErr("IP地址格式错误", err)
}
err = s.Proxy.RegisterBaiyin(req.Name, addr, req.Username, req.Password)
if err != nil {
return core.NewServErr("注册失败", err)
}
return nil
}
type RegisterProxyBaiyinReq struct {
Name string `json:"name" validate:"required"`
IP string `json:"ip" validate:"required"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
// region 报告上线 // region 报告上线
type ProxyReportOnlineReq struct {
Name string `json:"name" validate:"required"`
Version int `json:"version" validate:"required"`
}
type ProxyReportOnlineResp struct {
Id int32 `json:"id"`
Secret string `json:"secret"`
Permits []*ProxyPermit `json:"permits"`
Edges []*ProxyEdge `json:"edges"`
}
func ProxyReportOnline(c *fiber.Ctx) (err error) { func ProxyReportOnline(c *fiber.Ctx) (err error) {
return c.JSON(map[string]any{ return c.JSON(map[string]any{
"error": "接口暂不可用", "error": "接口暂不可用",
@@ -150,12 +162,19 @@ func ProxyReportOnline(c *fiber.Ctx) (err error) {
// }) // })
} }
// region 报告下线 type ProxyReportOnlineReq struct {
Name string `json:"name" validate:"required"`
type ProxyReportOfflineReq struct { Version int `json:"version" validate:"required"`
Id int32 `json:"id" validate:"required"`
} }
type ProxyReportOnlineResp struct {
Id int32 `json:"id"`
Secret string `json:"secret"`
Permits []*ProxyPermit `json:"permits"`
Edges []*ProxyEdge `json:"edges"`
}
// region 报告下线
func ProxyReportOffline(c *fiber.Ctx) (err error) { func ProxyReportOffline(c *fiber.Ctx) (err error) {
return c.JSON(map[string]any{ return c.JSON(map[string]any{
"error": "接口暂不可用", "error": "接口暂不可用",
@@ -193,13 +212,11 @@ func ProxyReportOffline(c *fiber.Ctx) (err error) {
// return nil // return nil
} }
// region 报告更新 type ProxyReportOfflineReq struct {
Id int32 `json:"id" validate:"required"`
type ProxyReportUpdateReq struct {
Id int32 `json:"id" validate:"required"`
Edges []*ProxyEdge `json:"edges" validate:"required"`
} }
// region 报告更新
func ProxyReportUpdate(c *fiber.Ctx) (err error) { func ProxyReportUpdate(c *fiber.Ctx) (err error) {
return c.JSON(map[string]any{ return c.JSON(map[string]any{
"error": "接口暂不可用", "error": "接口暂不可用",
@@ -358,6 +375,11 @@ func ProxyReportUpdate(c *fiber.Ctx) (err error) {
// return nil // return nil
} }
type ProxyReportUpdateReq struct {
Id int32 `json:"id" validate:"required"`
Edges []*ProxyEdge `json:"edges" validate:"required"`
}
type ProxyPermit struct { type ProxyPermit struct {
Id int32 `json:"id"` Id int32 `json:"id"`
Expire time.Time `json:"expire"` Expire time.Time `json:"expire"`

View File

@@ -15,18 +15,8 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type ListResourceShortReq struct { // PageResourceShort 分页查询当前用户短效套餐
core.PageReq func PageResourceShort(c *fiber.Ctx) error {
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Type *int `json:"type"`
CreateAfter *time.Time `json:"create_after"`
CreateBefore *time.Time `json:"create_before"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
}
func ListResourceShort(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil { if err != nil {
@@ -34,7 +24,7 @@ func ListResourceShort(c *fiber.Ctx) error {
} }
// 解析请求参数 // 解析请求参数
req := new(ListResourceShortReq) req := new(PageResourceShortReq)
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {
return err return err
} }
@@ -60,10 +50,23 @@ func ListResourceShort(c *fiber.Ctx) error {
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore)) do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
} }
if req.ExpireAfter != nil { if req.ExpireAfter != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Gte(*req.ExpireAfter)) do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Gte(*req.ExpireAfter))
} }
if req.ExpireBefore != nil { if req.ExpireBefore != nil {
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Expire.Lte(*req.ExpireBefore)) do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(*req.ExpireBefore))
}
if req.Status != nil {
var short = q.ResourceShort.As(q.Resource.Short.Name())
switch *req.Status {
case 1:
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Gte(time.Now()))
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.GtCol(short.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
case 2:
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Lte(time.Now()))
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.LteCol(short.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
}
} }
resource, err := q.Resource.Where(do). resource, err := q.Resource.Where(do).
@@ -96,7 +99,7 @@ func ListResourceShort(c *fiber.Ctx) error {
}) })
} }
type ListResourceLongReq struct { type PageResourceShortReq struct {
core.PageReq core.PageReq
ResourceNo *string `json:"resource_no"` ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"` Active *bool `json:"active"`
@@ -105,9 +108,11 @@ type ListResourceLongReq struct {
CreateBefore *time.Time `json:"create_before"` CreateBefore *time.Time `json:"create_before"`
ExpireAfter *time.Time `json:"expire_after"` ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"` ExpireBefore *time.Time `json:"expire_before"`
Status *int `json:"status"` // 0 - 全部1 - 有效2 - 过期
} }
func ListResourceLong(c *fiber.Ctx) error { // PageResourceLong 分页查询当前用户长效套餐
func PageResourceLong(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil { if err != nil {
@@ -115,7 +120,7 @@ func ListResourceLong(c *fiber.Ctx) error {
} }
// 解析请求参数 // 解析请求参数
req := new(ListResourceLongReq) req := new(PageResourceLongReq)
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {
return err return err
} }
@@ -141,10 +146,23 @@ func ListResourceLong(c *fiber.Ctx) error {
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore)) do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
} }
if req.ExpireAfter != nil { if req.ExpireAfter != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Gte(*req.ExpireAfter)) do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Gte(*req.ExpireAfter))
} }
if req.ExpireBefore != nil { if req.ExpireBefore != nil {
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Expire.Lte(*req.ExpireBefore)) do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(*req.ExpireBefore))
}
if req.Status != nil {
var long = q.ResourceLong.As(q.Resource.Long.Name())
switch *req.Status {
case 1:
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Gte(time.Now()))
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.GtCol(long.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
case 2:
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Lte(time.Now()))
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.LteCol(long.Used))
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
}
} }
resource, err := q.Resource.Where(do). resource, err := q.Resource.Where(do).
@@ -177,9 +195,347 @@ func ListResourceLong(c *fiber.Ctx) error {
}) })
} }
type AllResourceReq struct { type PageResourceLongReq struct {
core.PageReq
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Type *int `json:"type"`
CreateAfter *time.Time `json:"create_after"`
CreateBefore *time.Time `json:"create_before"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
Status *int `json:"status"` // 0 - 全部1 - 有效2 - 过期
} }
// PageResourceShortByAdmin 分页查询全部短效套餐
func PageResourceShortByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortRead)
if err != nil {
return err
}
var req PageResourceShortByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Resource.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Resource.CreatedAt.Lte(time))
}
if req.Expired != nil {
if *req.Expired {
do = do.Where(q.Resource.Where(
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
q.ResourceShort.As("Short").ExpireAt.Lte(time.Now()),
).Or(
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
q.ResourceShort.As("Short").Quota.LteCol(q.ResourceShort.As("Short").Used),
))
} else {
do = do.Where(q.Resource.Where(
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
q.ResourceShort.As("Short").ExpireAt.Gt(time.Now()),
).Or(
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
q.ResourceShort.As("Short").Quota.GtCol(q.ResourceShort.As("Short").Used),
))
}
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceShort.As("Short").Type.As("Short__type"),
q.ResourceShort.As("Short").Live.As("Short__live"),
q.ResourceShort.As("Short").Quota.As("Short__quota"),
q.ResourceShort.As("Short").Used.As("Short__used"),
q.ResourceShort.As("Short").Daily.As("Short__daily"),
q.ResourceShort.As("Short").LastAt.As("Short__last_at"),
q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"),
q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceShortByAdminReq struct {
core.PageReq
UserPhone *string `json:"user_phone" form:"user_phone"`
ResourceNo *string `json:"resource_no" form:"resource_no"`
Active *bool `json:"active" form:"active"`
Mode *int `json:"mode" form:"mode"`
CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"`
Expired *bool `json:"expired" form:"expired"`
}
// PageResourceLongByAdmin 分页查询全部长效套餐
func PageResourceLongByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongRead)
if err != nil {
return err
}
var req PageResourceLongByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
}
if req.CreatedAtStart != nil {
do = do.Where(q.Resource.CreatedAt.Gte(*req.CreatedAtStart))
}
if req.CreatedAtEnd != nil {
do = do.Where(q.Resource.CreatedAt.Lte(*req.CreatedAtEnd))
}
if req.Expired != nil {
if *req.Expired {
do = do.Where(q.Resource.Where(
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
q.ResourceLong.As("Long").ExpireAt.Lte(time.Now()),
).Or(
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
q.ResourceLong.As("Long").Quota.LteCol(q.ResourceLong.As("Long").Used),
))
} else {
do = do.Where(q.Resource.Where(
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
q.ResourceLong.As("Long").ExpireAt.Gt(time.Now()),
).Or(
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
q.ResourceLong.As("Long").Quota.GtCol(q.ResourceLong.As("Long").Used),
))
}
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceLong.As("Long").Type.As("Long__type"),
q.ResourceLong.As("Long").Live.As("Long__live"),
q.ResourceLong.As("Long").Quota.As("Long__quota"),
q.ResourceLong.As("Long").Used.As("Long__used"),
q.ResourceLong.As("Long").Daily.As("Long__daily"),
q.ResourceLong.As("Long").LastAt.As("Long__last_at"),
q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"),
q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceLongByAdminReq struct {
core.PageReq
UserPhone *string `json:"user_phone" form:"user_phone"`
ResourceNo *string `json:"resource_no" form:"resource_no"`
Active *bool `json:"active" form:"active"`
Mode *int `json:"mode" form:"mode"`
CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"`
Expired *bool `json:"expired" form:"expired"`
}
// PageResourceShortOfUserByAdmin 分页查询指定用户的短效套餐
func PageResourceShortOfUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortReadOfUser)
if err != nil {
return err
}
var req PageResourceShortOfUserByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Resource.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Resource.CreatedAt.Lte(t))
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceShort.As("Short").Type.As("Short__type"),
q.ResourceShort.As("Short").Live.As("Short__live"),
q.ResourceShort.As("Short").Quota.As("Short__quota"),
q.ResourceShort.As("Short").Used.As("Short__used"),
q.ResourceShort.As("Short").Daily.As("Short__daily"),
q.ResourceShort.As("Short").LastAt.As("Short__last_at"),
q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"),
q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceShortOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Mode *int `json:"mode"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// PageResourceLongOfUserByAdmin 分页查询指定用户的长效套餐
func PageResourceLongOfUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongReadOfUser)
if err != nil {
return err
}
var req PageResourceLongOfUserByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Resource.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Resource.CreatedAt.Lte(t))
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceLong.As("Long").Type.As("Long__type"),
q.ResourceLong.As("Long").Live.As("Long__live"),
q.ResourceLong.As("Long").Quota.As("Long__quota"),
q.ResourceLong.As("Long").Used.As("Long__used"),
q.ResourceLong.As("Long").Daily.As("Long__daily"),
q.ResourceLong.As("Long").LastAt.As("Long__last_at"),
q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"),
q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceLongOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Mode *int `json:"mode"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// AllActiveResource 所有可用套餐
func AllActiveResource(c *fiber.Ctx) error { func AllActiveResource(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -204,10 +560,10 @@ func AllActiveResource(c *fiber.Ctx) error {
q.Resource.Type.Eq(int(m.ResourceTypeShort)), q.Resource.Type.Eq(int(m.ResourceTypeShort)),
q.ResourceShort.As(q.Resource.Short.Name()).Where( q.ResourceShort.As(q.Resource.Short.Name()).Where(
short.Type.Eq(int(m.ResourceModeTime)), short.Type.Eq(int(m.ResourceModeTime)),
short.Expire.Gte(now), short.ExpireAt.Gte(now),
q.ResourceShort.As(q.Resource.Short.Name()). q.ResourceShort.As(q.Resource.Short.Name()).
Where(short.DailyLast.Lt(u.Today())). Where(short.LastAt.Lt(u.Today())).
Or(short.DailyLimit.GtCol(short.DailyUsed)), Or(short.Quota.GtCol(short.Daily)),
).Or( ).Or(
short.Type.Eq(int(m.ResourceModeQuota)), short.Type.Eq(int(m.ResourceModeQuota)),
short.Quota.GtCol(short.Used), short.Quota.GtCol(short.Used),
@@ -216,10 +572,10 @@ func AllActiveResource(c *fiber.Ctx) error {
q.Resource.Type.Eq(int(m.ResourceTypeLong)), q.Resource.Type.Eq(int(m.ResourceTypeLong)),
q.ResourceLong.As(q.Resource.Long.Name()).Where( q.ResourceLong.As(q.Resource.Long.Name()).Where(
long.Type.Eq(int(m.ResourceModeTime)), long.Type.Eq(int(m.ResourceModeTime)),
long.Expire.Gte(now), long.ExpireAt.Gte(now),
q.ResourceLong.As(q.Resource.Long.Name()). q.ResourceLong.As(q.Resource.Long.Name()).
Where(long.DailyLast.Lt(u.Today())). Where(long.LastAt.Lt(u.Today())).
Or(long.DailyLimit.GtCol(long.DailyUsed)), Or(long.Quota.GtCol(long.Daily)),
).Or( ).Or(
long.Type.Eq(int(m.ResourceModeQuota)), long.Type.Eq(int(m.ResourceModeQuota)),
long.Quota.GtCol(long.Used), long.Quota.GtCol(long.Used),
@@ -235,23 +591,25 @@ func AllActiveResource(c *fiber.Ctx) error {
return c.JSON(resources) return c.JSON(resources)
} }
type StatisticPersonalResp struct { func UpdateResourceByAdmin(c *fiber.Ctx) error {
Short StatisticShort `json:"short"` _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceWrite)
Long StatisticLong `json:"long"` if err != nil {
} return err
}
type StatisticShort struct {
ResourceCount int `json:"resource_count"` var req s.UpdateResourceData
ResourceQuotaSum int `json:"resource_quota_sum"` if err := c.BodyParser(&req); err != nil {
ResourceDailyFreeSum int `json:"resource_daily_free_sum"` return err
} }
type StatisticLong struct { if err := s.Resource.Update(&req); err != nil {
ResourceCount int `json:"resource_count"` return err
ResourceQuotaSum int `json:"resource_quota_sum"` }
ResourceDailyFreeSum int `json:"resource_daily_free_sum"`
return c.JSON(nil)
} }
// StatisticResourceFree 统计每日可用
func StatisticResourceFree(c *fiber.Ctx) error { func StatisticResourceFree(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -282,39 +640,39 @@ func StatisticResourceFree(c *fiber.Ctx) error {
// 短效包量 // 短效包量
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota: case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeQuota:
if u.Z(resource.Short.Quota) > resource.Short.Used { if resource.Short.Quota > resource.Short.Used {
shortCount++ shortCount++
shortQuotaSum += int(u.Z(resource.Short.Quota) - resource.Short.Used) shortQuotaSum += int(resource.Short.Quota - resource.Short.Used)
} }
// 长效包量 // 长效包量
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota: case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeQuota:
if u.Z(resource.Long.Quota) > resource.Long.Used { if resource.Long.Quota > resource.Long.Used {
longCount++ longCount++
longQuotaSum += int(u.Z(resource.Long.Quota) - resource.Long.Used) longQuotaSum += int(resource.Long.Quota - resource.Long.Used)
} }
// 短效包时 // 短效包时
case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime: case resource.Type == m.ResourceTypeShort && resource.Short.Type == m.ResourceModeTime:
if time.Time(*resource.Short.Expire).After(time.Now()) { if time.Time(*resource.Short.ExpireAt).After(time.Now()) {
if resource.Short.DailyLast == nil || u.IsToday(time.Time(*resource.Short.DailyLast)) == false { if resource.Short.LastAt == nil || u.IsToday(time.Time(*resource.Short.LastAt)) == false {
shortCount++ shortCount++
shortDailyFreeSum += int(resource.Short.DailyLimit) shortDailyFreeSum += int(resource.Short.Quota)
} else if resource.Short.DailyLimit > resource.Short.DailyUsed { } else if resource.Short.Quota > resource.Short.Daily {
shortCount++ shortCount++
shortDailyFreeSum += int(resource.Short.DailyLimit - resource.Short.DailyUsed) shortDailyFreeSum += int(resource.Short.Quota - resource.Short.Daily)
} }
} }
// 长效包时 // 长效包时
case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime: case resource.Type == m.ResourceTypeLong && resource.Long.Type == m.ResourceModeTime:
if time.Time(*resource.Long.Expire).After(time.Now()) { if time.Time(*resource.Long.ExpireAt).After(time.Now()) {
if resource.Long.DailyLast == nil || u.IsToday(time.Time(*resource.Long.DailyLast)) == false { if resource.Long.LastAt == nil || u.IsToday(time.Time(*resource.Long.LastAt)) == false {
longCount++ longCount++
longDailyFreeSum += int(resource.Long.DailyLimit) longDailyFreeSum += int(resource.Long.Quota)
} else if resource.Long.DailyLimit > resource.Long.DailyUsed { } else if resource.Long.Quota > resource.Long.Daily {
longCount++ longCount++
longDailyFreeSum += int(resource.Long.DailyLimit - resource.Long.DailyUsed) longDailyFreeSum += int(resource.Long.Quota - resource.Long.Daily)
} }
} }
} }
@@ -334,17 +692,24 @@ func StatisticResourceFree(c *fiber.Ctx) error {
}) })
} }
type StatisticResourceUsageReq struct { type StatisticPersonalResp struct {
ResourceNo *string `json:"resource_no"` Short StatisticShort `json:"short"`
TimeAfter *time.Time `json:"time_after"` Long StatisticLong `json:"long"`
TimeBefore *time.Time `json:"time_before"`
} }
type StatisticResourceUsageResp []struct { type StatisticShort struct {
Date time.Time `json:"date"` ResourceCount int `json:"resource_count"`
Count int `json:"count"` ResourceQuotaSum int `json:"resource_quota_sum"`
ResourceDailyFreeSum int `json:"resource_daily_free_sum"`
} }
type StatisticLong struct {
ResourceCount int `json:"resource_count"`
ResourceQuotaSum int `json:"resource_quota_sum"`
ResourceDailyFreeSum int `json:"resource_daily_free_sum"`
}
// StatisticResourceUsage 统计每日用量
func StatisticResourceUsage(c *fiber.Ctx) error { func StatisticResourceUsage(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -359,21 +724,10 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
} }
// 统计套餐提取数量 // 统计套餐提取数量
do := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(authCtx.User.ID)) do := q.LogsUserUsage.Where(
if req.ResourceNo != nil && *req.ResourceNo != "" { q.LogsUserUsage.UserID.Eq(authCtx.User.ID),
var resourceID int32 )
err := q.Resource.
Where(
q.Resource.UserID.Eq(authCtx.User.ID),
q.Resource.ResourceNo.Eq(*req.ResourceNo),
).
Select(q.Resource.ID).
Scan(&resourceID)
if err != nil {
return err
}
do.Where(q.LogsUserUsage.ResourceID.Eq(resourceID))
}
if req.TimeAfter != nil { if req.TimeAfter != nil {
do.Where(q.LogsUserUsage.Time.Gte(*req.TimeAfter)) do.Where(q.LogsUserUsage.Time.Gte(*req.TimeAfter))
} }
@@ -389,7 +743,7 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
). ).
Where(do). Where(do).
Group( Group(
field.NewUnsafeFieldRaw("date_trunc('day', time)"), field.NewField("", "date"),
). ).
Order( Order(
field.NewField("", "date").Desc(), field.NewField("", "date").Desc(),
@@ -402,10 +756,17 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
return c.JSON(data) return c.JSON(data)
} }
type CreateResourceReq struct { type StatisticResourceUsageReq struct {
*s.CreateResourceData TimeAfter *time.Time `json:"time_start"`
TimeBefore *time.Time `json:"time_end"`
} }
type StatisticResourceUsageResp []struct {
Date time.Time `json:"date"`
Count int `json:"count"`
}
// CreateResource 创建套餐
func CreateResource(c *fiber.Ctx) error { func CreateResource(c *fiber.Ctx) error {
// 检查权限 // 检查权限
@@ -421,7 +782,7 @@ func CreateResource(c *fiber.Ctx) error {
} }
// 创建套餐 // 创建套餐
err = s.Resource.CreateResourceByBalance(authCtx.User.ID, time.Now(), req.CreateResourceData) err = s.Resource.CreateResourceByBalance(authCtx.User, req.CreateResourceData)
if err != nil { if err != nil {
return err return err
} }
@@ -429,6 +790,11 @@ func CreateResource(c *fiber.Ctx) error {
return nil return nil
} }
type CreateResourceReq struct {
*s.CreateResourceData
}
// ResourcePrice 套餐价格
func ResourcePrice(c *fiber.Ctx) error { func ResourcePrice(c *fiber.Ctx) error {
// 检查权限 // 检查权限
_, err := auth.GetAuthCtx(c).PermitSecretClient() _, err := auth.GetAuthCtx(c).PermitSecretClient()
@@ -439,11 +805,25 @@ func ResourcePrice(c *fiber.Ctx) error {
// 解析请求参数 // 解析请求参数
var req = new(CreateResourceReq) var req = new(CreateResourceReq)
if err := g.Validator.ParseBody(c, req); err != nil { if err := g.Validator.ParseBody(c, req); err != nil {
return err return core.NewBizErr("接口参数解析异常", err)
} }
// 获取套餐价格 // 获取套餐价格
return c.JSON(fiber.Map{ detail, err := req.TradeDetail(nil)
"price": req.GetAmount().StringFixed(2), if err != nil {
return err
}
// 计算折扣
return c.JSON(ResourcePriceResp{
Price: detail.Amount.StringFixed(2),
Discounted: detail.Discounted.StringFixed(2),
Actual: detail.Actual.StringFixed(2),
}) })
} }
type ResourcePriceResp struct {
Price string `json:"price"`
Discounted string `json:"discounted"`
Actual string `json:"actual"`
}

View File

@@ -5,30 +5,177 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u"
"platform/web/auth" "platform/web/auth"
"platform/web/core" "platform/web/core"
g "platform/web/globals" g "platform/web/globals"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"reflect"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
) )
type TradeCreateReq struct { // PageTradeByAdmin 分页查询所有订单
s.CreateTradeData func PageTradeByAdmin(c *fiber.Ctx) error {
Type m.TradeType `json:"type" validate:"required"` // 检查权限
Resource *s.CreateResourceData `json:"resource,omitempty"` _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead)
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"` if err != nil {
return err
}
// 解析请求参数
req := new(PageTradeByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询语句
do := q.Trade.Where()
if req.UserPhone != nil {
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.InnerNo != nil {
do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo))
}
if req.OuterNo != nil {
do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo))
}
if req.Method != nil {
do = do.Where(q.Trade.Method.Eq(*req.Method))
}
if req.Platform != nil {
do = do.Where(q.Trade.Platform.Eq(*req.Platform))
}
if req.Status != nil {
do = do.Where(q.Trade.Status.Eq(*req.Status))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Trade.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Trade.CreatedAt.Lte(time))
}
// 查询用户列表
list, total, err := q.Trade.
Joins(q.Trade.User).
Select(
q.Trade.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Trade.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
} }
type TradeCreateResp struct { type PageTradeByAdminReq struct {
PayUrl string `json:"pay_url"` core.PageReq
TradeNo string `json:"trade_no"` UserPhone *string `json:"user_phone,omitempty"`
InnerNo *string `json:"inner_no,omitempty"`
OuterNo *string `json:"outer_no,omitempty"`
Method *int `json:"method,omitempty"`
Platform *int `json:"platform,omitempty"`
Status *int `json:"status,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
} }
// PageTradeOfUserByAdmin 分页查询指定用户的订单
func PageTradeOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeReadOfUser)
if err != nil {
return err
}
// 解析请求参数
req := new(PageTradeOfUserByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询语句
do := q.Trade.Where(q.Trade.UserID.Eq(req.UserID))
if req.InnerNo != nil {
do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo))
}
if req.OuterNo != nil {
do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo))
}
if req.Method != nil {
do = do.Where(q.Trade.Method.Eq(*req.Method))
}
if req.Platform != nil {
do = do.Where(q.Trade.Platform.Eq(*req.Platform))
}
if req.Status != nil {
do = do.Where(q.Trade.Status.Eq(*req.Status))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Trade.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Trade.CreatedAt.Lte(time))
}
// 查询订单列表
list, total, err := q.Trade.
Joins(q.Trade.User).
Select(
q.Trade.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Trade.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageTradeOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
InnerNo *string `json:"inner_no,omitempty"`
OuterNo *string `json:"outer_no,omitempty"`
Method *int `json:"method,omitempty"`
Platform *int `json:"platform,omitempty"`
Status *int `json:"status,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
}
// ============================================================
// 创建订单
func TradeCreate(c *fiber.Ctx) error { func TradeCreate(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -41,52 +188,57 @@ func TradeCreate(c *fiber.Ctx) error {
if err := g.Validator.ParseBody(c, req); err != nil { if err := g.Validator.ParseBody(c, req); err != nil {
return err return err
} }
switch req.Type { switch req.Type {
case m.TradeTypePurchase: case m.TradeTypePurchase:
if req.Resource == nil { if req.Resource == nil {
return core.NewBizErr("购买信息不能为空") return core.NewBizErr("购买信息不能为空")
} }
req.Product = req.Resource
case m.TradeTypeRecharge: case m.TradeTypeRecharge:
if req.Recharge == nil { if req.Recharge == nil {
return core.NewBizErr("充值信息不能为空") return core.NewBizErr("充值信息不能为空")
} }
req.Product = req.Recharge
} }
// 创建交易 // 处理订单
result, err := s.Trade.CreateTrade(authCtx.User.ID, time.Now(), &req.CreateTradeData) var result *s.CreateTradeResult
switch req.Type {
case m.TradeTypePurchase:
result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Resource)
case m.TradeTypeRecharge:
result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Recharge)
}
if err != nil { if err != nil {
slog.Error("创建交易失败", "error", err) return core.NewServErr("处理购买产品信息失败", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "创建交易失败"})
} }
return c.JSON(&TradeCreateResp{ return c.JSON(result)
PayUrl: result.PaymentUrl,
TradeNo: result.TradeNo,
})
} }
type TradeCompleteReq struct { type TradeCreateReq struct {
s.ModifyTradeData *s.CreateTradeData
Type m.TradeType `json:"type" validate:"required"`
Resource *s.CreateResourceData `json:"resource,omitempty"`
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
} }
// ============================================================
// 完成订单
func TradeComplete(c *fiber.Ctx) error { func TradeComplete(c *fiber.Ctx) error {
// 检查权限 // 检查权限
_, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil { if err != nil {
return err return err
} }
// 解析请求参数 // 解析请求参数
req := new(TradeCompleteReq) var req s.TradeRef
if err := g.Validator.ParseBody(c, req); err != nil { if err := g.Validator.ParseBody(c, &req); err != nil {
return err return err
} }
// 检查订单状态 // 检查订单状态
err = s.Trade.CompleteTrade(&req.ModifyTradeData) err = s.Trade.CompleteTrade(authCtx.User, &req)
if err != nil { if err != nil {
return err return err
} }
@@ -94,10 +246,41 @@ func TradeComplete(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
type TradeCancelReq struct { // 管理员完成订单
s.ModifyTradeData func TradeCompleteByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWriteComplete)
if err != nil {
return err
}
// 解析请求参数
var req struct {
s.TradeRef
UserID int32 `json:"user_id" validate:"required"`
}
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 获取用户信息
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
// 完成订单
err = s.Trade.CompleteTrade(user, &req.TradeRef)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusNoContent)
} }
// ============================================================
// 取消订单
func TradeCancel(c *fiber.Ctx) error { func TradeCancel(c *fiber.Ctx) error {
// 检查权限 // 检查权限
_, err := auth.GetAuthCtx(c).PermitUser() _, err := auth.GetAuthCtx(c).PermitUser()
@@ -112,7 +295,7 @@ func TradeCancel(c *fiber.Ctx) error {
} }
// 取消交易 // 取消交易
err = s.Trade.CancelTrade(&req.ModifyTradeData, time.Now()) err = s.Trade.CancelTrade(&req.TradeRef)
if err != nil { if err != nil {
slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err) slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"}) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"})
@@ -121,11 +304,16 @@ func TradeCancel(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
type TradeCheckReq struct { type TradeCancelReq struct {
s.ModifyTradeData s.TradeRef
} }
// ============================================================
// 检查订单
func TradeCheck(c *fiber.Ctx) error { func TradeCheck(c *fiber.Ctx) error {
// 检查权限sse 接口暂时不检查权限
// 解析请求参数 // 解析请求参数
req := new(TradeCheckReq) req := new(TradeCheckReq)
if err := g.Validator.ParseQuery(c, req); err != nil { if err := g.Validator.ParseQuery(c, req); err != nil {
@@ -142,7 +330,7 @@ func TradeCheck(c *fiber.Ctx) error {
interval := 5 interval := 5
for range expire / interval { for range expire / interval {
// 检查订单状态 // 检查订单状态
result, err := s.Trade.CheckTrade(&req.ModifyTradeData) result, err := s.Trade.CheckTrade(&req.TradeRef)
if err != nil { if err != nil {
slog.Error("检查订单状态失败", "trade_no", req.TradeNo, "error", err) slog.Error("检查订单状态失败", "trade_no", req.TradeNo, "error", err)
return return
@@ -154,9 +342,9 @@ func TradeCheck(c *fiber.Ctx) error {
slog.Error("写入订单状态失败", "trade_no", req.TradeNo, "error", err) slog.Error("写入订单状态失败", "trade_no", req.TradeNo, "error", err)
return return
} }
err = w.Flush() err = w.Flush()
if err != nil { if err != nil {
slog.Error("刷新缓冲区失败", "trade_no", req.TradeNo, "error", err, "errType", reflect.TypeOf(err))
return return
} }
@@ -171,3 +359,7 @@ func TradeCheck(c *fiber.Ctx) error {
return nil return nil
} }
type TradeCheckReq struct {
s.TradeRef
}

View File

@@ -1,24 +1,297 @@
package handlers package handlers
import ( import (
"errors"
"platform/web/auth" "platform/web/auth"
"platform/web/core"
g "platform/web/globals"
m "platform/web/models" m "platform/web/models"
q "platform/web/queries" q "platform/web/queries"
s "platform/web/services" s "platform/web/services"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gen/field"
"gorm.io/gorm"
) )
// region /update // 分页获取用户
func PageUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserRead)
if err != nil {
return err
}
type UpdateUserReq struct { // 解析请求参数
Username string `json:"username" validate:"omitempty,min=3,max=20"` req := new(PageUserByAdminReq)
Email string `json:"email" validate:"omitempty,email"` if err := g.Validator.ParseBody(c, req); err != nil {
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"` return err
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"` }
// 构建查询条件
do := q.User.Where()
if req.Account != nil {
do = do.Where(q.User.Where(
q.User.Username.Like("%" + *req.Account + "%"),
).Or(
q.User.Phone.Like("%" + *req.Account + "%"),
).Or(
q.User.Email.Like("%" + *req.Account + "%"),
))
}
if req.Name != nil {
do = do.Where(q.User.Name.Eq(*req.Name))
}
if req.Identified != nil {
if *req.Identified {
do = do.Where(q.User.IDType.Gt(0))
} else {
do = do.Where(q.User.IDType.Eq(0))
}
}
if req.Enabled != nil {
if *req.Enabled {
do = do.Where(q.User.Status.Eq(1))
} else {
do = do.Where(q.User.Status.Eq(0))
}
}
if req.Assigned != nil {
if *req.Assigned {
do = do.Where(q.User.AdminID.IsNotNull())
} else {
do = do.Where(q.User.AdminID.IsNull())
}
}
// 查询用户列表
users, total, err := q.User.
Preload(q.User.Admin, q.User.Discount).
Omit(q.User.Password, q.Admin.Password).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
for _, user := range users {
if user.IDNo != nil && len(*user.IDNo) == 18 {
var str = *user.IDNo
*user.IDNo = str[:6] + "****" + str[len(str)-2:]
}
if user.Admin != nil {
user.Admin = &m.Admin{
Name: user.Admin.Name,
}
}
}
// 返回结果
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: users,
})
} }
type PageUserByAdminReq struct {
core.PageReq
Account *string `json:"account,omitempty"`
Name *string `json:"name,omitempty"`
Identified *bool `json:"identified,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Assigned *bool `json:"assigned,omitempty"`
}
// 管理员获取单个用户
func GetUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadOne)
if err != nil {
return err
}
// 解析请求参数
var req GetUserByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 构建查询条件
do := q.User.Where()
if req.Account != nil {
do = do.Where(q.User.Where(
q.User.Username.Like("%" + *req.Account + "%"),
).Or(
q.User.Phone.Like("%" + *req.Account + "%"),
).Or(
q.User.Email.Like("%" + *req.Account + "%"),
))
}
if req.Name != nil {
do = do.Where(q.User.Name.Eq(*req.Name))
}
// 查询用户
user, err := q.User.
Preload(q.User.Admin, q.User.Discount).
Omit(q.User.Password, q.Admin.Password).
Where(do).
Order(q.User.CreatedAt.Desc()).
First()
if err == gorm.ErrRecordNotFound {
return core.NewBizErr("找不到用户")
}
if err != nil {
return err
}
// 仅保留管理员名称
if user.Admin != nil {
user.Admin = &m.Admin{
Name: user.Admin.Name,
}
}
// 返回结果
return c.JSON(user)
}
type GetUserByAdminReq struct {
Account *string `json:"account,omitempty"`
Name *string `json:"name,omitempty"`
}
// 管理员创建用户
func CreateUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
if err != nil {
return err
}
var req s.CreateUserByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.User.CreateByAdmin(req); err != nil {
return err
}
return c.JSON(nil)
}
// 管理员更新用户
func UpdateUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
if err != nil {
return err
}
var req s.UpdateUserByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.User.UpdateByAdmin(req); err != nil {
return err
}
return c.JSON(nil)
}
// 管理员删除用户
func RemoveUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
if err != nil {
return err
}
var req core.IdReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
if err := s.User.RemoveByAdmin(req.Id); err != nil {
return err
}
return c.JSON(nil)
}
// 管理员更新用户余额
func UpdateUserBalanceByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
balance, err := decimal.NewFromString(req.Balance)
if err != nil {
return err
}
if err := s.User.UpdateBalanceByAdmin(user, balance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
type UpdateUserBalanceByAdminData struct {
UserID int32 `json:"user_id" validate:"required"`
Balance string `json:"balance" validate:"required"`
}
// 绑定管理员
func BindAdmin(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
if err != nil {
return err
}
// 解析请求参数
req := new(struct {
UserID int `json:"user_id" validate:"required"`
})
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 更新用户信息
result, err := q.User.Where(
q.User.ID.Eq(int32(req.UserID)),
q.User.AdminID.IsNull(),
).UpdateColumnSimple(
q.User.AdminID.Value(authCtx.Admin.ID),
)
if err != nil {
return err
}
if result.RowsAffected == 0 {
return core.NewBizErr("用户已绑定管理员")
}
// 返回结果
return c.SendStatus(fiber.StatusNoContent)
}
// 更新用户
func UpdateUser(c *fiber.Ctx) error { func UpdateUser(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -33,14 +306,29 @@ func UpdateUser(c *fiber.Ctx) error {
} }
// 更新用户信息 // 更新用户信息
do := make([]field.AssignExpr, 0)
if req.Username != nil && *req.Username != "" {
do = append(do, q.User.Username.Value(*req.Username))
}
if req.Email != nil {
if *req.Email == "" {
do = append(do, q.User.Email.Null())
} else {
do = append(do, q.User.Email.Value(*req.Email))
}
}
if req.ContactQQ != nil {
do = append(do, q.User.ContactQQ.Value(*req.ContactQQ))
}
if req.ContactWechat != nil {
do = append(do, q.User.ContactWechat.Value(*req.ContactWechat))
}
_, err = q.User. _, err = q.User.
Where(q.User.ID.Eq(authCtx.User.ID)). Where(q.User.ID.Eq(authCtx.User.ID)).
Updates(m.User{ UpdateSimple(do...)
Username: &req.Username, if errors.Is(err, gorm.ErrDuplicatedKey) {
Email: &req.Email, return core.NewBizErr("用户名或邮箱已被占用")
ContactQQ: &req.ContactQQ, }
ContactWechat: &req.ContactWechat,
})
if err != nil { if err != nil {
return err return err
} }
@@ -49,15 +337,14 @@ func UpdateUser(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
// endregion type UpdateUserReq struct {
Username *string `json:"username" validate:"omitempty,min=3,max=20"`
// region /update/account Email *string `json:"email" validate:"omitempty,email"`
ContactQQ *string `json:"contact_qq" validate:"omitempty,qq"`
type UpdateAccountReq struct { ContactWechat *string `json:"contact_wechat" validate:"omitempty,wechat"`
Username string `json:"username" validate:"omitempty,min=3,max=20"`
Password string `json:"password" validate:"omitempty,min=6,max=20"`
} }
// 更新账号信息
func UpdateAccount(c *fiber.Ctx) error { func UpdateAccount(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -86,16 +373,12 @@ func UpdateAccount(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
// endregion type UpdateAccountReq struct {
Username string `json:"username" validate:"omitempty,min=3,max=20"`
// region /update/password Password string `json:"password" validate:"omitempty,min=6,max=20"`
type UpdatePasswordReq struct {
Phone string `json:"phone"`
Code string `json:"code"`
Password string `json:"password"`
} }
// 更新账号密码
func UpdatePassword(c *fiber.Ctx) error { func UpdatePassword(c *fiber.Ctx) error {
// 检查权限 // 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser() authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -110,10 +393,13 @@ func UpdatePassword(c *fiber.Ctx) error {
} }
// 验证手机令牌 // 验证手机令牌
if req.Phone == "" || req.Code == "" { if req.Code == "" {
return fiber.NewError(fiber.StatusBadRequest, "手机号码和验证码不能为空") return fiber.NewError(fiber.StatusBadRequest, "验证码不能为空")
}
err = s.Verifier.VerifySms(c.Context(), authCtx.User.Phone, req.Code, s.VerifierSmsPurposePassword)
if errors.Is(err, s.ErrVerifierServiceInvalid) {
return core.NewBizErr(s.ErrVerifierServiceInvalid.Error())
} }
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
if err != nil { if err != nil {
return err return err
} }
@@ -135,4 +421,122 @@ func UpdatePassword(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
// endregion type UpdatePasswordReq struct {
Code string `json:"code"`
Password string `json:"password"`
}
// PageUserNotBindByAdmin 分页获取未绑定管理员的用户
func PageUserNotBindByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadNotBind)
if err != nil {
return err
}
// 解析请求参数
req := new(PageUserNotBindByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询条件(强制过滤未绑定管理员的用户)
do := q.User.Where(q.User.AdminID.IsNull())
if req.Phone != nil {
do = do.Where(q.User.Phone.Eq(*req.Phone))
}
// 查询用户列表
users, total, err := q.User.
Omit(q.User.Password, q.User.IDNo).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: users,
})
}
type PageUserNotBindByAdminReq struct {
core.PageReq
Phone *string `json:"phone,omitempty"`
}
// UpdateUserBalanceIncByAdmin 管理员增加用户余额
func UpdateUserBalanceIncByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Add(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
// UpdateUserBalanceDecByAdmin 管理员减少用户余额
func UpdateUserBalanceDecByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Sub(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
type UpdateUserBalanceChangeByAdminData struct {
UserID int32 `json:"user_id" validate:"required"`
Amount string `json:"amount" validate:"required"`
}

View File

@@ -5,6 +5,7 @@ import (
"platform/pkg/env" "platform/pkg/env"
"platform/web/auth" "platform/web/auth"
"platform/web/services" "platform/web/services"
s "platform/web/services"
"regexp" "regexp"
"strconv" "strconv"
@@ -13,12 +14,11 @@ import (
) )
type VerifierReq struct { type VerifierReq struct {
Purpose services.VerifierSmsPurpose `json:"purpose"` Purpose s.VerifierSmsPurpose `json:"purpose"`
Phone string `json:"phone"` Phone string `json:"phone"`
} }
func SmsCode(c *fiber.Ctx) error { func SendSmsCode(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitOfficialClient() _, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil { if err != nil {
return err return err
@@ -38,9 +38,9 @@ func SmsCode(c *fiber.Ctx) error {
} }
// 发送身份验证码 // 发送身份验证码
err = services.Verifier.SendSms(c.Context(), req.Phone, req.Purpose) err = s.Verifier.SendSms(c.Context(), req.Phone, req.Purpose)
if err != nil { if err != nil {
var sErr services.VerifierServiceSendLimitErr var sErr s.VerifierServiceSendLimitErr
if errors.As(err, &sErr) { if errors.As(err, &sErr) {
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr))) return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
} }
@@ -51,6 +51,23 @@ func SmsCode(c *fiber.Ctx) error {
return nil return nil
} }
func SendSmsCodeForPassword(c *fiber.Ctx) error {
ac, err := auth.GetAuthCtx(c).PermitUser()
if err != nil {
return err
}
if err := s.Verifier.SendSms(c.Context(), ac.User.Phone, s.VerifierSmsPurposePassword); err != nil {
var sErr s.VerifierServiceSendLimitErr
if errors.As(err, &sErr) {
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
}
return err
}
return nil
}
func DebugGetSmsCode(c *fiber.Ctx) error { func DebugGetSmsCode(c *fiber.Ctx) error {
if env.RunMode != env.RunModeDev { if env.RunMode != env.RunModeDev {
return fiber.NewError(fiber.StatusForbidden, "not allowed") return fiber.NewError(fiber.StatusForbidden, "not allowed")

View File

@@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"errors"
"platform/pkg/env" "platform/pkg/env"
"platform/pkg/u" "platform/pkg/u"
"platform/web/auth" "platform/web/auth"
@@ -92,7 +93,7 @@ func CreateWhitelist(c *fiber.Ctx) error {
ip, err := secureAddr(req.Host) ip, err := secureAddr(req.Host)
if err != nil { if err != nil {
return err return core.NewBizErr("IP 地址无效", err)
} }
// 创建白名单 // 创建白名单
@@ -132,7 +133,7 @@ func UpdateWhitelist(c *fiber.Ctx) error {
ip, err := secureAddr(req.Host) ip, err := secureAddr(req.Host)
if err != nil { if err != nil {
return err return core.NewBizErr("IP 地址无效", err)
} }
// 更新白名单 // 更新白名单
@@ -201,7 +202,7 @@ func secureAddr(str string) (*orm.Inet, error) {
return nil, err return nil, err
} }
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev { if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
return nil, fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用") return nil, errors.New("IP 地址不可用")
} }
return ip, nil return ip, nil
} }

View File

@@ -20,6 +20,14 @@ func ApplyMiddlewares(app *fiber.App) {
EnableStackTrace: true, EnableStackTrace: true,
})) }))
// cors
app.Use(cors.New(cors.Config{
AllowCredentials: true,
AllowOriginsFunc: func(origin string) bool {
return true
},
}))
// logger // logger
app.Use(logger.New(logger.Config{ app.Use(logger.New(logger.Config{
Next: func(c *fiber.Ctx) bool { Next: func(c *fiber.Ctx) bool {
@@ -38,9 +46,6 @@ func ApplyMiddlewares(app *fiber.App) {
}, },
})) }))
// cors
app.Use(cors.New())
// authenticate // authenticate
app.Use(auth.Authenticate()) app.Use(auth.Authenticate())
} }

View File

@@ -10,16 +10,19 @@ import (
// Admin 管理员表 // Admin 管理员表
type Admin struct { type Admin struct {
core.Model core.Model
Username string `json:"username" gorm:"column:username"` // 用户名 Username string `json:"username" gorm:"column:username"` // 用户名
Password string `json:"password" gorm:"column:password"` // 密码 Password string `json:"-" gorm:"column:password"` // 密码
Name *string `json:"name" gorm:"column:name"` // 真实姓名 Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar" gorm:"column:avatar"` // 头像URL Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
Phone *string `json:"phone" gorm:"column:phone"` // 手机号码 Phone *string `json:"phone,omitempty" gorm:"column:phone"` // 手机号码
Email *string `json:"email" gorm:"column:email"` // 邮箱 Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
Status AdminStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常 Status AdminStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常
LastLogin *time.Time `json:"last_login" gorm:"column:last_login"` // 最后登录时间 LastLogin *time.Time `json:"last_login,omitempty" gorm:"column:last_login"` // 最后登录时间
LastLoginIP *orm.Inet `json:"last_login_ip" gorm:"column:last_login_ip"` // 最后登录地址 LastLoginIP *orm.Inet `json:"last_login_ip,omitempty" gorm:"column:last_login_ip"` // 最后登录地址
LastLoginUA *string `json:"last_login_ua" gorm:"column:last_login_ua"` // 最后登录代理 LastLoginUA *string `json:"last_login_ua,omitempty" gorm:"column:last_login_ua"` // 最后登录代理
Lock bool `json:"lock" gorm:"column:lock"` // 是否锁定编辑
Roles []*AdminRole `json:"roles" gorm:"many2many:link_admin_role"`
} }
// AdminStatus 管理员状态枚举 // AdminStatus 管理员状态枚举

View File

@@ -7,8 +7,10 @@ import (
// AdminRole 管理员角色表 // AdminRole 管理员角色表
type AdminRole struct { type AdminRole struct {
core.Model core.Model
Name string `json:"name" gorm:"column:name"` // 角色名称 Name string `json:"name" gorm:"column:name"` // 角色名称
Description *string `json:"description" gorm:"column:description"` // 角色描述 Description *string `json:"description,omitempty" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活 Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序 Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Permissions []*Permission `json:"permissions" gorm:"many2many:link_admin_role_permission"`
} }

View File

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

View File

@@ -0,0 +1,22 @@
package models
import (
"time"
)
// BalanceActivity 余额变动记录表
type BalanceActivity struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
BillID *int32 `json:"bill_id,omitempty" gorm:"column:bill_id"` // 账单ID
AdminID *int32 `json:"admin_id,omitempty" gorm:"column:admin_id"` // 管理员ID
Amount string `json:"amount" gorm:"column:amount"` // 变动金额
BalancePrev string `json:"balance_prev" gorm:"column:balance_prev"` // 变动前余额
BalanceCurr string `json:"balance_curr" gorm:"column:balance_curr"` // 变动后余额
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
Admin *Admin `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
}

View File

@@ -9,19 +9,22 @@ import (
// Bill 账单表 // Bill 账单表
type Bill struct { type Bill struct {
core.Model core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
TradeID *int32 `json:"trade_id" gorm:"column:trade_id"` // 订单ID TradeID *int32 `json:"trade_id,omitempty" gorm:"column:trade_id"` // 订单ID
ResourceID *int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID ResourceID *int32 `json:"resource_id,omitempty" gorm:"column:resource_id"` // 套餐ID
RefundID *int32 `json:"refund_id" gorm:"column:refund_id"` // 退款ID RefundID *int32 `json:"refund_id,omitempty" gorm:"column:refund_id"` // 退款ID
BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号 CouponUserID *int32 `json:"coupon_user_id,omitempty" gorm:"column:coupon_user_id"` // 优惠券发放ID
Info *string `json:"info" gorm:"column:info"` // 产品可读信息 BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号
Type BillType `json:"type" gorm:"column:type"` // 账单类型1-消费2-退款3-充值 Info *string `json:"info,omitempty" gorm:"column:info"` // 产品可读信息
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 账单金额 Type BillType `json:"type" gorm:"column:type"` // 账单类型1-消费2-退款3-充值
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 应付金额
Actual decimal.Decimal `json:"actual" gorm:"column:actual"` // 实付金额
User *User `json:"user" gorm:"foreignKey:UserID"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Trade *Trade `json:"trade" gorm:"foreignKey:TradeID"` Trade *Trade `json:"trade,omitempty" gorm:"foreignKey:TradeID"`
Resource *Resource `json:"resource" gorm:"foreignKey:ResourceID"` Resource *Resource `json:"resource,omitempty" gorm:"foreignKey:ResourceID"`
Refund *Refund `json:"refund" gorm:"foreignKey:RefundID"` Refund *Refund `json:"refund,omitempty" gorm:"foreignKey:RefundID"`
CouponUser *CouponUser `json:"coupon,omitempty" gorm:"foreignKey:CouponUserID"`
} }
// BillType 账单类型枚举 // BillType 账单类型枚举

View File

@@ -9,23 +9,25 @@ import (
// Channel 通道表 // Channel 通道表
type Channel struct { type Channel struct {
core.Model core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_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"` // 批次编号
BatchNo string `json:"batch_no" gorm:"column:batch_no"` // 批次编号 ProxyID int32 `json:"proxy_id" gorm:"column:proxy_id"` // 代理ID
Port uint16 `json:"port" gorm:"column:port"` // 代理端口 Host string `json:"host" gorm:"column:host"` // 代理主机
EdgeID *int32 `json:"edge_id" gorm:"column:edge_id"` // 节点ID手动配置 Port uint16 `json:"port" gorm:"column:port"` // 代理端口
FilterISP *EdgeISP `json:"filter_isp" gorm:"column:filter_isp"` // 运营商过滤(自动配置):参考 edge.isp EdgeID *int32 `json:"edge_id,omitempty" gorm:"column:edge_id"` // 节点ID手动配置
FilterProv *string `json:"filter_prov" gorm:"column:filter_prov"` // 省份过滤(自动配置) EdgeRef *string `json:"edge_ref,omitempty" gorm:"column:edge_ref"` // 外部节点引用用于索引没有ID的外部非受控节点
FilterCity *string `json:"filter_city" gorm:"column:filter_city"` // 城市过滤(自动配置) FilterISP *EdgeISP `json:"filter_isp,omitempty" gorm:"column:filter_isp"` // 运营商过滤(自动配置):参考 edge.isp
IP *orm.Inet `json:"ip" gorm:"column:ip"` // 节点地址 FilterProv *string `json:"filter_prov,omitempty" gorm:"column:filter_prov"` // 省份过滤(自动配置)
Whitelists *string `json:"whitelists" gorm:"column:whitelists"` // IP白名单逗号分隔 FilterCity *string `json:"filter_city,omitempty" gorm:"column:filter_city"` // 城市过滤(自动配置)
Username *string `json:"username" gorm:"column:username"` // 用户名 IP *orm.Inet `json:"ip,omitempty" gorm:"column:ip"` // 节点地址
Password *string `json:"password" gorm:"column:password"` // 密码 Whitelists *string `json:"whitelists,omitempty" gorm:"column:whitelists"` // IP白名单逗号分隔
ExpiredAt time.Time `json:"expired_at" gorm:"column:expired_at"` // 过期时间 Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
Password *string `json:"password,omitempty" gorm:"column:password"` // 密码
ExpiredAt time.Time `json:"expired_at" gorm:"column:expired_at"` // 过期时间
User User `json:"user" gorm:"foreignKey:UserID"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Resource Resource `json:"resource" gorm:"foreignKey:ResourceID"` Resource *Resource `json:"resource,omitempty" gorm:"foreignKey:ResourceID"`
Proxy Proxy `json:"proxy" gorm:"foreignKey:ProxyID"` Proxy *Proxy `json:"proxy,omitempty" gorm:"foreignKey:ProxyID"`
Edge *Edge `json:"edge" gorm:"foreignKey:EdgeID"` Edge *Edge `json:"edge,omitempty" gorm:"foreignKey:EdgeID"`
} }

View File

@@ -7,14 +7,16 @@ import (
// Client 客户端表 // Client 客户端表
type Client struct { type Client struct {
core.Model core.Model
ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符 ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符
ClientSecret string `json:"client_secret" gorm:"column:client_secret"` // OAuth2客户端密钥 ClientSecret string `json:"-" gorm:"column:client_secret"` // OAuth2客户端密钥
RedirectURI *string `json:"redirect_uri" gorm:"column:redirect_uri"` // OAuth2 重定向URI RedirectURI *string `json:"redirect_uri,omitempty" gorm:"column:redirect_uri"` // OAuth2 重定向URI
Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范1-native2-browser3-web4-api Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范1-native2-browser3-web4-api
Name string `json:"name" gorm:"column:name"` // 名称 Name string `json:"name" gorm:"column:name"` // 名称
Icon *string `json:"icon" gorm:"column:icon"` // 图标URL Icon *string `json:"icon,omitempty" gorm:"column:icon"` // 图标URL
Status ClientStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常 Status ClientStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常
Type ClientType `json:"type" gorm:"column:type"` // 类型0-普通1-官方 Type ClientType `json:"type" gorm:"column:type"` // 类型0-普通1-官方
Permissions []*Permission `json:"permissions" gorm:"many2many:link_client_permission"`
} }
// ClientSpec 客户端安全规范枚举 // ClientSpec 客户端安全规范枚举

View File

@@ -10,20 +10,29 @@ import (
// Coupon 优惠券表 // Coupon 优惠券表
type Coupon struct { type Coupon struct {
core.Model core.Model
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID Name string `json:"name" gorm:"column:name"` // 优惠券名称
Code string `json:"code" gorm:"column:code"` // 优惠券代码 Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 优惠券金额
Remark *string `json:"remark" gorm:"column:remark"` // 优惠券备注 MinAmount decimal.Decimal `json:"min_amount" gorm:"column:min_amount"` // 最低消费金额
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 优惠券金额 Count int32 `json:"count" gorm:"column:count"` // 优惠券数量
MinAmount decimal.Decimal `json:"min_amount" gorm:"column:min_amount"` // 最低消费金额 Status CouponStatus `json:"status" gorm:"column:status"` // 优惠券状态0-禁用1-正常
Status CouponStatus `json:"status" gorm:"column:status"` // 优惠券状态0-未使用1-已使用2-已过期 ExpireType CouponExpireType `json:"expire_type" gorm:"column:expire_type"` // 过期类型0-不过期1-固定日期2-相对日期(从发放时间算起)
ExpireAt *time.Time `json:"expire_at" gorm:"column:expire_at"` // 过期时间 ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间,固定日期必填
ExpireIn *int `json:"expire_in,omitempty" gorm:"column:expire_in"` // 过期时长(天),相对日期必填
} }
// CouponStatus 优惠券状态枚举 // CouponStatus 优惠券使用状态枚举
type CouponStatus int type CouponStatus int
const ( const (
CouponStatusUnused CouponStatus = 0 // 未使 CouponStatusDisabled CouponStatus = 0 //
CouponStatusUsed CouponStatus = 1 // 已使用 CouponStatusEnabled CouponStatus = 1 // 正常
CouponStatusExpired CouponStatus = 2 // 已过期 )
// CouponExpireType 优惠券过期类型枚举
type CouponExpireType int
const (
CouponExpireTypeNever CouponExpireType = 0 // 不过期
CouponExpireTypeFixed CouponExpireType = 1 // 固定日期
CouponExpireTypeRelative CouponExpireType = 2 // 相对日期
) )

25
web/models/coupon_user.go Normal file
View File

@@ -0,0 +1,25 @@
package models
import "time"
// CouponUser 优惠券发放表
type CouponUser struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID
CouponID int32 `json:"coupon_id" gorm:"column:coupon_id"` // 优惠券ID
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
Status CouponStatus `json:"status" gorm:"column:status"` // 使用状态0-未使用1-已使用
ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间
UsedAt *time.Time `json:"used_at,omitempty" gorm:"column:used_at"` // 使用时间
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
Coupon *Coupon `json:"coupon,omitempty" gorm:"foreignKey:CouponID"`
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// CouponUserStatus 优惠券发放状态枚举
type CouponUserStatus int
const (
CouponUserStatusUnused CouponUserStatus = 0 // 未使用
CouponUserStatusUsed CouponUserStatus = 1 // 已使用
)

25
web/models/inquiry.go Normal file
View File

@@ -0,0 +1,25 @@
package models
import (
"platform/web/core"
)
// Inquiry 用户咨询表
type Inquiry struct {
core.Model
Company *string `json:"company,omitempty" gorm:"column:company"` // 公司名称
Name *string `json:"name,omitempty" gorm:"column:name"` // 联系人姓名
Phone *string `json:"phone,omitempty" gorm:"column:phone"` // 联系电话
Email *string `json:"email,omitempty" gorm:"column:email"` // 联系邮箱
Content *string `json:"content,omitempty" gorm:"column:content"` // 咨询内容
Status InquiryStatus `json:"status" gorm:"column:status"` // 处理状态0-待处理1-已处理
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
}
// InquiryStatus 咨询处理状态枚举
type InquiryStatus int
const (
InquiryStatusPending InquiryStatus = 0 // 待处理
InquiryStatusProcessed InquiryStatus = 1 // 已处理
)

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,10 +13,10 @@ type LogsLogin struct {
GrantType GrantType `json:"grant_type" gorm:"column:grant_type"` // 授权类型 GrantType GrantType `json:"grant_type" gorm:"column:grant_type"` // 授权类型
PasswordType PasswordType `json:"password_type" gorm:"column:password_type"` // 密码模式子授权类型 PasswordType PasswordType `json:"password_type" gorm:"column:password_type"` // 密码模式子授权类型
Success bool `json:"success" gorm:"column:success"` // 登录是否成功 Success bool `json:"success" gorm:"column:success"` // 登录是否成功
UserID *int32 `json:"user_id" gorm:"column:user_id"` // 用户ID UserID *int32 `json:"user_id,omitempty" gorm:"column:user_id"` // 用户ID
Time time.Time `json:"time" gorm:"column:time"` // 登录时间 Time time.Time `json:"time" gorm:"column:time"` // 登录时间
User *User `json:"user" gorm:"foreignKey:UserID"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
} }
// GrantType 授权类型枚举 // GrantType 授权类型枚举

View File

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

View File

@@ -12,9 +12,12 @@ type LogsUserUsage struct {
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
BatchNo string `json:"batch_no" gorm:"column:batch_no"` // 批次编号 BatchNo string `json:"batch_no" gorm:"column:batch_no"` // 批次编号
Count int32 `json:"count" gorm:"column:count"` // 数量 Count int32 `json:"count" gorm:"column:count"` // 数量
Prov *string `json:"prov" gorm:"column:prov"` // 省份 Prov *string `json:"prov,omitempty" gorm:"column:prov"` // 省份
City *string `json:"city" gorm:"column:city"` // 城市 City *string `json:"city,omitempty" gorm:"column:city"` // 城市
ISP *string `json:"isp" gorm:"column:isp"` // 运营商 ISP *string `json:"isp,omitempty" gorm:"column:isp"` // 运营商
IP orm.Inet `json:"ip" gorm:"column:ip"` // IP地址 IP orm.Inet `json:"ip" gorm:"column:ip"` // IP地址
Time time.Time `json:"time" gorm:"column:time"` // 提取时间 Time time.Time `json:"time" gorm:"column:time"` // 提取时间
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Resource *Resource `json:"resource,omitempty" gorm:"foreignKey:ResourceID"`
} }

View File

@@ -5,10 +5,11 @@ import "platform/web/core"
// Permission 权限表 // Permission 权限表
type Permission struct { type Permission struct {
core.Model core.Model
ParentID *int32 `json:"parent_id" gorm:"column:parent_id"` // 父权限ID ParentID *int32 `json:"parent_id,omitempty" gorm:"column:parent_id"` // 父权限ID
Name string `json:"name" gorm:"column:name"` // 权限名称 Name string `json:"name" gorm:"column:name"` // 权限名称
Description *string `json:"description" gorm:"column:description"` // 权限描述 Description *string `json:"description,omitempty" gorm:"column:description"` // 权限描述
Sort int `json:"sort" gorm:"column:sort"` // 排序
Parent *Permission `json:"parent" gorm:"foreignKey:ParentID"` Parent *Permission `json:"parent,omitempty" gorm:"foreignKey:ParentID"`
Children []*Permission `json:"children" gorm:"foreignKey:ParentID"` Children []*Permission `json:"children,omitempty" gorm:"foreignKey:ParentID"`
} }

View File

@@ -7,11 +7,13 @@ import (
// Product 产品表 // Product 产品表
type Product struct { type Product struct {
core.Model core.Model
Code string `json:"code" gorm:"column:code"` // 产品代码 Code string `json:"code" gorm:"column:code"` // 产品代码
Name string `json:"name" gorm:"column:name"` // 产品名称 Name string `json:"name" gorm:"column:name"` // 产品名称
Description *string `json:"description" gorm:"column:description"` // 产品描述 Description *string `json:"description,omitempty" gorm:"column:description"` // 产品描述
Sort int32 `json:"sort" gorm:"column:sort"` // 排序 Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态0-禁用1-正常 Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态0-禁用1-正常
Skus []*ProductSku `json:"skus,omitempty" gorm:"foreignKey:ProductID"` // 产品包含的SKU列表
} }
// ProductStatus 产品状态枚举 // ProductStatus 产品状态枚举

View File

@@ -0,0 +1,19 @@
package models
import (
"platform/web/core"
"github.com/shopspring/decimal"
)
// ProductDiscount 产品折扣表
type ProductDiscount struct {
core.Model
Name string `json:"name" gorm:"column:name"` // 产品名称
Discount int32 `json:"discount" gorm:"column:discount"` // 产品折扣
}
func (pd ProductDiscount) Rate() decimal.Decimal {
return decimal.NewFromInt32(pd.Discount).
Div(decimal.NewFromInt32(100))
}

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

@@ -0,0 +1,31 @@
package models
import (
"platform/web/core"
"github.com/shopspring/decimal"
)
// ProductSku 产品SKU表
type ProductSku struct {
core.Model
ProductID int32 `json:"product_id" gorm:"column:product_id"` // 产品ID
DiscountId int32 `json:"discount_id" gorm:"column:discount_id"` // 折扣0 - 1 的小数,表示 xx 折
Code string `json:"code" gorm:"column:code"` // SSKU 代码:格式为 key=value,key=value,...其中key:value 是 SKU 的属性,多个属性用逗号分隔
Name string `json:"name" gorm:"column:name"` // SKU 可读名称
Price decimal.Decimal `json:"price" gorm:"column:price"` // 定价
PriceMin decimal.Decimal `json:"price_min" gorm:"column:price_min"` // 最低价格
Status SkuStatus `json:"status" gorm:"column:status"` // SKU 状态0-禁用1-正常
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
}
// SkuStatus SKU 状态
type SkuStatus int32
const (
SkuStatusDisabled SkuStatus = 0 // 禁用
SkuStatusEnabled SkuStatus = 1 // 正常
)

View File

@@ -0,0 +1,19 @@
package models
import (
"time"
)
// ProductSkuUser 用户产品SKU表
type ProductSkuUser struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"`
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ProductSkuID int32 `json:"product_sku_id" gorm:"column:product_sku_id"` // 产品SKU ID
DiscountId int32 `json:"discount_id" gorm:"column:discount_id"` // 折扣ID
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
ProductSku *ProductSku `json:"product_sku,omitempty" gorm:"foreignKey:ProductSkuID"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
}

View File

@@ -10,15 +10,16 @@ import (
// Proxy 代理服务表 // Proxy 代理服务表
type Proxy struct { type Proxy struct {
core.Model core.Model
Version int32 `json:"version" gorm:"column:version"` // 代理服务版本 Version int32 `json:"version" gorm:"column:version"` // 代理服务版本
Mac string `json:"mac" gorm:"column:mac"` // 代理服务名称 Mac string `json:"mac" gorm:"column:mac"` // 代理服务名称
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // 代理服务地址 IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // 代理服务地址
Secret *string `json:"secret" gorm:"column:secret"` // 代理服务密钥 Host *string `json:"host,omitempty" gorm:"column:host"` // 代理服务域名
Type ProxyType `json:"type" gorm:"column:type"` // 代理服务类型1-自有2-白银 Secret *string `json:"secret,omitempty" gorm:"column:secret"` // 代理服务密钥
Status ProxyStatus `json:"status" gorm:"column:status"` // 代理服务状态0-离线1-在线 Type ProxyType `json:"type" gorm:"column:type"` // 代理服务类型1-自有2-白银
Meta *datatypes.JSONType[any] `json:"meta" gorm:"column:meta"` // 代理服务元信息 Status ProxyStatus `json:"status" gorm:"column:status"` // 代理服务状态0-离线1-在线
Meta *datatypes.JSONType[any] `json:"meta,omitempty" gorm:"column:meta"` // 代理服务元信息
Channels []Channel `json:"channels" gorm:"foreignkey:ProxyID"` Channels []Channel `json:"channels,omitempty" gorm:"foreignkey:ProxyID"`
} }
// ProxyType 代理服务类型枚举 // ProxyType 代理服务类型枚举

View File

@@ -9,11 +9,11 @@ import (
// Refund 退款记录表 // Refund 退款记录表
type Refund struct { type Refund struct {
core.Model core.Model
TradeID int32 `json:"trade_id" gorm:"column:trade_id"` // 订单ID TradeID int32 `json:"trade_id" gorm:"column:trade_id"` // 订单ID
ProductID *int32 `json:"product_id" gorm:"column:product_id"` // 产品ID ProductID *int32 `json:"product_id,omitempty" gorm:"column:product_id"` // 产品ID
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 退款金额 Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 退款金额
Reason *string `json:"reason" gorm:"column:reason"` // 退款原因 Reason *string `json:"reason,omitempty" gorm:"column:reason"` // 退款原因
Status RefundStatus `json:"status" gorm:"column:status"` // 退款状态0-待处理1-已退款2-已拒绝 Status RefundStatus `json:"status" gorm:"column:status"` // 退款状态0-待处理1-已退款2-已拒绝
} }
// RefundStatus 退款状态枚举 // RefundStatus 退款状态枚举

View File

@@ -7,14 +7,16 @@ import (
// Resource 套餐表 // Resource 套餐表
type Resource struct { type Resource struct {
core.Model core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
ResourceNo *string `json:"resource_no" gorm:"column:resource_no"` // 套餐编号 ResourceNo *string `json:"resource_no,omitempty" gorm:"column:resource_no"` // 套餐编号
Active bool `json:"active" gorm:"column:active"` // 套餐状态 Active bool `json:"active" gorm:"column:active"` // 套餐状态
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型1-短效动态2-长效动态 Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型1-短效动态2-长效动态
Code string `json:"code" gorm:"column:code"` // 产品编码
User User `json:"user" gorm:"foreignKey:UserID"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Short *ResourceShort `json:"short" gorm:"foreignKey:ResourceID"` Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`
Long *ResourceLong `json:"long" gorm:"foreignKey:ResourceID"` Long *ResourceLong `json:"long,omitempty" gorm:"foreignKey:ResourceID"`
Product *Product `json:"product,omitempty" gorm:"foreignKey:Code;references:Code"`
} }
// ResourceType 套餐类型枚举 // ResourceType 套餐类型枚举
@@ -25,10 +27,32 @@ const (
ResourceTypeLong ResourceType = 2 // 长效动态 ResourceTypeLong ResourceType = 2 // 长效动态
) )
// ResourceLongType 套餐计费模式枚举 func (t ResourceType) Code() string {
switch t {
case ResourceTypeShort:
return "short"
case ResourceTypeLong:
return "long"
default:
return "unknown"
}
}
// ResourceMode 套餐计费模式枚举
type ResourceMode int type ResourceMode int
const ( const (
ResourceModeTime ResourceMode = 1 // 包时 ResourceModeTime ResourceMode = 1 // 包时
ResourceModeQuota ResourceMode = 2 // 包量 ResourceModeQuota ResourceMode = 2 // 包量
) )
func (m ResourceMode) Code() string {
switch m {
case ResourceModeTime:
return "time"
case ResourceModeQuota:
return "quota"
default:
return "unknown"
}
}

View File

@@ -6,14 +6,16 @@ import (
// ResourceLong 长效动态套餐表 // ResourceLong 长效动态套餐表
type ResourceLong struct { type ResourceLong struct {
ID int32 `json:"id" gorm:"column:id"` // ID ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量 Code string `json:"code" gorm:"column:code"` // 套餐编码
Live int32 `json:"live" gorm:"column:live"` // 可用时长() Live int32 `json:"live" gorm:"column:live"` // 可用时长(小时)
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间 Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量 Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
Used int32 `json:"used" gorm:"column:used"` // 已用数量 ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 套餐过期时间,包时模式可用
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制 Used int32 `json:"used" gorm:"column:used"` // 总用量
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数 Daily int32 `json:"daily" gorm:"column:daily"` // 当日用
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间 LastAt *time.Time `json:"last_at,omitempty" gorm:"column:last_at"` // 最后使用时间
Sku *ProductSku `json:"sku,omitempty" gorm:"foreignKey:Code;references:Code"`
} }

View File

@@ -6,14 +6,16 @@ import (
// ResourceShort 短效动态套餐表 // ResourceShort 短效动态套餐表
type ResourceShort struct { type ResourceShort struct {
ID int32 `json:"id" gorm:"column:id"` // ID ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量 Code string `json:"code" gorm:"column:code"` // 套餐编码
Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒) Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒)
Expire *time.Time `json:"expire" gorm:"column:expire"` // 过期时间 Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Quota *int32 `json:"quota" gorm:"column:quota"` // 配额数量 Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
Used int32 `json:"used" gorm:"column:used"` // 已用数量 ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 套餐过期时间,包时模式可用
DailyLimit int32 `json:"daily_limit" gorm:"column:daily_limit"` // 每日限制 Used int32 `json:"used" gorm:"column:used"` // 总用量
DailyUsed int32 `json:"daily_used" gorm:"column:daily_used"` // 今日已用数 Daily int32 `json:"daily" gorm:"column:daily"` // 当日用
DailyLast *time.Time `json:"daily_last" gorm:"column:daily_last"` // 今日最后使用时间 LastAt *time.Time `json:"last_at,omitempty" gorm:"column:last_at"` // 最后使用时间
Sku *ProductSku `json:"sku,omitempty" gorm:"foreignKey:Code;references:Code"`
} }

View File

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

View File

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

View File

@@ -11,25 +11,29 @@ import (
// User 用户表 // User 用户表
type User struct { type User struct {
core.Model core.Model
AdminID *int32 `json:"admin_id" gorm:"column:admin_id"` // 管理员ID AdminID *int32 `json:"admin_id,omitempty" gorm:"column:admin_id"` // 管理员ID
Phone string `json:"phone" gorm:"column:phone"` // 手机号码 DiscountID *int32 `json:"discount_id,omitempty" gorm:"column:discount_id"` // 折扣ID
Username *string `json:"username" gorm:"column:username"` // 用户名 Phone string `json:"phone" gorm:"column:phone"` // 手机号码
Email *string `json:"email" gorm:"column:email"` // 邮箱 Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
Password *string `json:"password" gorm:"column:password"` // 用户密码 Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
Name *string `json:"name" gorm:"column:name"` // 真实姓名 Password *string `json:"-" gorm:"column:password"` // 用户密码
Avatar *string `json:"avatar" gorm:"column:avatar"` // 头像URL Source *UserSource `json:"source,omitempty" gorm:"column:source"` // 用户来源0-官网注册1-管理员添加2-代理商注册3-代理商添加
Status UserStatus `json:"status" gorm:"column:status"` // 用户状态0-禁用1-正常 Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
Balance decimal.Decimal `json:"balance" gorm:"column:balance"` // 账户余额 Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
IDType UserIDType `json:"id_type" gorm:"column:id_type"` // 认证类型0-未认证1-个人认证2-企业认证 Status UserStatus `json:"status" gorm:"column:status"` // 用户状态0-禁用1-正常
IDNo *string `json:"id_no" gorm:"column:id_no"` // 身份证号或营业执照号 Balance decimal.Decimal `json:"balance" gorm:"column:balance"` // 账户余额
IDToken *string `json:"id_token" gorm:"column:id_token"` // 身份验证标识 IDType UserIDType `json:"id_type" gorm:"column:id_type"` // 认证类型0-未认证1-个人认证2-企业认证
ContactQQ *string `json:"contact_qq" gorm:"column:contact_qq"` // QQ联系方式 IDNo *string `json:"id_no,omitempty" gorm:"column:id_no"` // 身份证号或营业执照号
ContactWechat *string `json:"contact_wechat" gorm:"column:contact_wechat"` // 微信联系方式 IDToken *string `json:"id_token,omitempty" gorm:"column:id_token"` // 身份验证标识
LastLogin *time.Time `json:"last_login" gorm:"column:last_login"` // 最后登录时间 ContactQQ *string `json:"contact_qq,omitempty" gorm:"column:contact_qq"` // QQ联系方式
LastLoginIP *orm.Inet `json:"last_login_ip" gorm:"column:last_login_ip"` // 最后登录地址 ContactWechat *string `json:"contact_wechat,omitempty" gorm:"column:contact_wechat"` // 微信联系方式
LastLoginUA *string `json:"last_login_ua" gorm:"column:last_login_ua"` // 最后登录代理 LastLogin *time.Time `json:"last_login,omitempty" gorm:"column:last_login"` // 最后登录时间
LastLoginIP *orm.Inet `json:"last_login_ip,omitempty" gorm:"column:last_login_ip"` // 最后登录地址
LastLoginUA *string `json:"last_login_ua,omitempty" gorm:"column:last_login_ua"` // 最后登录代理
Admin Admin `json:"admin" gorm:"foreignKey:AdminID"` Admin *Admin `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
Roles []*UserRole `json:"roles" gorm:"many2many:link_user_role"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountID"`
} }
// UserStatus 用户状态枚举 // UserStatus 用户状态枚举
@@ -48,3 +52,13 @@ const (
UserIDTypePersonal UserIDType = 1 // 个人认证 UserIDTypePersonal UserIDType = 1 // 个人认证
UserIDTypeEnterprise UserIDType = 2 // 企业认证 UserIDTypeEnterprise UserIDType = 2 // 企业认证
) )
// UserSource 用户来源枚举
type UserSource int
const (
UserSourceReg UserSource = 0 // 官网注册
UserSourceAdd UserSource = 1 // 管理员添加
UserSourceAgentReg UserSource = 2 // 代理商注册
UserSourceAgentAdd UserSource = 3 // 代理商添加
)

View File

@@ -7,8 +7,10 @@ import (
// UserRole 用户角色表 // UserRole 用户角色表
type UserRole struct { type UserRole struct {
core.Model core.Model
Name string `json:"name" gorm:"column:name"` // 角色名称 Name string `json:"name" gorm:"column:name"` // 角色名称
Description *string `json:"description" gorm:"column:description"` // 角色描述 Description *string `json:"description,omitempty" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活 Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序 Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Permissions []*Permission `json:"permissions" gorm:"many2many:link_user_role_permission"`
} }

View File

@@ -8,7 +8,7 @@ import (
// Whitelist 白名单表 // Whitelist 白名单表
type Whitelist struct { type Whitelist struct {
core.Model core.Model
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // IP地址 IP orm.Inet `json:"ip" gorm:"column:ip;not null"` // IP地址
Remark *string `json:"remark" gorm:"column:remark"` // 备注 Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
} }

View File

@@ -41,6 +41,33 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
_admin.LastLogin = field.NewTime(tableName, "last_login") _admin.LastLogin = field.NewTime(tableName, "last_login")
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip") _admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua") _admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
_admin.Lock = field.NewBool(tableName, "lock")
_admin.Roles = adminManyToManyRoles{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Roles.Permissions.Children", "models.Permission"),
},
},
}
_admin.fillFieldMap() _admin.fillFieldMap()
@@ -65,6 +92,8 @@ type admin struct {
LastLogin field.Time LastLogin field.Time
LastLoginIP field.Field LastLoginIP field.Field
LastLoginUA field.String LastLoginUA field.String
Lock field.Bool
Roles adminManyToManyRoles
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -95,6 +124,7 @@ func (a *admin) updateTableName(table string) *admin {
a.LastLogin = field.NewTime(table, "last_login") a.LastLogin = field.NewTime(table, "last_login")
a.LastLoginIP = field.NewField(table, "last_login_ip") a.LastLoginIP = field.NewField(table, "last_login_ip")
a.LastLoginUA = field.NewString(table, "last_login_ua") a.LastLoginUA = field.NewString(table, "last_login_ua")
a.Lock = field.NewBool(table, "lock")
a.fillFieldMap() a.fillFieldMap()
@@ -111,7 +141,7 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (a *admin) fillFieldMap() { func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 14) a.fieldMap = make(map[string]field.Expr, 16)
a.fieldMap["id"] = a.ID a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt a.fieldMap["updated_at"] = a.UpdatedAt
@@ -126,18 +156,114 @@ func (a *admin) fillFieldMap() {
a.fieldMap["last_login"] = a.LastLogin a.fieldMap["last_login"] = a.LastLogin
a.fieldMap["last_login_ip"] = a.LastLoginIP a.fieldMap["last_login_ip"] = a.LastLoginIP
a.fieldMap["last_login_ua"] = a.LastLoginUA a.fieldMap["last_login_ua"] = a.LastLoginUA
a.fieldMap["lock"] = a.Lock
} }
func (a admin) clone(db *gorm.DB) admin { func (a admin) clone(db *gorm.DB) admin {
a.adminDo.ReplaceConnPool(db.Statement.ConnPool) a.adminDo.ReplaceConnPool(db.Statement.ConnPool)
a.Roles.db = db.Session(&gorm.Session{Initialized: true})
a.Roles.db.Statement.ConnPool = db.Statement.ConnPool
return a return a
} }
func (a admin) replaceDB(db *gorm.DB) admin { func (a admin) replaceDB(db *gorm.DB) admin {
a.adminDo.ReplaceDB(db) a.adminDo.ReplaceDB(db)
a.Roles.db = db.Session(&gorm.Session{})
return a return a
} }
type adminManyToManyRoles struct {
db *gorm.DB
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
func (a adminManyToManyRoles) Where(conds ...field.Expr) *adminManyToManyRoles {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a adminManyToManyRoles) WithContext(ctx context.Context) *adminManyToManyRoles {
a.db = a.db.WithContext(ctx)
return &a
}
func (a adminManyToManyRoles) Session(session *gorm.Session) *adminManyToManyRoles {
a.db = a.db.Session(session)
return &a
}
func (a adminManyToManyRoles) Model(m *models.Admin) *adminManyToManyRolesTx {
return &adminManyToManyRolesTx{a.db.Model(m).Association(a.Name())}
}
func (a adminManyToManyRoles) Unscoped() *adminManyToManyRoles {
a.db = a.db.Unscoped()
return &a
}
type adminManyToManyRolesTx struct{ tx *gorm.Association }
func (a adminManyToManyRolesTx) Find() (result []*models.AdminRole, err error) {
return result, a.tx.Find(&result)
}
func (a adminManyToManyRolesTx) Append(values ...*models.AdminRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a adminManyToManyRolesTx) Replace(values ...*models.AdminRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a adminManyToManyRolesTx) Delete(values ...*models.AdminRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a adminManyToManyRolesTx) Clear() error {
return a.tx.Clear()
}
func (a adminManyToManyRolesTx) Count() int64 {
return a.tx.Count()
}
func (a adminManyToManyRolesTx) Unscoped() *adminManyToManyRolesTx {
a.tx = a.tx.Unscoped()
return &a
}
type adminDo struct{ gen.DO } type adminDo struct{ gen.DO }
func (a adminDo) Debug() *adminDo { func (a adminDo) Debug() *adminDo {

View File

@@ -35,6 +35,21 @@ func newAdminRole(db *gorm.DB, opts ...gen.DOOption) adminRole {
_adminRole.Description = field.NewString(tableName, "description") _adminRole.Description = field.NewString(tableName, "description")
_adminRole.Active = field.NewBool(tableName, "active") _adminRole.Active = field.NewBool(tableName, "active")
_adminRole.Sort = field.NewInt32(tableName, "sort") _adminRole.Sort = field.NewInt32(tableName, "sort")
_adminRole.Permissions = adminRoleManyToManyPermissions{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Permissions.Children", "models.Permission"),
},
}
_adminRole.fillFieldMap() _adminRole.fillFieldMap()
@@ -53,6 +68,7 @@ type adminRole struct {
Description field.String Description field.String
Active field.Bool Active field.Bool
Sort field.Int32 Sort field.Int32
Permissions adminRoleManyToManyPermissions
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -93,7 +109,7 @@ func (a *adminRole) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (a *adminRole) fillFieldMap() { func (a *adminRole) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 8) a.fieldMap = make(map[string]field.Expr, 9)
a.fieldMap["id"] = a.ID a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt a.fieldMap["updated_at"] = a.UpdatedAt
@@ -102,18 +118,110 @@ func (a *adminRole) fillFieldMap() {
a.fieldMap["description"] = a.Description a.fieldMap["description"] = a.Description
a.fieldMap["active"] = a.Active a.fieldMap["active"] = a.Active
a.fieldMap["sort"] = a.Sort a.fieldMap["sort"] = a.Sort
} }
func (a adminRole) clone(db *gorm.DB) adminRole { func (a adminRole) clone(db *gorm.DB) adminRole {
a.adminRoleDo.ReplaceConnPool(db.Statement.ConnPool) a.adminRoleDo.ReplaceConnPool(db.Statement.ConnPool)
a.Permissions.db = db.Session(&gorm.Session{Initialized: true})
a.Permissions.db.Statement.ConnPool = db.Statement.ConnPool
return a return a
} }
func (a adminRole) replaceDB(db *gorm.DB) adminRole { func (a adminRole) replaceDB(db *gorm.DB) adminRole {
a.adminRoleDo.ReplaceDB(db) a.adminRoleDo.ReplaceDB(db)
a.Permissions.db = db.Session(&gorm.Session{})
return a return a
} }
type adminRoleManyToManyPermissions struct {
db *gorm.DB
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
func (a adminRoleManyToManyPermissions) Where(conds ...field.Expr) *adminRoleManyToManyPermissions {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a adminRoleManyToManyPermissions) WithContext(ctx context.Context) *adminRoleManyToManyPermissions {
a.db = a.db.WithContext(ctx)
return &a
}
func (a adminRoleManyToManyPermissions) Session(session *gorm.Session) *adminRoleManyToManyPermissions {
a.db = a.db.Session(session)
return &a
}
func (a adminRoleManyToManyPermissions) Model(m *models.AdminRole) *adminRoleManyToManyPermissionsTx {
return &adminRoleManyToManyPermissionsTx{a.db.Model(m).Association(a.Name())}
}
func (a adminRoleManyToManyPermissions) Unscoped() *adminRoleManyToManyPermissions {
a.db = a.db.Unscoped()
return &a
}
type adminRoleManyToManyPermissionsTx struct{ tx *gorm.Association }
func (a adminRoleManyToManyPermissionsTx) Find() (result []*models.Permission, err error) {
return result, a.tx.Find(&result)
}
func (a adminRoleManyToManyPermissionsTx) Append(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a adminRoleManyToManyPermissionsTx) Replace(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a adminRoleManyToManyPermissionsTx) Delete(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a adminRoleManyToManyPermissionsTx) Clear() error {
return a.tx.Clear()
}
func (a adminRoleManyToManyPermissionsTx) Count() int64 {
return a.tx.Count()
}
func (a adminRoleManyToManyPermissionsTx) Unscoped() *adminRoleManyToManyPermissionsTx {
a.tx = a.tx.Unscoped()
return &a
}
type adminRoleDo struct{ gen.DO } type adminRoleDo struct{ gen.DO }
func (a adminRoleDo) Debug() *adminRoleDo { func (a adminRoleDo) Debug() *adminRoleDo {

View File

@@ -0,0 +1,921 @@
// 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 newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
_balanceActivity := balanceActivity{}
_balanceActivity.balanceActivityDo.UseDB(db, opts...)
_balanceActivity.balanceActivityDo.UseModel(&models.BalanceActivity{})
tableName := _balanceActivity.balanceActivityDo.TableName()
_balanceActivity.ALL = field.NewAsterisk(tableName)
_balanceActivity.ID = field.NewInt32(tableName, "id")
_balanceActivity.UserID = field.NewInt32(tableName, "user_id")
_balanceActivity.BillID = field.NewInt32(tableName, "bill_id")
_balanceActivity.AdminID = field.NewInt32(tableName, "admin_id")
_balanceActivity.Amount = field.NewString(tableName, "amount")
_balanceActivity.BalancePrev = field.NewString(tableName, "balance_prev")
_balanceActivity.BalanceCurr = field.NewString(tableName, "balance_curr")
_balanceActivity.Remark = field.NewString(tableName, "remark")
_balanceActivity.CreatedAt = field.NewTime(tableName, "created_at")
_balanceActivity.User = balanceActivityBelongsToUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("User", "models.User"),
Admin: struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}{
RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
},
},
}
_balanceActivity.Bill = balanceActivityBelongsToBill{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Bill", "models.Bill"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.User", "models.User"),
},
Trade: struct {
field.RelationField
User struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Trade", "models.Trade"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Trade.User", "models.User"),
},
},
Resource: struct {
field.RelationField
User struct {
field.RelationField
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource", "models.Resource"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.User", "models.User"),
},
Short: struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Bill.Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
},
Long: struct {
field.RelationField
Sku struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource.Long", "models.ResourceLong"),
Sku: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Long.Sku", "models.ProductSku"),
},
},
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Product", "models.Product"),
},
},
Refund: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Refund", "models.Refund"),
},
CouponUser: struct {
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.CouponUser", "models.CouponUser"),
Coupon: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.CouponUser.Coupon", "models.Coupon"),
},
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.CouponUser.User", "models.User"),
},
},
}
_balanceActivity.Admin = balanceActivityBelongsToAdmin{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Admin", "models.Admin"),
}
_balanceActivity.fillFieldMap()
return _balanceActivity
}
type balanceActivity struct {
balanceActivityDo
ALL field.Asterisk
ID field.Int32
UserID field.Int32
BillID field.Int32
AdminID field.Int32
Amount field.String
BalancePrev field.String
BalanceCurr field.String
Remark field.String
CreatedAt field.Time
User balanceActivityBelongsToUser
Bill balanceActivityBelongsToBill
Admin balanceActivityBelongsToAdmin
fieldMap map[string]field.Expr
}
func (b balanceActivity) Table(newTableName string) *balanceActivity {
b.balanceActivityDo.UseTable(newTableName)
return b.updateTableName(newTableName)
}
func (b balanceActivity) As(alias string) *balanceActivity {
b.balanceActivityDo.DO = *(b.balanceActivityDo.As(alias).(*gen.DO))
return b.updateTableName(alias)
}
func (b *balanceActivity) updateTableName(table string) *balanceActivity {
b.ALL = field.NewAsterisk(table)
b.ID = field.NewInt32(table, "id")
b.UserID = field.NewInt32(table, "user_id")
b.BillID = field.NewInt32(table, "bill_id")
b.AdminID = field.NewInt32(table, "admin_id")
b.Amount = field.NewString(table, "amount")
b.BalancePrev = field.NewString(table, "balance_prev")
b.BalanceCurr = field.NewString(table, "balance_curr")
b.Remark = field.NewString(table, "remark")
b.CreatedAt = field.NewTime(table, "created_at")
b.fillFieldMap()
return b
}
func (b *balanceActivity) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := b.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (b *balanceActivity) fillFieldMap() {
b.fieldMap = make(map[string]field.Expr, 12)
b.fieldMap["id"] = b.ID
b.fieldMap["user_id"] = b.UserID
b.fieldMap["bill_id"] = b.BillID
b.fieldMap["admin_id"] = b.AdminID
b.fieldMap["amount"] = b.Amount
b.fieldMap["balance_prev"] = b.BalancePrev
b.fieldMap["balance_curr"] = b.BalanceCurr
b.fieldMap["remark"] = b.Remark
b.fieldMap["created_at"] = b.CreatedAt
}
func (b balanceActivity) clone(db *gorm.DB) balanceActivity {
b.balanceActivityDo.ReplaceConnPool(db.Statement.ConnPool)
b.User.db = db.Session(&gorm.Session{Initialized: true})
b.User.db.Statement.ConnPool = db.Statement.ConnPool
b.Bill.db = db.Session(&gorm.Session{Initialized: true})
b.Bill.db.Statement.ConnPool = db.Statement.ConnPool
b.Admin.db = db.Session(&gorm.Session{Initialized: true})
b.Admin.db.Statement.ConnPool = db.Statement.ConnPool
return b
}
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
b.balanceActivityDo.ReplaceDB(db)
b.User.db = db.Session(&gorm.Session{})
b.Bill.db = db.Session(&gorm.Session{})
b.Admin.db = db.Session(&gorm.Session{})
return b
}
type balanceActivityBelongsToUser struct {
db *gorm.DB
field.RelationField
Admin struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}
Discount struct {
field.RelationField
}
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
}
}
func (a balanceActivityBelongsToUser) Where(conds ...field.Expr) *balanceActivityBelongsToUser {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a balanceActivityBelongsToUser) WithContext(ctx context.Context) *balanceActivityBelongsToUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityBelongsToUser) Session(session *gorm.Session) *balanceActivityBelongsToUser {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityBelongsToUser) Model(m *models.BalanceActivity) *balanceActivityBelongsToUserTx {
return &balanceActivityBelongsToUserTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityBelongsToUser) Unscoped() *balanceActivityBelongsToUser {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityBelongsToUserTx struct{ tx *gorm.Association }
func (a balanceActivityBelongsToUserTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityBelongsToUserTx) Append(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a balanceActivityBelongsToUserTx) Replace(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a balanceActivityBelongsToUserTx) Delete(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a balanceActivityBelongsToUserTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityBelongsToUserTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityBelongsToUserTx) Unscoped() *balanceActivityBelongsToUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityBelongsToBill struct {
db *gorm.DB
field.RelationField
User struct {
field.RelationField
}
Trade struct {
field.RelationField
User struct {
field.RelationField
}
}
Resource struct {
field.RelationField
User struct {
field.RelationField
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}
Refund struct {
field.RelationField
}
CouponUser struct {
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}
}
func (a balanceActivityBelongsToBill) Where(conds ...field.Expr) *balanceActivityBelongsToBill {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a balanceActivityBelongsToBill) WithContext(ctx context.Context) *balanceActivityBelongsToBill {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityBelongsToBill) Session(session *gorm.Session) *balanceActivityBelongsToBill {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityBelongsToBill) Model(m *models.BalanceActivity) *balanceActivityBelongsToBillTx {
return &balanceActivityBelongsToBillTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityBelongsToBill) Unscoped() *balanceActivityBelongsToBill {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityBelongsToBillTx struct{ tx *gorm.Association }
func (a balanceActivityBelongsToBillTx) Find() (result *models.Bill, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityBelongsToBillTx) Append(values ...*models.Bill) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a balanceActivityBelongsToBillTx) Replace(values ...*models.Bill) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a balanceActivityBelongsToBillTx) Delete(values ...*models.Bill) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a balanceActivityBelongsToBillTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityBelongsToBillTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityBelongsToBillTx) Unscoped() *balanceActivityBelongsToBillTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityBelongsToAdmin struct {
db *gorm.DB
field.RelationField
}
func (a balanceActivityBelongsToAdmin) Where(conds ...field.Expr) *balanceActivityBelongsToAdmin {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a balanceActivityBelongsToAdmin) WithContext(ctx context.Context) *balanceActivityBelongsToAdmin {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityBelongsToAdmin) Session(session *gorm.Session) *balanceActivityBelongsToAdmin {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityBelongsToAdmin) Model(m *models.BalanceActivity) *balanceActivityBelongsToAdminTx {
return &balanceActivityBelongsToAdminTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityBelongsToAdmin) Unscoped() *balanceActivityBelongsToAdmin {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityBelongsToAdminTx struct{ tx *gorm.Association }
func (a balanceActivityBelongsToAdminTx) Find() (result *models.Admin, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityBelongsToAdminTx) Append(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Replace(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Delete(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityBelongsToAdminTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityBelongsToAdminTx) Unscoped() *balanceActivityBelongsToAdminTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityDo struct{ gen.DO }
func (b balanceActivityDo) Debug() *balanceActivityDo {
return b.withDO(b.DO.Debug())
}
func (b balanceActivityDo) WithContext(ctx context.Context) *balanceActivityDo {
return b.withDO(b.DO.WithContext(ctx))
}
func (b balanceActivityDo) ReadDB() *balanceActivityDo {
return b.Clauses(dbresolver.Read)
}
func (b balanceActivityDo) WriteDB() *balanceActivityDo {
return b.Clauses(dbresolver.Write)
}
func (b balanceActivityDo) Session(config *gorm.Session) *balanceActivityDo {
return b.withDO(b.DO.Session(config))
}
func (b balanceActivityDo) Clauses(conds ...clause.Expression) *balanceActivityDo {
return b.withDO(b.DO.Clauses(conds...))
}
func (b balanceActivityDo) Returning(value interface{}, columns ...string) *balanceActivityDo {
return b.withDO(b.DO.Returning(value, columns...))
}
func (b balanceActivityDo) Not(conds ...gen.Condition) *balanceActivityDo {
return b.withDO(b.DO.Not(conds...))
}
func (b balanceActivityDo) Or(conds ...gen.Condition) *balanceActivityDo {
return b.withDO(b.DO.Or(conds...))
}
func (b balanceActivityDo) Select(conds ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Select(conds...))
}
func (b balanceActivityDo) Where(conds ...gen.Condition) *balanceActivityDo {
return b.withDO(b.DO.Where(conds...))
}
func (b balanceActivityDo) Order(conds ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Order(conds...))
}
func (b balanceActivityDo) Distinct(cols ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Distinct(cols...))
}
func (b balanceActivityDo) Omit(cols ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Omit(cols...))
}
func (b balanceActivityDo) Join(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Join(table, on...))
}
func (b balanceActivityDo) LeftJoin(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.LeftJoin(table, on...))
}
func (b balanceActivityDo) RightJoin(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.RightJoin(table, on...))
}
func (b balanceActivityDo) Group(cols ...field.Expr) *balanceActivityDo {
return b.withDO(b.DO.Group(cols...))
}
func (b balanceActivityDo) Having(conds ...gen.Condition) *balanceActivityDo {
return b.withDO(b.DO.Having(conds...))
}
func (b balanceActivityDo) Limit(limit int) *balanceActivityDo {
return b.withDO(b.DO.Limit(limit))
}
func (b balanceActivityDo) Offset(offset int) *balanceActivityDo {
return b.withDO(b.DO.Offset(offset))
}
func (b balanceActivityDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *balanceActivityDo {
return b.withDO(b.DO.Scopes(funcs...))
}
func (b balanceActivityDo) Unscoped() *balanceActivityDo {
return b.withDO(b.DO.Unscoped())
}
func (b balanceActivityDo) Create(values ...*models.BalanceActivity) error {
if len(values) == 0 {
return nil
}
return b.DO.Create(values)
}
func (b balanceActivityDo) CreateInBatches(values []*models.BalanceActivity, batchSize int) error {
return b.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 (b balanceActivityDo) Save(values ...*models.BalanceActivity) error {
if len(values) == 0 {
return nil
}
return b.DO.Save(values)
}
func (b balanceActivityDo) First() (*models.BalanceActivity, error) {
if result, err := b.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.BalanceActivity), nil
}
}
func (b balanceActivityDo) Take() (*models.BalanceActivity, error) {
if result, err := b.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.BalanceActivity), nil
}
}
func (b balanceActivityDo) Last() (*models.BalanceActivity, error) {
if result, err := b.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.BalanceActivity), nil
}
}
func (b balanceActivityDo) Find() ([]*models.BalanceActivity, error) {
result, err := b.DO.Find()
return result.([]*models.BalanceActivity), err
}
func (b balanceActivityDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.BalanceActivity, err error) {
buf := make([]*models.BalanceActivity, 0, batchSize)
err = b.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 (b balanceActivityDo) FindInBatches(result *[]*models.BalanceActivity, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return b.DO.FindInBatches(result, batchSize, fc)
}
func (b balanceActivityDo) Attrs(attrs ...field.AssignExpr) *balanceActivityDo {
return b.withDO(b.DO.Attrs(attrs...))
}
func (b balanceActivityDo) Assign(attrs ...field.AssignExpr) *balanceActivityDo {
return b.withDO(b.DO.Assign(attrs...))
}
func (b balanceActivityDo) Joins(fields ...field.RelationField) *balanceActivityDo {
for _, _f := range fields {
b = *b.withDO(b.DO.Joins(_f))
}
return &b
}
func (b balanceActivityDo) Preload(fields ...field.RelationField) *balanceActivityDo {
for _, _f := range fields {
b = *b.withDO(b.DO.Preload(_f))
}
return &b
}
func (b balanceActivityDo) FirstOrInit() (*models.BalanceActivity, error) {
if result, err := b.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.BalanceActivity), nil
}
}
func (b balanceActivityDo) FirstOrCreate() (*models.BalanceActivity, error) {
if result, err := b.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.BalanceActivity), nil
}
}
func (b balanceActivityDo) FindByPage(offset int, limit int) (result []*models.BalanceActivity, count int64, err error) {
result, err = b.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 = b.Offset(-1).Limit(-1).Count()
return
}
func (b balanceActivityDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = b.Count()
if err != nil {
return
}
err = b.Offset(offset).Limit(limit).Scan(result)
return
}
func (b balanceActivityDo) Scan(result interface{}) (err error) {
return b.DO.Scan(result)
}
func (b balanceActivityDo) Delete(models ...*models.BalanceActivity) (result gen.ResultInfo, err error) {
return b.DO.Delete(models)
}
func (b *balanceActivityDo) withDO(do gen.Dao) *balanceActivityDo {
b.DO = *do.(*gen.DO)
return b
}

View File

@@ -35,18 +35,85 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
_bill.TradeID = field.NewInt32(tableName, "trade_id") _bill.TradeID = field.NewInt32(tableName, "trade_id")
_bill.ResourceID = field.NewInt32(tableName, "resource_id") _bill.ResourceID = field.NewInt32(tableName, "resource_id")
_bill.RefundID = field.NewInt32(tableName, "refund_id") _bill.RefundID = field.NewInt32(tableName, "refund_id")
_bill.CouponUserID = field.NewInt32(tableName, "coupon_user_id")
_bill.BillNo = field.NewString(tableName, "bill_no") _bill.BillNo = field.NewString(tableName, "bill_no")
_bill.Info = field.NewString(tableName, "info") _bill.Info = field.NewString(tableName, "info")
_bill.Type = field.NewInt(tableName, "type") _bill.Type = field.NewInt(tableName, "type")
_bill.Amount = field.NewField(tableName, "amount") _bill.Amount = field.NewField(tableName, "amount")
_bill.Actual = field.NewField(tableName, "actual")
_bill.User = billBelongsToUser{ _bill.User = billBelongsToUser{
db: db.Session(&gorm.Session{}), db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("User", "models.User"), RelationField: field.NewRelation("User", "models.User"),
Admin: struct { Admin: struct {
field.RelationField field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}{ }{
RelationField: field.NewRelation("User.Admin", "models.Admin"), RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
},
}, },
} }
@@ -54,6 +121,11 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
db: db.Session(&gorm.Session{}), db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Trade", "models.Trade"), RelationField: field.NewRelation("Trade", "models.Trade"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Trade.User", "models.User"),
},
} }
_bill.Resource = billBelongsToResource{ _bill.Resource = billBelongsToResource{
@@ -67,13 +139,70 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
}, },
Short: struct { Short: struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
}{ }{
RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"), RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
}, },
Long: struct { Long: struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
}
}{ }{
RelationField: field.NewRelation("Resource.Long", "models.ResourceLong"), RelationField: field.NewRelation("Resource.Long", "models.ResourceLong"),
Sku: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Long.Sku", "models.ProductSku"),
},
},
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Product", "models.Product"),
}, },
} }
@@ -83,6 +212,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
RelationField: field.NewRelation("Refund", "models.Refund"), RelationField: field.NewRelation("Refund", "models.Refund"),
} }
_bill.CouponUser = billBelongsToCouponUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("CouponUser", "models.CouponUser"),
Coupon: struct {
field.RelationField
}{
RelationField: field.NewRelation("CouponUser.Coupon", "models.Coupon"),
},
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("CouponUser.User", "models.User"),
},
}
_bill.fillFieldMap() _bill.fillFieldMap()
return _bill return _bill
@@ -91,20 +236,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
type bill struct { type bill struct {
billDo billDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 ID field.Int32
CreatedAt field.Time CreatedAt field.Time
UpdatedAt field.Time UpdatedAt field.Time
DeletedAt field.Field DeletedAt field.Field
UserID field.Int32 UserID field.Int32
TradeID field.Int32 TradeID field.Int32
ResourceID field.Int32 ResourceID field.Int32
RefundID field.Int32 RefundID field.Int32
BillNo field.String CouponUserID field.Int32
Info field.String BillNo field.String
Type field.Int Info field.String
Amount field.Field Type field.Int
User billBelongsToUser Amount field.Field
Actual field.Field
User billBelongsToUser
Trade billBelongsToTrade Trade billBelongsToTrade
@@ -112,6 +259,8 @@ type bill struct {
Refund billBelongsToRefund Refund billBelongsToRefund
CouponUser billBelongsToCouponUser
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -135,10 +284,12 @@ func (b *bill) updateTableName(table string) *bill {
b.TradeID = field.NewInt32(table, "trade_id") b.TradeID = field.NewInt32(table, "trade_id")
b.ResourceID = field.NewInt32(table, "resource_id") b.ResourceID = field.NewInt32(table, "resource_id")
b.RefundID = field.NewInt32(table, "refund_id") b.RefundID = field.NewInt32(table, "refund_id")
b.CouponUserID = field.NewInt32(table, "coupon_user_id")
b.BillNo = field.NewString(table, "bill_no") b.BillNo = field.NewString(table, "bill_no")
b.Info = field.NewString(table, "info") b.Info = field.NewString(table, "info")
b.Type = field.NewInt(table, "type") b.Type = field.NewInt(table, "type")
b.Amount = field.NewField(table, "amount") b.Amount = field.NewField(table, "amount")
b.Actual = field.NewField(table, "actual")
b.fillFieldMap() b.fillFieldMap()
@@ -155,7 +306,7 @@ func (b *bill) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (b *bill) fillFieldMap() { func (b *bill) fillFieldMap() {
b.fieldMap = make(map[string]field.Expr, 16) b.fieldMap = make(map[string]field.Expr, 19)
b.fieldMap["id"] = b.ID b.fieldMap["id"] = b.ID
b.fieldMap["created_at"] = b.CreatedAt b.fieldMap["created_at"] = b.CreatedAt
b.fieldMap["updated_at"] = b.UpdatedAt b.fieldMap["updated_at"] = b.UpdatedAt
@@ -164,10 +315,12 @@ func (b *bill) fillFieldMap() {
b.fieldMap["trade_id"] = b.TradeID b.fieldMap["trade_id"] = b.TradeID
b.fieldMap["resource_id"] = b.ResourceID b.fieldMap["resource_id"] = b.ResourceID
b.fieldMap["refund_id"] = b.RefundID b.fieldMap["refund_id"] = b.RefundID
b.fieldMap["coupon_user_id"] = b.CouponUserID
b.fieldMap["bill_no"] = b.BillNo b.fieldMap["bill_no"] = b.BillNo
b.fieldMap["info"] = b.Info b.fieldMap["info"] = b.Info
b.fieldMap["type"] = b.Type b.fieldMap["type"] = b.Type
b.fieldMap["amount"] = b.Amount b.fieldMap["amount"] = b.Amount
b.fieldMap["actual"] = b.Actual
} }
@@ -181,6 +334,8 @@ func (b bill) clone(db *gorm.DB) bill {
b.Resource.db.Statement.ConnPool = db.Statement.ConnPool b.Resource.db.Statement.ConnPool = db.Statement.ConnPool
b.Refund.db = db.Session(&gorm.Session{Initialized: true}) b.Refund.db = db.Session(&gorm.Session{Initialized: true})
b.Refund.db.Statement.ConnPool = db.Statement.ConnPool b.Refund.db.Statement.ConnPool = db.Statement.ConnPool
b.CouponUser.db = db.Session(&gorm.Session{Initialized: true})
b.CouponUser.db.Statement.ConnPool = db.Statement.ConnPool
return b return b
} }
@@ -190,6 +345,7 @@ func (b bill) replaceDB(db *gorm.DB) bill {
b.Trade.db = db.Session(&gorm.Session{}) b.Trade.db = db.Session(&gorm.Session{})
b.Resource.db = db.Session(&gorm.Session{}) b.Resource.db = db.Session(&gorm.Session{})
b.Refund.db = db.Session(&gorm.Session{}) b.Refund.db = db.Session(&gorm.Session{})
b.CouponUser.db = db.Session(&gorm.Session{})
return b return b
} }
@@ -200,6 +356,27 @@ type billBelongsToUser struct {
Admin struct { Admin struct {
field.RelationField field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}
Discount struct {
field.RelationField
}
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
} }
} }
@@ -282,6 +459,10 @@ type billBelongsToTrade struct {
db *gorm.DB db *gorm.DB
field.RelationField field.RelationField
User struct {
field.RelationField
}
} }
func (a billBelongsToTrade) Where(conds ...field.Expr) *billBelongsToTrade { func (a billBelongsToTrade) Where(conds ...field.Expr) *billBelongsToTrade {
@@ -369,9 +550,27 @@ type billBelongsToResource struct {
} }
Short struct { Short struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
} }
Long struct { Long struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
} }
} }
@@ -531,6 +730,94 @@ func (a billBelongsToRefundTx) Unscoped() *billBelongsToRefundTx {
return &a return &a
} }
type billBelongsToCouponUser struct {
db *gorm.DB
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}
func (a billBelongsToCouponUser) Where(conds ...field.Expr) *billBelongsToCouponUser {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a billBelongsToCouponUser) WithContext(ctx context.Context) *billBelongsToCouponUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a billBelongsToCouponUser) Session(session *gorm.Session) *billBelongsToCouponUser {
a.db = a.db.Session(session)
return &a
}
func (a billBelongsToCouponUser) Model(m *models.Bill) *billBelongsToCouponUserTx {
return &billBelongsToCouponUserTx{a.db.Model(m).Association(a.Name())}
}
func (a billBelongsToCouponUser) Unscoped() *billBelongsToCouponUser {
a.db = a.db.Unscoped()
return &a
}
type billBelongsToCouponUserTx struct{ tx *gorm.Association }
func (a billBelongsToCouponUserTx) Find() (result *models.CouponUser, err error) {
return result, a.tx.Find(&result)
}
func (a billBelongsToCouponUserTx) Append(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a billBelongsToCouponUserTx) Replace(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a billBelongsToCouponUserTx) Delete(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a billBelongsToCouponUserTx) Clear() error {
return a.tx.Clear()
}
func (a billBelongsToCouponUserTx) Count() int64 {
return a.tx.Count()
}
func (a billBelongsToCouponUserTx) Unscoped() *billBelongsToCouponUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type billDo struct{ gen.DO } type billDo struct{ gen.DO }
func (b billDo) Debug() *billDo { func (b billDo) Debug() *billDo {

View File

@@ -33,10 +33,12 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
_channel.DeletedAt = field.NewField(tableName, "deleted_at") _channel.DeletedAt = field.NewField(tableName, "deleted_at")
_channel.UserID = field.NewInt32(tableName, "user_id") _channel.UserID = field.NewInt32(tableName, "user_id")
_channel.ResourceID = field.NewInt32(tableName, "resource_id") _channel.ResourceID = field.NewInt32(tableName, "resource_id")
_channel.ProxyID = field.NewInt32(tableName, "proxy_id")
_channel.BatchNo = field.NewString(tableName, "batch_no") _channel.BatchNo = field.NewString(tableName, "batch_no")
_channel.ProxyID = field.NewInt32(tableName, "proxy_id")
_channel.Host = field.NewString(tableName, "host")
_channel.Port = field.NewUint16(tableName, "port") _channel.Port = field.NewUint16(tableName, "port")
_channel.EdgeID = field.NewInt32(tableName, "edge_id") _channel.EdgeID = field.NewInt32(tableName, "edge_id")
_channel.EdgeRef = field.NewString(tableName, "edge_ref")
_channel.FilterISP = field.NewInt(tableName, "filter_isp") _channel.FilterISP = field.NewInt(tableName, "filter_isp")
_channel.FilterProv = field.NewString(tableName, "filter_prov") _channel.FilterProv = field.NewString(tableName, "filter_prov")
_channel.FilterCity = field.NewString(tableName, "filter_city") _channel.FilterCity = field.NewString(tableName, "filter_city")
@@ -51,8 +53,73 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
RelationField: field.NewRelation("User", "models.User"), RelationField: field.NewRelation("User", "models.User"),
Admin: struct { Admin: struct {
field.RelationField field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}{ }{
RelationField: field.NewRelation("User.Admin", "models.Admin"), RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
},
}, },
} }
@@ -67,13 +134,70 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
}, },
Short: struct { Short: struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
}{ }{
RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"), RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
}, },
Long: struct { Long: struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
}
}{ }{
RelationField: field.NewRelation("Resource.Long", "models.ResourceLong"), RelationField: field.NewRelation("Resource.Long", "models.ResourceLong"),
Sku: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Long.Sku", "models.ProductSku"),
},
},
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Product", "models.Product"),
}, },
} }
@@ -141,10 +265,12 @@ type channel struct {
DeletedAt field.Field DeletedAt field.Field
UserID field.Int32 UserID field.Int32
ResourceID field.Int32 ResourceID field.Int32
ProxyID field.Int32
BatchNo field.String BatchNo field.String
ProxyID field.Int32
Host field.String
Port field.Uint16 Port field.Uint16
EdgeID field.Int32 EdgeID field.Int32
EdgeRef field.String
FilterISP field.Int FilterISP field.Int
FilterProv field.String FilterProv field.String
FilterCity field.String FilterCity field.String
@@ -182,10 +308,12 @@ func (c *channel) updateTableName(table string) *channel {
c.DeletedAt = field.NewField(table, "deleted_at") c.DeletedAt = field.NewField(table, "deleted_at")
c.UserID = field.NewInt32(table, "user_id") c.UserID = field.NewInt32(table, "user_id")
c.ResourceID = field.NewInt32(table, "resource_id") c.ResourceID = field.NewInt32(table, "resource_id")
c.ProxyID = field.NewInt32(table, "proxy_id")
c.BatchNo = field.NewString(table, "batch_no") c.BatchNo = field.NewString(table, "batch_no")
c.ProxyID = field.NewInt32(table, "proxy_id")
c.Host = field.NewString(table, "host")
c.Port = field.NewUint16(table, "port") c.Port = field.NewUint16(table, "port")
c.EdgeID = field.NewInt32(table, "edge_id") c.EdgeID = field.NewInt32(table, "edge_id")
c.EdgeRef = field.NewString(table, "edge_ref")
c.FilterISP = field.NewInt(table, "filter_isp") c.FilterISP = field.NewInt(table, "filter_isp")
c.FilterProv = field.NewString(table, "filter_prov") c.FilterProv = field.NewString(table, "filter_prov")
c.FilterCity = field.NewString(table, "filter_city") c.FilterCity = field.NewString(table, "filter_city")
@@ -210,17 +338,19 @@ func (c *channel) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (c *channel) fillFieldMap() { func (c *channel) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 22) c.fieldMap = make(map[string]field.Expr, 24)
c.fieldMap["id"] = c.ID c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["user_id"] = c.UserID c.fieldMap["user_id"] = c.UserID
c.fieldMap["resource_id"] = c.ResourceID c.fieldMap["resource_id"] = c.ResourceID
c.fieldMap["proxy_id"] = c.ProxyID
c.fieldMap["batch_no"] = c.BatchNo c.fieldMap["batch_no"] = c.BatchNo
c.fieldMap["proxy_id"] = c.ProxyID
c.fieldMap["host"] = c.Host
c.fieldMap["port"] = c.Port c.fieldMap["port"] = c.Port
c.fieldMap["edge_id"] = c.EdgeID c.fieldMap["edge_id"] = c.EdgeID
c.fieldMap["edge_ref"] = c.EdgeRef
c.fieldMap["filter_isp"] = c.FilterISP c.fieldMap["filter_isp"] = c.FilterISP
c.fieldMap["filter_prov"] = c.FilterProv c.fieldMap["filter_prov"] = c.FilterProv
c.fieldMap["filter_city"] = c.FilterCity c.fieldMap["filter_city"] = c.FilterCity
@@ -261,6 +391,27 @@ type channelBelongsToUser struct {
Admin struct { Admin struct {
field.RelationField field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}
Discount struct {
field.RelationField
}
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
} }
} }
@@ -349,9 +500,27 @@ type channelBelongsToResource struct {
} }
Short struct { Short struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
}
}
} }
Long struct { Long struct {
field.RelationField field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
} }
} }

View File

@@ -39,6 +39,21 @@ func newClient(db *gorm.DB, opts ...gen.DOOption) client {
_client.Icon = field.NewString(tableName, "icon") _client.Icon = field.NewString(tableName, "icon")
_client.Status = field.NewInt(tableName, "status") _client.Status = field.NewInt(tableName, "status")
_client.Type = field.NewInt(tableName, "type") _client.Type = field.NewInt(tableName, "type")
_client.Permissions = clientManyToManyPermissions{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Permissions.Children", "models.Permission"),
},
}
_client.fillFieldMap() _client.fillFieldMap()
@@ -61,6 +76,7 @@ type client struct {
Icon field.String Icon field.String
Status field.Int Status field.Int
Type field.Int Type field.Int
Permissions clientManyToManyPermissions
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -105,7 +121,7 @@ func (c *client) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (c *client) fillFieldMap() { func (c *client) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 12) c.fieldMap = make(map[string]field.Expr, 13)
c.fieldMap["id"] = c.ID c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt c.fieldMap["updated_at"] = c.UpdatedAt
@@ -118,18 +134,110 @@ func (c *client) fillFieldMap() {
c.fieldMap["icon"] = c.Icon c.fieldMap["icon"] = c.Icon
c.fieldMap["status"] = c.Status c.fieldMap["status"] = c.Status
c.fieldMap["type"] = c.Type c.fieldMap["type"] = c.Type
} }
func (c client) clone(db *gorm.DB) client { func (c client) clone(db *gorm.DB) client {
c.clientDo.ReplaceConnPool(db.Statement.ConnPool) c.clientDo.ReplaceConnPool(db.Statement.ConnPool)
c.Permissions.db = db.Session(&gorm.Session{Initialized: true})
c.Permissions.db.Statement.ConnPool = db.Statement.ConnPool
return c return c
} }
func (c client) replaceDB(db *gorm.DB) client { func (c client) replaceDB(db *gorm.DB) client {
c.clientDo.ReplaceDB(db) c.clientDo.ReplaceDB(db)
c.Permissions.db = db.Session(&gorm.Session{})
return c return c
} }
type clientManyToManyPermissions struct {
db *gorm.DB
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
func (a clientManyToManyPermissions) Where(conds ...field.Expr) *clientManyToManyPermissions {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a clientManyToManyPermissions) WithContext(ctx context.Context) *clientManyToManyPermissions {
a.db = a.db.WithContext(ctx)
return &a
}
func (a clientManyToManyPermissions) Session(session *gorm.Session) *clientManyToManyPermissions {
a.db = a.db.Session(session)
return &a
}
func (a clientManyToManyPermissions) Model(m *models.Client) *clientManyToManyPermissionsTx {
return &clientManyToManyPermissionsTx{a.db.Model(m).Association(a.Name())}
}
func (a clientManyToManyPermissions) Unscoped() *clientManyToManyPermissions {
a.db = a.db.Unscoped()
return &a
}
type clientManyToManyPermissionsTx struct{ tx *gorm.Association }
func (a clientManyToManyPermissionsTx) Find() (result []*models.Permission, err error) {
return result, a.tx.Find(&result)
}
func (a clientManyToManyPermissionsTx) Append(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a clientManyToManyPermissionsTx) Replace(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a clientManyToManyPermissionsTx) Delete(values ...*models.Permission) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a clientManyToManyPermissionsTx) Clear() error {
return a.tx.Clear()
}
func (a clientManyToManyPermissionsTx) Count() int64 {
return a.tx.Count()
}
func (a clientManyToManyPermissionsTx) Unscoped() *clientManyToManyPermissionsTx {
a.tx = a.tx.Unscoped()
return &a
}
type clientDo struct{ gen.DO } type clientDo struct{ gen.DO }
func (c clientDo) Debug() *clientDo { func (c clientDo) Debug() *clientDo {

View File

@@ -31,13 +31,14 @@ func newCoupon(db *gorm.DB, opts ...gen.DOOption) coupon {
_coupon.CreatedAt = field.NewTime(tableName, "created_at") _coupon.CreatedAt = field.NewTime(tableName, "created_at")
_coupon.UpdatedAt = field.NewTime(tableName, "updated_at") _coupon.UpdatedAt = field.NewTime(tableName, "updated_at")
_coupon.DeletedAt = field.NewField(tableName, "deleted_at") _coupon.DeletedAt = field.NewField(tableName, "deleted_at")
_coupon.UserID = field.NewInt32(tableName, "user_id") _coupon.Name = field.NewString(tableName, "name")
_coupon.Code = field.NewString(tableName, "code")
_coupon.Remark = field.NewString(tableName, "remark")
_coupon.Amount = field.NewField(tableName, "amount") _coupon.Amount = field.NewField(tableName, "amount")
_coupon.MinAmount = field.NewField(tableName, "min_amount") _coupon.MinAmount = field.NewField(tableName, "min_amount")
_coupon.Count_ = field.NewInt32(tableName, "count")
_coupon.Status = field.NewInt(tableName, "status") _coupon.Status = field.NewInt(tableName, "status")
_coupon.ExpireType = field.NewInt(tableName, "expire_type")
_coupon.ExpireAt = field.NewTime(tableName, "expire_at") _coupon.ExpireAt = field.NewTime(tableName, "expire_at")
_coupon.ExpireIn = field.NewInt(tableName, "expire_in")
_coupon.fillFieldMap() _coupon.fillFieldMap()
@@ -47,18 +48,19 @@ func newCoupon(db *gorm.DB, opts ...gen.DOOption) coupon {
type coupon struct { type coupon struct {
couponDo couponDo
ALL field.Asterisk ALL field.Asterisk
ID field.Int32 ID field.Int32
CreatedAt field.Time CreatedAt field.Time
UpdatedAt field.Time UpdatedAt field.Time
DeletedAt field.Field DeletedAt field.Field
UserID field.Int32 Name field.String
Code field.String Amount field.Field
Remark field.String MinAmount field.Field
Amount field.Field Count_ field.Int32
MinAmount field.Field Status field.Int
Status field.Int ExpireType field.Int
ExpireAt field.Time ExpireAt field.Time
ExpireIn field.Int
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -79,13 +81,14 @@ func (c *coupon) updateTableName(table string) *coupon {
c.CreatedAt = field.NewTime(table, "created_at") c.CreatedAt = field.NewTime(table, "created_at")
c.UpdatedAt = field.NewTime(table, "updated_at") c.UpdatedAt = field.NewTime(table, "updated_at")
c.DeletedAt = field.NewField(table, "deleted_at") c.DeletedAt = field.NewField(table, "deleted_at")
c.UserID = field.NewInt32(table, "user_id") c.Name = field.NewString(table, "name")
c.Code = field.NewString(table, "code")
c.Remark = field.NewString(table, "remark")
c.Amount = field.NewField(table, "amount") c.Amount = field.NewField(table, "amount")
c.MinAmount = field.NewField(table, "min_amount") c.MinAmount = field.NewField(table, "min_amount")
c.Count_ = field.NewInt32(table, "count")
c.Status = field.NewInt(table, "status") c.Status = field.NewInt(table, "status")
c.ExpireType = field.NewInt(table, "expire_type")
c.ExpireAt = field.NewTime(table, "expire_at") c.ExpireAt = field.NewTime(table, "expire_at")
c.ExpireIn = field.NewInt(table, "expire_in")
c.fillFieldMap() c.fillFieldMap()
@@ -102,18 +105,19 @@ func (c *coupon) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (c *coupon) fillFieldMap() { func (c *coupon) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 11) c.fieldMap = make(map[string]field.Expr, 12)
c.fieldMap["id"] = c.ID c.fieldMap["id"] = c.ID
c.fieldMap["created_at"] = c.CreatedAt c.fieldMap["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt c.fieldMap["updated_at"] = c.UpdatedAt
c.fieldMap["deleted_at"] = c.DeletedAt c.fieldMap["deleted_at"] = c.DeletedAt
c.fieldMap["user_id"] = c.UserID c.fieldMap["name"] = c.Name
c.fieldMap["code"] = c.Code
c.fieldMap["remark"] = c.Remark
c.fieldMap["amount"] = c.Amount c.fieldMap["amount"] = c.Amount
c.fieldMap["min_amount"] = c.MinAmount c.fieldMap["min_amount"] = c.MinAmount
c.fieldMap["count"] = c.Count_
c.fieldMap["status"] = c.Status c.fieldMap["status"] = c.Status
c.fieldMap["expire_type"] = c.ExpireType
c.fieldMap["expire_at"] = c.ExpireAt c.fieldMap["expire_at"] = c.ExpireAt
c.fieldMap["expire_in"] = c.ExpireIn
} }
func (c coupon) clone(db *gorm.DB) coupon { func (c coupon) clone(db *gorm.DB) coupon {

View File

@@ -0,0 +1,621 @@
// 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 newCouponUser(db *gorm.DB, opts ...gen.DOOption) couponUser {
_couponUser := couponUser{}
_couponUser.couponUserDo.UseDB(db, opts...)
_couponUser.couponUserDo.UseModel(&models.CouponUser{})
tableName := _couponUser.couponUserDo.TableName()
_couponUser.ALL = field.NewAsterisk(tableName)
_couponUser.ID = field.NewInt32(tableName, "id")
_couponUser.CouponID = field.NewInt32(tableName, "coupon_id")
_couponUser.UserID = field.NewInt32(tableName, "user_id")
_couponUser.Status = field.NewInt(tableName, "status")
_couponUser.ExpireAt = field.NewTime(tableName, "expire_at")
_couponUser.UsedAt = field.NewTime(tableName, "used_at")
_couponUser.CreatedAt = field.NewTime(tableName, "created_at")
_couponUser.Coupon = couponUserBelongsToCoupon{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Coupon", "models.Coupon"),
}
_couponUser.User = couponUserBelongsToUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("User", "models.User"),
Admin: struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}{
RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
},
},
}
_couponUser.fillFieldMap()
return _couponUser
}
type couponUser struct {
couponUserDo
ALL field.Asterisk
ID field.Int32
CouponID field.Int32
UserID field.Int32
Status field.Int
ExpireAt field.Time
UsedAt field.Time
CreatedAt field.Time
Coupon couponUserBelongsToCoupon
User couponUserBelongsToUser
fieldMap map[string]field.Expr
}
func (c couponUser) Table(newTableName string) *couponUser {
c.couponUserDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c couponUser) As(alias string) *couponUser {
c.couponUserDo.DO = *(c.couponUserDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *couponUser) updateTableName(table string) *couponUser {
c.ALL = field.NewAsterisk(table)
c.ID = field.NewInt32(table, "id")
c.CouponID = field.NewInt32(table, "coupon_id")
c.UserID = field.NewInt32(table, "user_id")
c.Status = field.NewInt(table, "status")
c.ExpireAt = field.NewTime(table, "expire_at")
c.UsedAt = field.NewTime(table, "used_at")
c.CreatedAt = field.NewTime(table, "created_at")
c.fillFieldMap()
return c
}
func (c *couponUser) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *couponUser) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 9)
c.fieldMap["id"] = c.ID
c.fieldMap["coupon_id"] = c.CouponID
c.fieldMap["user_id"] = c.UserID
c.fieldMap["status"] = c.Status
c.fieldMap["expire_at"] = c.ExpireAt
c.fieldMap["used_at"] = c.UsedAt
c.fieldMap["created_at"] = c.CreatedAt
}
func (c couponUser) clone(db *gorm.DB) couponUser {
c.couponUserDo.ReplaceConnPool(db.Statement.ConnPool)
c.Coupon.db = db.Session(&gorm.Session{Initialized: true})
c.Coupon.db.Statement.ConnPool = db.Statement.ConnPool
c.User.db = db.Session(&gorm.Session{Initialized: true})
c.User.db.Statement.ConnPool = db.Statement.ConnPool
return c
}
func (c couponUser) replaceDB(db *gorm.DB) couponUser {
c.couponUserDo.ReplaceDB(db)
c.Coupon.db = db.Session(&gorm.Session{})
c.User.db = db.Session(&gorm.Session{})
return c
}
type couponUserBelongsToCoupon struct {
db *gorm.DB
field.RelationField
}
func (a couponUserBelongsToCoupon) Where(conds ...field.Expr) *couponUserBelongsToCoupon {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a couponUserBelongsToCoupon) WithContext(ctx context.Context) *couponUserBelongsToCoupon {
a.db = a.db.WithContext(ctx)
return &a
}
func (a couponUserBelongsToCoupon) Session(session *gorm.Session) *couponUserBelongsToCoupon {
a.db = a.db.Session(session)
return &a
}
func (a couponUserBelongsToCoupon) Model(m *models.CouponUser) *couponUserBelongsToCouponTx {
return &couponUserBelongsToCouponTx{a.db.Model(m).Association(a.Name())}
}
func (a couponUserBelongsToCoupon) Unscoped() *couponUserBelongsToCoupon {
a.db = a.db.Unscoped()
return &a
}
type couponUserBelongsToCouponTx struct{ tx *gorm.Association }
func (a couponUserBelongsToCouponTx) Find() (result *models.Coupon, err error) {
return result, a.tx.Find(&result)
}
func (a couponUserBelongsToCouponTx) Append(values ...*models.Coupon) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a couponUserBelongsToCouponTx) Replace(values ...*models.Coupon) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a couponUserBelongsToCouponTx) Delete(values ...*models.Coupon) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a couponUserBelongsToCouponTx) Clear() error {
return a.tx.Clear()
}
func (a couponUserBelongsToCouponTx) Count() int64 {
return a.tx.Count()
}
func (a couponUserBelongsToCouponTx) Unscoped() *couponUserBelongsToCouponTx {
a.tx = a.tx.Unscoped()
return &a
}
type couponUserBelongsToUser struct {
db *gorm.DB
field.RelationField
Admin struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}
Discount struct {
field.RelationField
}
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
}
}
func (a couponUserBelongsToUser) Where(conds ...field.Expr) *couponUserBelongsToUser {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a couponUserBelongsToUser) WithContext(ctx context.Context) *couponUserBelongsToUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a couponUserBelongsToUser) Session(session *gorm.Session) *couponUserBelongsToUser {
a.db = a.db.Session(session)
return &a
}
func (a couponUserBelongsToUser) Model(m *models.CouponUser) *couponUserBelongsToUserTx {
return &couponUserBelongsToUserTx{a.db.Model(m).Association(a.Name())}
}
func (a couponUserBelongsToUser) Unscoped() *couponUserBelongsToUser {
a.db = a.db.Unscoped()
return &a
}
type couponUserBelongsToUserTx struct{ tx *gorm.Association }
func (a couponUserBelongsToUserTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a couponUserBelongsToUserTx) Append(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a couponUserBelongsToUserTx) Replace(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a couponUserBelongsToUserTx) Delete(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a couponUserBelongsToUserTx) Clear() error {
return a.tx.Clear()
}
func (a couponUserBelongsToUserTx) Count() int64 {
return a.tx.Count()
}
func (a couponUserBelongsToUserTx) Unscoped() *couponUserBelongsToUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type couponUserDo struct{ gen.DO }
func (c couponUserDo) Debug() *couponUserDo {
return c.withDO(c.DO.Debug())
}
func (c couponUserDo) WithContext(ctx context.Context) *couponUserDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c couponUserDo) ReadDB() *couponUserDo {
return c.Clauses(dbresolver.Read)
}
func (c couponUserDo) WriteDB() *couponUserDo {
return c.Clauses(dbresolver.Write)
}
func (c couponUserDo) Session(config *gorm.Session) *couponUserDo {
return c.withDO(c.DO.Session(config))
}
func (c couponUserDo) Clauses(conds ...clause.Expression) *couponUserDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c couponUserDo) Returning(value interface{}, columns ...string) *couponUserDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c couponUserDo) Not(conds ...gen.Condition) *couponUserDo {
return c.withDO(c.DO.Not(conds...))
}
func (c couponUserDo) Or(conds ...gen.Condition) *couponUserDo {
return c.withDO(c.DO.Or(conds...))
}
func (c couponUserDo) Select(conds ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Select(conds...))
}
func (c couponUserDo) Where(conds ...gen.Condition) *couponUserDo {
return c.withDO(c.DO.Where(conds...))
}
func (c couponUserDo) Order(conds ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Order(conds...))
}
func (c couponUserDo) Distinct(cols ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c couponUserDo) Omit(cols ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c couponUserDo) Join(table schema.Tabler, on ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c couponUserDo) LeftJoin(table schema.Tabler, on ...field.Expr) *couponUserDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c couponUserDo) RightJoin(table schema.Tabler, on ...field.Expr) *couponUserDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c couponUserDo) Group(cols ...field.Expr) *couponUserDo {
return c.withDO(c.DO.Group(cols...))
}
func (c couponUserDo) Having(conds ...gen.Condition) *couponUserDo {
return c.withDO(c.DO.Having(conds...))
}
func (c couponUserDo) Limit(limit int) *couponUserDo {
return c.withDO(c.DO.Limit(limit))
}
func (c couponUserDo) Offset(offset int) *couponUserDo {
return c.withDO(c.DO.Offset(offset))
}
func (c couponUserDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *couponUserDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c couponUserDo) Unscoped() *couponUserDo {
return c.withDO(c.DO.Unscoped())
}
func (c couponUserDo) Create(values ...*models.CouponUser) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c couponUserDo) CreateInBatches(values []*models.CouponUser, batchSize int) error {
return c.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 (c couponUserDo) Save(values ...*models.CouponUser) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c couponUserDo) First() (*models.CouponUser, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.CouponUser), nil
}
}
func (c couponUserDo) Take() (*models.CouponUser, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.CouponUser), nil
}
}
func (c couponUserDo) Last() (*models.CouponUser, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.CouponUser), nil
}
}
func (c couponUserDo) Find() ([]*models.CouponUser, error) {
result, err := c.DO.Find()
return result.([]*models.CouponUser), err
}
func (c couponUserDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.CouponUser, err error) {
buf := make([]*models.CouponUser, 0, batchSize)
err = c.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 (c couponUserDo) FindInBatches(result *[]*models.CouponUser, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c couponUserDo) Attrs(attrs ...field.AssignExpr) *couponUserDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c couponUserDo) Assign(attrs ...field.AssignExpr) *couponUserDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c couponUserDo) Joins(fields ...field.RelationField) *couponUserDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c couponUserDo) Preload(fields ...field.RelationField) *couponUserDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c couponUserDo) FirstOrInit() (*models.CouponUser, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.CouponUser), nil
}
}
func (c couponUserDo) FirstOrCreate() (*models.CouponUser, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.CouponUser), nil
}
}
func (c couponUserDo) FindByPage(offset int, limit int) (result []*models.CouponUser, count int64, err error) {
result, err = c.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 = c.Offset(-1).Limit(-1).Count()
return
}
func (c couponUserDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c couponUserDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c couponUserDo) Delete(models ...*models.CouponUser) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *couponUserDo) withDO(do gen.Dao) *couponUserDo {
c.DO = *do.(*gen.DO)
return c
}

View File

@@ -20,11 +20,14 @@ var (
Admin *admin Admin *admin
AdminRole *adminRole AdminRole *adminRole
Announcement *announcement Announcement *announcement
BalanceActivity *balanceActivity
Bill *bill Bill *bill
Channel *channel Channel *channel
Client *client Client *client
Coupon *coupon Coupon *coupon
CouponUser *couponUser
Edge *edge Edge *edge
Inquiry *inquiry
LinkAdminRole *linkAdminRole LinkAdminRole *linkAdminRole
LinkAdminRolePermission *linkAdminRolePermission LinkAdminRolePermission *linkAdminRolePermission
LinkClientPermission *linkClientPermission LinkClientPermission *linkClientPermission
@@ -36,6 +39,9 @@ var (
LogsUserUsage *logsUserUsage LogsUserUsage *logsUserUsage
Permission *permission Permission *permission
Product *product Product *product
ProductDiscount *productDiscount
ProductSku *productSku
ProductSkuUser *productSkuUser
Proxy *proxy Proxy *proxy
Refund *refund Refund *refund
Resource *resource Resource *resource
@@ -53,11 +59,14 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Admin = &Q.Admin Admin = &Q.Admin
AdminRole = &Q.AdminRole AdminRole = &Q.AdminRole
Announcement = &Q.Announcement Announcement = &Q.Announcement
BalanceActivity = &Q.BalanceActivity
Bill = &Q.Bill Bill = &Q.Bill
Channel = &Q.Channel Channel = &Q.Channel
Client = &Q.Client Client = &Q.Client
Coupon = &Q.Coupon Coupon = &Q.Coupon
CouponUser = &Q.CouponUser
Edge = &Q.Edge Edge = &Q.Edge
Inquiry = &Q.Inquiry
LinkAdminRole = &Q.LinkAdminRole LinkAdminRole = &Q.LinkAdminRole
LinkAdminRolePermission = &Q.LinkAdminRolePermission LinkAdminRolePermission = &Q.LinkAdminRolePermission
LinkClientPermission = &Q.LinkClientPermission LinkClientPermission = &Q.LinkClientPermission
@@ -69,6 +78,9 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
LogsUserUsage = &Q.LogsUserUsage LogsUserUsage = &Q.LogsUserUsage
Permission = &Q.Permission Permission = &Q.Permission
Product = &Q.Product Product = &Q.Product
ProductDiscount = &Q.ProductDiscount
ProductSku = &Q.ProductSku
ProductSkuUser = &Q.ProductSkuUser
Proxy = &Q.Proxy Proxy = &Q.Proxy
Refund = &Q.Refund Refund = &Q.Refund
Resource = &Q.Resource Resource = &Q.Resource
@@ -87,11 +99,14 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Admin: newAdmin(db, opts...), Admin: newAdmin(db, opts...),
AdminRole: newAdminRole(db, opts...), AdminRole: newAdminRole(db, opts...),
Announcement: newAnnouncement(db, opts...), Announcement: newAnnouncement(db, opts...),
BalanceActivity: newBalanceActivity(db, opts...),
Bill: newBill(db, opts...), Bill: newBill(db, opts...),
Channel: newChannel(db, opts...), Channel: newChannel(db, opts...),
Client: newClient(db, opts...), Client: newClient(db, opts...),
Coupon: newCoupon(db, opts...), Coupon: newCoupon(db, opts...),
CouponUser: newCouponUser(db, opts...),
Edge: newEdge(db, opts...), Edge: newEdge(db, opts...),
Inquiry: newInquiry(db, opts...),
LinkAdminRole: newLinkAdminRole(db, opts...), LinkAdminRole: newLinkAdminRole(db, opts...),
LinkAdminRolePermission: newLinkAdminRolePermission(db, opts...), LinkAdminRolePermission: newLinkAdminRolePermission(db, opts...),
LinkClientPermission: newLinkClientPermission(db, opts...), LinkClientPermission: newLinkClientPermission(db, opts...),
@@ -103,6 +118,9 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
LogsUserUsage: newLogsUserUsage(db, opts...), LogsUserUsage: newLogsUserUsage(db, opts...),
Permission: newPermission(db, opts...), Permission: newPermission(db, opts...),
Product: newProduct(db, opts...), Product: newProduct(db, opts...),
ProductDiscount: newProductDiscount(db, opts...),
ProductSku: newProductSku(db, opts...),
ProductSkuUser: newProductSkuUser(db, opts...),
Proxy: newProxy(db, opts...), Proxy: newProxy(db, opts...),
Refund: newRefund(db, opts...), Refund: newRefund(db, opts...),
Resource: newResource(db, opts...), Resource: newResource(db, opts...),
@@ -122,11 +140,14 @@ type Query struct {
Admin admin Admin admin
AdminRole adminRole AdminRole adminRole
Announcement announcement Announcement announcement
BalanceActivity balanceActivity
Bill bill Bill bill
Channel channel Channel channel
Client client Client client
Coupon coupon Coupon coupon
CouponUser couponUser
Edge edge Edge edge
Inquiry inquiry
LinkAdminRole linkAdminRole LinkAdminRole linkAdminRole
LinkAdminRolePermission linkAdminRolePermission LinkAdminRolePermission linkAdminRolePermission
LinkClientPermission linkClientPermission LinkClientPermission linkClientPermission
@@ -138,6 +159,9 @@ type Query struct {
LogsUserUsage logsUserUsage LogsUserUsage logsUserUsage
Permission permission Permission permission
Product product Product product
ProductDiscount productDiscount
ProductSku productSku
ProductSkuUser productSkuUser
Proxy proxy Proxy proxy
Refund refund Refund refund
Resource resource Resource resource
@@ -158,11 +182,14 @@ func (q *Query) clone(db *gorm.DB) *Query {
Admin: q.Admin.clone(db), Admin: q.Admin.clone(db),
AdminRole: q.AdminRole.clone(db), AdminRole: q.AdminRole.clone(db),
Announcement: q.Announcement.clone(db), Announcement: q.Announcement.clone(db),
BalanceActivity: q.BalanceActivity.clone(db),
Bill: q.Bill.clone(db), Bill: q.Bill.clone(db),
Channel: q.Channel.clone(db), Channel: q.Channel.clone(db),
Client: q.Client.clone(db), Client: q.Client.clone(db),
Coupon: q.Coupon.clone(db), Coupon: q.Coupon.clone(db),
CouponUser: q.CouponUser.clone(db),
Edge: q.Edge.clone(db), Edge: q.Edge.clone(db),
Inquiry: q.Inquiry.clone(db),
LinkAdminRole: q.LinkAdminRole.clone(db), LinkAdminRole: q.LinkAdminRole.clone(db),
LinkAdminRolePermission: q.LinkAdminRolePermission.clone(db), LinkAdminRolePermission: q.LinkAdminRolePermission.clone(db),
LinkClientPermission: q.LinkClientPermission.clone(db), LinkClientPermission: q.LinkClientPermission.clone(db),
@@ -174,6 +201,9 @@ func (q *Query) clone(db *gorm.DB) *Query {
LogsUserUsage: q.LogsUserUsage.clone(db), LogsUserUsage: q.LogsUserUsage.clone(db),
Permission: q.Permission.clone(db), Permission: q.Permission.clone(db),
Product: q.Product.clone(db), Product: q.Product.clone(db),
ProductDiscount: q.ProductDiscount.clone(db),
ProductSku: q.ProductSku.clone(db),
ProductSkuUser: q.ProductSkuUser.clone(db),
Proxy: q.Proxy.clone(db), Proxy: q.Proxy.clone(db),
Refund: q.Refund.clone(db), Refund: q.Refund.clone(db),
Resource: q.Resource.clone(db), Resource: q.Resource.clone(db),
@@ -201,11 +231,14 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Admin: q.Admin.replaceDB(db), Admin: q.Admin.replaceDB(db),
AdminRole: q.AdminRole.replaceDB(db), AdminRole: q.AdminRole.replaceDB(db),
Announcement: q.Announcement.replaceDB(db), Announcement: q.Announcement.replaceDB(db),
BalanceActivity: q.BalanceActivity.replaceDB(db),
Bill: q.Bill.replaceDB(db), Bill: q.Bill.replaceDB(db),
Channel: q.Channel.replaceDB(db), Channel: q.Channel.replaceDB(db),
Client: q.Client.replaceDB(db), Client: q.Client.replaceDB(db),
Coupon: q.Coupon.replaceDB(db), Coupon: q.Coupon.replaceDB(db),
CouponUser: q.CouponUser.replaceDB(db),
Edge: q.Edge.replaceDB(db), Edge: q.Edge.replaceDB(db),
Inquiry: q.Inquiry.replaceDB(db),
LinkAdminRole: q.LinkAdminRole.replaceDB(db), LinkAdminRole: q.LinkAdminRole.replaceDB(db),
LinkAdminRolePermission: q.LinkAdminRolePermission.replaceDB(db), LinkAdminRolePermission: q.LinkAdminRolePermission.replaceDB(db),
LinkClientPermission: q.LinkClientPermission.replaceDB(db), LinkClientPermission: q.LinkClientPermission.replaceDB(db),
@@ -217,6 +250,9 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
LogsUserUsage: q.LogsUserUsage.replaceDB(db), LogsUserUsage: q.LogsUserUsage.replaceDB(db),
Permission: q.Permission.replaceDB(db), Permission: q.Permission.replaceDB(db),
Product: q.Product.replaceDB(db), Product: q.Product.replaceDB(db),
ProductDiscount: q.ProductDiscount.replaceDB(db),
ProductSku: q.ProductSku.replaceDB(db),
ProductSkuUser: q.ProductSkuUser.replaceDB(db),
Proxy: q.Proxy.replaceDB(db), Proxy: q.Proxy.replaceDB(db),
Refund: q.Refund.replaceDB(db), Refund: q.Refund.replaceDB(db),
Resource: q.Resource.replaceDB(db), Resource: q.Resource.replaceDB(db),
@@ -234,11 +270,14 @@ type queryCtx struct {
Admin *adminDo Admin *adminDo
AdminRole *adminRoleDo AdminRole *adminRoleDo
Announcement *announcementDo Announcement *announcementDo
BalanceActivity *balanceActivityDo
Bill *billDo Bill *billDo
Channel *channelDo Channel *channelDo
Client *clientDo Client *clientDo
Coupon *couponDo Coupon *couponDo
CouponUser *couponUserDo
Edge *edgeDo Edge *edgeDo
Inquiry *inquiryDo
LinkAdminRole *linkAdminRoleDo LinkAdminRole *linkAdminRoleDo
LinkAdminRolePermission *linkAdminRolePermissionDo LinkAdminRolePermission *linkAdminRolePermissionDo
LinkClientPermission *linkClientPermissionDo LinkClientPermission *linkClientPermissionDo
@@ -250,6 +289,9 @@ type queryCtx struct {
LogsUserUsage *logsUserUsageDo LogsUserUsage *logsUserUsageDo
Permission *permissionDo Permission *permissionDo
Product *productDo Product *productDo
ProductDiscount *productDiscountDo
ProductSku *productSkuDo
ProductSkuUser *productSkuUserDo
Proxy *proxyDo Proxy *proxyDo
Refund *refundDo Refund *refundDo
Resource *resourceDo Resource *resourceDo
@@ -267,11 +309,14 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Admin: q.Admin.WithContext(ctx), Admin: q.Admin.WithContext(ctx),
AdminRole: q.AdminRole.WithContext(ctx), AdminRole: q.AdminRole.WithContext(ctx),
Announcement: q.Announcement.WithContext(ctx), Announcement: q.Announcement.WithContext(ctx),
BalanceActivity: q.BalanceActivity.WithContext(ctx),
Bill: q.Bill.WithContext(ctx), Bill: q.Bill.WithContext(ctx),
Channel: q.Channel.WithContext(ctx), Channel: q.Channel.WithContext(ctx),
Client: q.Client.WithContext(ctx), Client: q.Client.WithContext(ctx),
Coupon: q.Coupon.WithContext(ctx), Coupon: q.Coupon.WithContext(ctx),
CouponUser: q.CouponUser.WithContext(ctx),
Edge: q.Edge.WithContext(ctx), Edge: q.Edge.WithContext(ctx),
Inquiry: q.Inquiry.WithContext(ctx),
LinkAdminRole: q.LinkAdminRole.WithContext(ctx), LinkAdminRole: q.LinkAdminRole.WithContext(ctx),
LinkAdminRolePermission: q.LinkAdminRolePermission.WithContext(ctx), LinkAdminRolePermission: q.LinkAdminRolePermission.WithContext(ctx),
LinkClientPermission: q.LinkClientPermission.WithContext(ctx), LinkClientPermission: q.LinkClientPermission.WithContext(ctx),
@@ -283,6 +328,9 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
LogsUserUsage: q.LogsUserUsage.WithContext(ctx), LogsUserUsage: q.LogsUserUsage.WithContext(ctx),
Permission: q.Permission.WithContext(ctx), Permission: q.Permission.WithContext(ctx),
Product: q.Product.WithContext(ctx), Product: q.Product.WithContext(ctx),
ProductDiscount: q.ProductDiscount.WithContext(ctx),
ProductSku: q.ProductSku.WithContext(ctx),
ProductSkuUser: q.ProductSkuUser.WithContext(ctx),
Proxy: q.Proxy.WithContext(ctx), Proxy: q.Proxy.WithContext(ctx),
Refund: q.Refund.WithContext(ctx), Refund: q.Refund.WithContext(ctx),
Resource: q.Resource.WithContext(ctx), Resource: q.Resource.WithContext(ctx),

359
web/queries/inquiry.gen.go Normal file
View File

@@ -0,0 +1,359 @@
// 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 newInquiry(db *gorm.DB, opts ...gen.DOOption) inquiry {
_inquiry := inquiry{}
_inquiry.inquiryDo.UseDB(db, opts...)
_inquiry.inquiryDo.UseModel(&models.Inquiry{})
tableName := _inquiry.inquiryDo.TableName()
_inquiry.ALL = field.NewAsterisk(tableName)
_inquiry.ID = field.NewInt32(tableName, "id")
_inquiry.CreatedAt = field.NewTime(tableName, "created_at")
_inquiry.UpdatedAt = field.NewTime(tableName, "updated_at")
_inquiry.DeletedAt = field.NewField(tableName, "deleted_at")
_inquiry.Company = field.NewString(tableName, "company")
_inquiry.Name = field.NewString(tableName, "name")
_inquiry.Phone = field.NewString(tableName, "phone")
_inquiry.Email = field.NewString(tableName, "email")
_inquiry.Content = field.NewString(tableName, "content")
_inquiry.Status = field.NewInt(tableName, "status")
_inquiry.Remark = field.NewString(tableName, "remark")
_inquiry.fillFieldMap()
return _inquiry
}
type inquiry struct {
inquiryDo
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Company field.String
Name field.String
Phone field.String
Email field.String
Content field.String
Status field.Int
Remark field.String
fieldMap map[string]field.Expr
}
func (i inquiry) Table(newTableName string) *inquiry {
i.inquiryDo.UseTable(newTableName)
return i.updateTableName(newTableName)
}
func (i inquiry) As(alias string) *inquiry {
i.inquiryDo.DO = *(i.inquiryDo.As(alias).(*gen.DO))
return i.updateTableName(alias)
}
func (i *inquiry) updateTableName(table string) *inquiry {
i.ALL = field.NewAsterisk(table)
i.ID = field.NewInt32(table, "id")
i.CreatedAt = field.NewTime(table, "created_at")
i.UpdatedAt = field.NewTime(table, "updated_at")
i.DeletedAt = field.NewField(table, "deleted_at")
i.Company = field.NewString(table, "company")
i.Name = field.NewString(table, "name")
i.Phone = field.NewString(table, "phone")
i.Email = field.NewString(table, "email")
i.Content = field.NewString(table, "content")
i.Status = field.NewInt(table, "status")
i.Remark = field.NewString(table, "remark")
i.fillFieldMap()
return i
}
func (i *inquiry) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := i.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (i *inquiry) fillFieldMap() {
i.fieldMap = make(map[string]field.Expr, 11)
i.fieldMap["id"] = i.ID
i.fieldMap["created_at"] = i.CreatedAt
i.fieldMap["updated_at"] = i.UpdatedAt
i.fieldMap["deleted_at"] = i.DeletedAt
i.fieldMap["company"] = i.Company
i.fieldMap["name"] = i.Name
i.fieldMap["phone"] = i.Phone
i.fieldMap["email"] = i.Email
i.fieldMap["content"] = i.Content
i.fieldMap["status"] = i.Status
i.fieldMap["remark"] = i.Remark
}
func (i inquiry) clone(db *gorm.DB) inquiry {
i.inquiryDo.ReplaceConnPool(db.Statement.ConnPool)
return i
}
func (i inquiry) replaceDB(db *gorm.DB) inquiry {
i.inquiryDo.ReplaceDB(db)
return i
}
type inquiryDo struct{ gen.DO }
func (i inquiryDo) Debug() *inquiryDo {
return i.withDO(i.DO.Debug())
}
func (i inquiryDo) WithContext(ctx context.Context) *inquiryDo {
return i.withDO(i.DO.WithContext(ctx))
}
func (i inquiryDo) ReadDB() *inquiryDo {
return i.Clauses(dbresolver.Read)
}
func (i inquiryDo) WriteDB() *inquiryDo {
return i.Clauses(dbresolver.Write)
}
func (i inquiryDo) Session(config *gorm.Session) *inquiryDo {
return i.withDO(i.DO.Session(config))
}
func (i inquiryDo) Clauses(conds ...clause.Expression) *inquiryDo {
return i.withDO(i.DO.Clauses(conds...))
}
func (i inquiryDo) Returning(value interface{}, columns ...string) *inquiryDo {
return i.withDO(i.DO.Returning(value, columns...))
}
func (i inquiryDo) Not(conds ...gen.Condition) *inquiryDo {
return i.withDO(i.DO.Not(conds...))
}
func (i inquiryDo) Or(conds ...gen.Condition) *inquiryDo {
return i.withDO(i.DO.Or(conds...))
}
func (i inquiryDo) Select(conds ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Select(conds...))
}
func (i inquiryDo) Where(conds ...gen.Condition) *inquiryDo {
return i.withDO(i.DO.Where(conds...))
}
func (i inquiryDo) Order(conds ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Order(conds...))
}
func (i inquiryDo) Distinct(cols ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Distinct(cols...))
}
func (i inquiryDo) Omit(cols ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Omit(cols...))
}
func (i inquiryDo) Join(table schema.Tabler, on ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Join(table, on...))
}
func (i inquiryDo) LeftJoin(table schema.Tabler, on ...field.Expr) *inquiryDo {
return i.withDO(i.DO.LeftJoin(table, on...))
}
func (i inquiryDo) RightJoin(table schema.Tabler, on ...field.Expr) *inquiryDo {
return i.withDO(i.DO.RightJoin(table, on...))
}
func (i inquiryDo) Group(cols ...field.Expr) *inquiryDo {
return i.withDO(i.DO.Group(cols...))
}
func (i inquiryDo) Having(conds ...gen.Condition) *inquiryDo {
return i.withDO(i.DO.Having(conds...))
}
func (i inquiryDo) Limit(limit int) *inquiryDo {
return i.withDO(i.DO.Limit(limit))
}
func (i inquiryDo) Offset(offset int) *inquiryDo {
return i.withDO(i.DO.Offset(offset))
}
func (i inquiryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *inquiryDo {
return i.withDO(i.DO.Scopes(funcs...))
}
func (i inquiryDo) Unscoped() *inquiryDo {
return i.withDO(i.DO.Unscoped())
}
func (i inquiryDo) Create(values ...*models.Inquiry) error {
if len(values) == 0 {
return nil
}
return i.DO.Create(values)
}
func (i inquiryDo) CreateInBatches(values []*models.Inquiry, batchSize int) error {
return i.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 (i inquiryDo) Save(values ...*models.Inquiry) error {
if len(values) == 0 {
return nil
}
return i.DO.Save(values)
}
func (i inquiryDo) First() (*models.Inquiry, error) {
if result, err := i.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Inquiry), nil
}
}
func (i inquiryDo) Take() (*models.Inquiry, error) {
if result, err := i.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Inquiry), nil
}
}
func (i inquiryDo) Last() (*models.Inquiry, error) {
if result, err := i.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Inquiry), nil
}
}
func (i inquiryDo) Find() ([]*models.Inquiry, error) {
result, err := i.DO.Find()
return result.([]*models.Inquiry), err
}
func (i inquiryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Inquiry, err error) {
buf := make([]*models.Inquiry, 0, batchSize)
err = i.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 (i inquiryDo) FindInBatches(result *[]*models.Inquiry, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return i.DO.FindInBatches(result, batchSize, fc)
}
func (i inquiryDo) Attrs(attrs ...field.AssignExpr) *inquiryDo {
return i.withDO(i.DO.Attrs(attrs...))
}
func (i inquiryDo) Assign(attrs ...field.AssignExpr) *inquiryDo {
return i.withDO(i.DO.Assign(attrs...))
}
func (i inquiryDo) Joins(fields ...field.RelationField) *inquiryDo {
for _, _f := range fields {
i = *i.withDO(i.DO.Joins(_f))
}
return &i
}
func (i inquiryDo) Preload(fields ...field.RelationField) *inquiryDo {
for _, _f := range fields {
i = *i.withDO(i.DO.Preload(_f))
}
return &i
}
func (i inquiryDo) FirstOrInit() (*models.Inquiry, error) {
if result, err := i.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Inquiry), nil
}
}
func (i inquiryDo) FirstOrCreate() (*models.Inquiry, error) {
if result, err := i.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Inquiry), nil
}
}
func (i inquiryDo) FindByPage(offset int, limit int) (result []*models.Inquiry, count int64, err error) {
result, err = i.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 = i.Offset(-1).Limit(-1).Count()
return
}
func (i inquiryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = i.Count()
if err != nil {
return
}
err = i.Offset(offset).Limit(limit).Scan(result)
return
}
func (i inquiryDo) Scan(result interface{}) (err error) {
return i.DO.Scan(result)
}
func (i inquiryDo) Delete(models ...*models.Inquiry) (result gen.ResultInfo, err error) {
return i.DO.Delete(models)
}
func (i *inquiryDo) withDO(do gen.Dao) *inquiryDo {
i.DO = *do.(*gen.DO)
return i
}

View File

@@ -29,7 +29,7 @@ func newLinkAdminRole(db *gorm.DB, opts ...gen.DOOption) linkAdminRole {
_linkAdminRole.ALL = field.NewAsterisk(tableName) _linkAdminRole.ALL = field.NewAsterisk(tableName)
_linkAdminRole.ID = field.NewInt32(tableName, "id") _linkAdminRole.ID = field.NewInt32(tableName, "id")
_linkAdminRole.AdminID = field.NewInt32(tableName, "admin_id") _linkAdminRole.AdminID = field.NewInt32(tableName, "admin_id")
_linkAdminRole.RoleID = field.NewInt32(tableName, "role_id") _linkAdminRole.RoleID = field.NewInt32(tableName, "admin_role_id")
_linkAdminRole.fillFieldMap() _linkAdminRole.fillFieldMap()
@@ -61,7 +61,7 @@ func (l *linkAdminRole) updateTableName(table string) *linkAdminRole {
l.ALL = field.NewAsterisk(table) l.ALL = field.NewAsterisk(table)
l.ID = field.NewInt32(table, "id") l.ID = field.NewInt32(table, "id")
l.AdminID = field.NewInt32(table, "admin_id") l.AdminID = field.NewInt32(table, "admin_id")
l.RoleID = field.NewInt32(table, "role_id") l.RoleID = field.NewInt32(table, "admin_role_id")
l.fillFieldMap() l.fillFieldMap()
@@ -81,7 +81,7 @@ func (l *linkAdminRole) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 3) l.fieldMap = make(map[string]field.Expr, 3)
l.fieldMap["id"] = l.ID l.fieldMap["id"] = l.ID
l.fieldMap["admin_id"] = l.AdminID l.fieldMap["admin_id"] = l.AdminID
l.fieldMap["role_id"] = l.RoleID l.fieldMap["admin_role_id"] = l.RoleID
} }
func (l linkAdminRole) clone(db *gorm.DB) linkAdminRole { func (l linkAdminRole) clone(db *gorm.DB) linkAdminRole {

View File

@@ -28,7 +28,7 @@ func newLinkAdminRolePermission(db *gorm.DB, opts ...gen.DOOption) linkAdminRole
tableName := _linkAdminRolePermission.linkAdminRolePermissionDo.TableName() tableName := _linkAdminRolePermission.linkAdminRolePermissionDo.TableName()
_linkAdminRolePermission.ALL = field.NewAsterisk(tableName) _linkAdminRolePermission.ALL = field.NewAsterisk(tableName)
_linkAdminRolePermission.ID = field.NewInt32(tableName, "id") _linkAdminRolePermission.ID = field.NewInt32(tableName, "id")
_linkAdminRolePermission.RoleID = field.NewInt32(tableName, "role_id") _linkAdminRolePermission.RoleID = field.NewInt32(tableName, "admin_role_id")
_linkAdminRolePermission.PermissionID = field.NewInt32(tableName, "permission_id") _linkAdminRolePermission.PermissionID = field.NewInt32(tableName, "permission_id")
_linkAdminRolePermission.fillFieldMap() _linkAdminRolePermission.fillFieldMap()
@@ -60,7 +60,7 @@ func (l linkAdminRolePermission) As(alias string) *linkAdminRolePermission {
func (l *linkAdminRolePermission) updateTableName(table string) *linkAdminRolePermission { func (l *linkAdminRolePermission) updateTableName(table string) *linkAdminRolePermission {
l.ALL = field.NewAsterisk(table) l.ALL = field.NewAsterisk(table)
l.ID = field.NewInt32(table, "id") l.ID = field.NewInt32(table, "id")
l.RoleID = field.NewInt32(table, "role_id") l.RoleID = field.NewInt32(table, "admin_role_id")
l.PermissionID = field.NewInt32(table, "permission_id") l.PermissionID = field.NewInt32(table, "permission_id")
l.fillFieldMap() l.fillFieldMap()
@@ -80,7 +80,7 @@ func (l *linkAdminRolePermission) GetFieldByName(fieldName string) (field.OrderE
func (l *linkAdminRolePermission) fillFieldMap() { func (l *linkAdminRolePermission) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 3) l.fieldMap = make(map[string]field.Expr, 3)
l.fieldMap["id"] = l.ID l.fieldMap["id"] = l.ID
l.fieldMap["role_id"] = l.RoleID l.fieldMap["admin_role_id"] = l.RoleID
l.fieldMap["permission_id"] = l.PermissionID l.fieldMap["permission_id"] = l.PermissionID
} }

View File

@@ -29,7 +29,7 @@ func newLinkUserRole(db *gorm.DB, opts ...gen.DOOption) linkUserRole {
_linkUserRole.ALL = field.NewAsterisk(tableName) _linkUserRole.ALL = field.NewAsterisk(tableName)
_linkUserRole.ID = field.NewInt32(tableName, "id") _linkUserRole.ID = field.NewInt32(tableName, "id")
_linkUserRole.UserID = field.NewInt32(tableName, "user_id") _linkUserRole.UserID = field.NewInt32(tableName, "user_id")
_linkUserRole.RoleID = field.NewInt32(tableName, "role_id") _linkUserRole.RoleID = field.NewInt32(tableName, "user_role_id")
_linkUserRole.fillFieldMap() _linkUserRole.fillFieldMap()
@@ -61,7 +61,7 @@ func (l *linkUserRole) updateTableName(table string) *linkUserRole {
l.ALL = field.NewAsterisk(table) l.ALL = field.NewAsterisk(table)
l.ID = field.NewInt32(table, "id") l.ID = field.NewInt32(table, "id")
l.UserID = field.NewInt32(table, "user_id") l.UserID = field.NewInt32(table, "user_id")
l.RoleID = field.NewInt32(table, "role_id") l.RoleID = field.NewInt32(table, "user_role_id")
l.fillFieldMap() l.fillFieldMap()
@@ -81,7 +81,7 @@ func (l *linkUserRole) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 3) l.fieldMap = make(map[string]field.Expr, 3)
l.fieldMap["id"] = l.ID l.fieldMap["id"] = l.ID
l.fieldMap["user_id"] = l.UserID l.fieldMap["user_id"] = l.UserID
l.fieldMap["role_id"] = l.RoleID l.fieldMap["user_role_id"] = l.RoleID
} }
func (l linkUserRole) clone(db *gorm.DB) linkUserRole { func (l linkUserRole) clone(db *gorm.DB) linkUserRole {

View File

@@ -28,7 +28,7 @@ func newLinkUserRolePermission(db *gorm.DB, opts ...gen.DOOption) linkUserRolePe
tableName := _linkUserRolePermission.linkUserRolePermissionDo.TableName() tableName := _linkUserRolePermission.linkUserRolePermissionDo.TableName()
_linkUserRolePermission.ALL = field.NewAsterisk(tableName) _linkUserRolePermission.ALL = field.NewAsterisk(tableName)
_linkUserRolePermission.ID = field.NewInt32(tableName, "id") _linkUserRolePermission.ID = field.NewInt32(tableName, "id")
_linkUserRolePermission.RoleID = field.NewInt32(tableName, "role_id") _linkUserRolePermission.RoleID = field.NewInt32(tableName, "user_role_id")
_linkUserRolePermission.PermissionID = field.NewInt32(tableName, "permission_id") _linkUserRolePermission.PermissionID = field.NewInt32(tableName, "permission_id")
_linkUserRolePermission.fillFieldMap() _linkUserRolePermission.fillFieldMap()
@@ -60,7 +60,7 @@ func (l linkUserRolePermission) As(alias string) *linkUserRolePermission {
func (l *linkUserRolePermission) updateTableName(table string) *linkUserRolePermission { func (l *linkUserRolePermission) updateTableName(table string) *linkUserRolePermission {
l.ALL = field.NewAsterisk(table) l.ALL = field.NewAsterisk(table)
l.ID = field.NewInt32(table, "id") l.ID = field.NewInt32(table, "id")
l.RoleID = field.NewInt32(table, "role_id") l.RoleID = field.NewInt32(table, "user_role_id")
l.PermissionID = field.NewInt32(table, "permission_id") l.PermissionID = field.NewInt32(table, "permission_id")
l.fillFieldMap() l.fillFieldMap()
@@ -80,7 +80,7 @@ func (l *linkUserRolePermission) GetFieldByName(fieldName string) (field.OrderEx
func (l *linkUserRolePermission) fillFieldMap() { func (l *linkUserRolePermission) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 3) l.fieldMap = make(map[string]field.Expr, 3)
l.fieldMap["id"] = l.ID l.fieldMap["id"] = l.ID
l.fieldMap["role_id"] = l.RoleID l.fieldMap["user_role_id"] = l.RoleID
l.fieldMap["permission_id"] = l.PermissionID l.fieldMap["permission_id"] = l.PermissionID
} }

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