24 Commits

Author SHA1 Message Date
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
101 changed files with 9300 additions and 1325 deletions

View File

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

View File

@@ -1,51 +1,43 @@
## TODO
前端需要 token 化改造,以避免每次 basic 认证流程中 bcrypt 对比导致的性能对比
优化中间件,配置通用限速
observe 部署,蓝狐部署
---
用户请求需要检查数据权限
用反射实现环境变量解析,以简化函数签名
分离 task 的客户端支持多进程prefork 必要!)
调整目录结构:
```
- /util 工具函数
- /models 模型
- /queries 数据库层
- /clients 三方依赖的客户端实例
- /services 服务层
- /auth 认证相关,特化服务
- /app 应用相关,初始化日志,环境变量,错误类型等
- /http 协议层http 服务
- /cmd 主函数
逐层向上依赖
cmd 调用 app, http 的初始化函数
http 调用 clients 的初始化函数
```
---
数据库转模型文件
分离 task 的客户端支持多进程prefork 必要!)
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
慢速请求底层调用埋点监控
- redis
- gorm
- 三方接口
数据库转模型文件
冷数据迁移方案
## 开发环境
### 更新表结构的流程
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` 下定义了系统所有静态权限
新增系统权限需要在数据库中配套添加权限
前端也需要新增配套权限定义
## 业务逻辑
### 订单关闭的几种方式
@@ -64,7 +56,8 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
### 节点分配与存储逻辑
提取:
提取
- 检查用户套餐与白名单
- 选中代理
- 找到当前可用端口最多的代理
@@ -76,6 +69,7 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
- 分别提交连接与配置请求
释放:
- 根据批次查出所有端口与相关节点
- 分别提交断开与关闭请求
- 释放端口

View File

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

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/gofiber/contrib/otelfiber/v2 v2.2.3
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/hibiken/asynq v0.25.1
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/stdr v1.2.2 // 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/gomodule/redigo v2.0.0+incompatible // 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/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/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/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=

2
pkg/env/env.go vendored
View File

@@ -20,7 +20,7 @@ const (
var (
RunMode = RunModeProd
LogLevel = slog.LevelDebug
TradeExpire = 15 * 60 // 交易过期时间,单位秒。默认 15 分钟
TradeExpire = 15 * 60 // 交易过期时间,单位秒。默认 900 秒(15 分钟
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
DebugHttpDump = false // 是否打印请求和响应的原始数据

View File

@@ -17,14 +17,6 @@ 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 {
return nil
} else {
return P(f(*a))
}
}
// 三元表达式
func Ternary[T any](condition bool, trueValue T, falseValue T) T {
if condition {
@@ -82,7 +74,7 @@ func DateHead(date time.Time) time.Time {
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()
return time.Date(y, m, d, 23, 59, 59, 999999999, date.Location())
}

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])

View File

@@ -2,11 +2,20 @@
-- region 填充数据
-- ====================
insert into client (
client_id, client_secret, redirect_uri, spec, name, type
)
values (
'web', '$2a$10$Ss12mXQgpYyo1CKIZ3URouDm.Lc2KcYJzsvEK2PTIXlv6fHQht45a', '', 3, 'web', 1
);
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', '');
insert into product (code, name, description) values ('short', '短效动态', '短效动态');
insert into product (code, name, description) values ('long', '长效动态', '长效动态');
insert into product (code, name, description) values ('static', '长效静态', '长效静态');
delete from permission where true;
insert into permission
(name, description)
values
('permission:read', '读取权限列表'),
('permission:write', '写入权限'),
('admin-role:read', '读取管理员角色列表'),
('admin-role:write', '写入管理员角色')
;
-- endregion

View File

@@ -200,7 +200,7 @@ create table admin (
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create unique index udx_admin_username on admin (username);
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;
@@ -227,8 +227,8 @@ 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,
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
@@ -258,10 +258,12 @@ drop table if exists "user" cascade;
create table "user" (
id int generated by default as identity primary key,
admin_id int,
discount_id int,
phone text not null unique,
username text,
email text,
password text,
source int default 0,
name text,
avatar text,
status int not null default 1,
@@ -279,6 +281,7 @@ create table "user" (
deleted_at timestamptz
);
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_username on "user" (username) where deleted_at is null;
create unique index udx_user_email on "user" (email) where deleted_at is null;
@@ -288,9 +291,11 @@ create index idx_user_created_at on "user" (created_at) where deleted_at is null
comment on table "user" is '用户表';
comment on column "user".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".username 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".avatar is '头像URL';
comment on column "user".status is '用户状态0-禁用1-正常';
@@ -428,6 +433,7 @@ create table permission (
parent_id int,
name text not null,
description text,
sort int,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -442,6 +448,7 @@ comment on column permission.id is '权限ID';
comment on column permission.parent_id is '父权限ID';
comment on column permission.name is '权限名称';
comment on column permission.description is '权限描述';
comment on column permission.sort is '排序';
comment on column permission.created_at is '创建时间';
comment on column permission.updated_at is '更新时间';
comment on column permission.deleted_at is '删除时间';
@@ -449,65 +456,65 @@ comment on column permission.deleted_at is '删除时间';
-- link_user_role
drop table if exists link_user_role cascade;
create table link_user_role (
id int generated by default as identity primary key,
user_id int not null,
role_id int not null
id int generated by default as identity primary key,
user_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_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表字段注释
comment on table link_user_role is '用户角色关联表';
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.role_id is '角色ID';
comment on column link_user_role.user_role_id is '角色ID';
-- link_admin_role
drop table if exists link_admin_role cascade;
create table link_admin_role (
id int generated by default as identity primary key,
admin_id int not null,
role_id int not null
id int generated by default as identity primary key,
admin_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_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表字段注释
comment on table link_admin_role is '管理员角色关联表';
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.role_id is '角色ID';
comment on column link_admin_role.admin_role_id is '角色ID';
-- link_user_role_permission
drop table if exists link_user_role_permission cascade;
create table link_user_role_permission (
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
);
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);
-- link_user_role_permission表字段注释
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.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';
-- link_admin_role_permission
drop table if exists link_admin_role_permission cascade;
create table link_admin_role_permission (
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
);
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);
-- link_admin_role_permission表字段注释
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.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';
-- link_client_permission
@@ -720,14 +727,88 @@ comment on column product.created_at is '创建时间';
comment on column product.updated_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,
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 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
drop table if exists resource cascade;
create table resource (
id int generated by default as identity primary key,
user_id int not null,
resource_no text,
active bool not null default true,
resource_no text not null,
code text,
type int not null,
active bool not null default true,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -735,6 +816,7 @@ create table resource (
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_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表字段注释
comment on table resource is '套餐表';
@@ -743,6 +825,7 @@ comment on column resource.user_id is '用户ID';
comment on column resource.resource_no is '套餐编号';
comment on column resource.active is '套餐状态';
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.updated_at is '更新时间';
comment on column resource.deleted_at is '删除时间';
@@ -752,6 +835,7 @@ drop table if exists resource_short cascade;
create table resource_short (
id int generated by default as identity primary key,
resource_id int not null,
code text,
live int not null,
type int not null,
quota int not null,
@@ -761,11 +845,13 @@ create table resource_short (
last_at timestamptz
);
create index idx_resource_short_resource_id on resource_short (resource_id);
create index idx_resource_short_code on resource_short (code);
-- resource_short表字段注释
comment on table resource_short is '短效动态套餐表';
comment on column resource_short.id is 'ID';
comment on column resource_short.resource_id is '套餐ID';
comment on column resource_short.code is '产品套餐编码';
comment on column resource_short.live is '可用时长(秒)';
comment on column resource_short.type is '套餐类型1-包时2-包量';
comment on column resource_short.quota is '每日配额(包时)或总配额(包量)';
@@ -779,6 +865,7 @@ drop table if exists resource_long cascade;
create table resource_long (
id int generated by default as identity primary key,
resource_id int not null,
code text,
live int not null,
type int not null,
quota int not null,
@@ -788,11 +875,13 @@ create table resource_long (
last_at timestamptz
);
create index idx_resource_long_resource_id on resource_long (resource_id);
create index idx_resource_long_code on resource_long (code);
-- resource_long表字段注释
comment on table resource_long is '长效动态套餐表';
comment on column resource_long.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.quota is '每日配额(包时)或总配额(包量)';
@@ -895,10 +984,12 @@ create table bill (
trade_id int,
resource_id int,
refund_id int,
coupon_id int,
bill_no text not null,
info text,
type int not null,
amount decimal(12, 2) not null default 0,
actual decimal(12, 2) not null default 0,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -908,6 +999,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_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_coupon_id on bill (coupon_id) where deleted_at is null;
create index idx_bill_created_at on bill (created_at) where deleted_at is null;
-- bill表字段注释
@@ -920,11 +1012,42 @@ comment on column bill.refund_id is '退款ID';
comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息';
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.updated_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 优惠券
drop table if exists coupon cascade;
create table coupon (
@@ -967,6 +1090,8 @@ comment on column coupon.deleted_at is '删除时间';
-- user表外键
alter table "user"
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表外键
alter table session
@@ -982,23 +1107,23 @@ alter table permission
alter table link_user_role
add constraint fk_link_user_role_user_id foreign key (user_id) references "user" (id) on delete cascade;
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表外键
alter table link_admin_role
add constraint fk_link_admin_role_admin_id foreign key (admin_id) references admin (id) on delete cascade;
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表外键
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
add constraint fk_link_user_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;
-- 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
add constraint fk_link_admin_role_permission_permission_id foreign key (permission_id) references permission (id) on delete cascade;
@@ -1025,14 +1150,20 @@ alter table channel
-- resource表外键
alter table resource
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表外键
alter table resource_short
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表外键
alter table resource_long
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表外键
alter table trade
@@ -1053,9 +1184,31 @@ alter table bill
add constraint fk_bill_resource_id foreign key (resource_id) references resource (id) on delete set null;
alter table bill
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_id) references coupon (id) on delete set null;
-- coupon表外键
alter table coupon
add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade;
-- product_sku表外键
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
add constraint fk_product_sku_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;
-- product_sku_user表外键
alter table product_sku_user
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

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

@@ -0,0 +1,175 @@
package auth
import (
"context"
"errors"
"log/slog"
"platform/pkg/u"
"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,
Username: u.P(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)
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 {
scopeNames = append(scopeNames, scope.Name)
}
return scopeNames, nil
}

View File

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

View File

@@ -6,10 +6,8 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"log/slog"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/core"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
@@ -22,67 +20,52 @@ import (
"gorm.io/gorm"
)
type GrantType string
// AuthorizeGet 授权端点
func AuthorizeGet(ctx *fiber.Ctx) error {
const (
GrantAuthorizationCode = GrantType("authorization_code") // 授权码模式
GrantClientCredentials = GrantType("client_credentials") // 客户端凭证模式
GrantRefreshToken = GrantType("refresh_token") // 刷新令牌模式
GrantPassword = GrantType("password") // 密码模式(私有扩展)
)
// 检查请求
req := new(AuthorizeGetReq)
if err := g.Validator.ParseQuery(ctx, req); err != nil {
return err
}
type PasswordGrantType string
// 检查客户端
client, err := authClient(req.ClientID)
if err != nil {
return err
}
const (
GrantPasswordSecret = PasswordGrantType("password") // 账号密码
GrantPasswordPhone = PasswordGrantType("phone_code") // 手机验证码
GrantPasswordEmail = PasswordGrantType("email_code") // 邮箱验证码
)
if client.RedirectURI == nil || *client.RedirectURI != req.RedirectURI {
return errors.New("客户端重定向URI错误")
}
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
// todo 检查 scope
// 授权确认页面
return nil
}
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 AuthorizeGetReq struct {
ResponseType string `json:"response_type" validate:"eq=code"`
ClientID string `json:"client_id" validate:"required"`
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 {
RefreshToken string `json:"refresh_token" form:"refresh_token"`
}
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"`
type AuthorizePostReq struct {
Accept bool `json:"accept"`
Scope string `json:"scope"`
}
// Token 令牌端点
func Token(c *fiber.Ctx) error {
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) {
// 检查 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))
}
err = SaveSession(session)
err = SaveSession(q.Q, session)
if err != nil {
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) {
// todo 检查 scope
scopes := strings.Join(auth.Scopes, " ")
// 生成会话
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
@@ -246,10 +299,11 @@ func authClientCredential(c *fiber.Ctx, auth *AuthCtx, _ *TokenReq, now time.Tim
ClientID: &auth.Client.ID,
AccessToken: uuid.NewString(),
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
Scopes: &scopes,
}
// 保存会话
err := SaveSession(session)
err := SaveSession(q.Q, session)
if err != nil {
return nil, err
}
@@ -261,71 +315,90 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
ip, _ := orm.ParseInet(c.IP()) // 可空字段,忽略异常
ua := u.X(c.Get(fiber.HeaderUserAgent))
// 分池认证
var err error
var user *m.User
err := q.Q.Transaction(func(tx *q.Query) (err error) {
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 admin *m.Admin
// 账户状态
if user.Status == m.UserStatusDisabled {
slog.Debug("账户状态异常", "username", req.Username, "status", user.Status)
return core.NewBizErr("账号无法登录")
var scopes []string
pool := req.LoginPool
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.LastLoginIP = ip
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 err != nil {
return nil, err
// 更新管理员登录时间
admin.LastLogin = u.P(time.Now())
admin.LastLoginIP = ip
admin.LastLoginUA = ua
default:
return nil, ErrAuthorizeInvalidRequest
}
// 生成会话
session := &m.Session{
IP: ip,
UA: ua,
UserID: &user.ID,
ClientID: &auth.Client.ID,
Scopes: u.X(req.Scope),
Scopes: u.X(strings.Join(scopes, " ")),
AccessToken: uuid.NewString(),
AccessTokenExpires: now.Add(time.Duration(env.SessionAccessExpire) * time.Second),
}
if req.Remember {
session.RefreshToken = u.P(uuid.NewString())
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 {
return nil, err
}
@@ -353,7 +426,7 @@ func authRefreshToken(_ *fiber.Ctx, _ *AuthCtx, req *TokenReq, now time.Time) (*
}
// 保存令牌
err = SaveSession(session)
err = SaveSession(q.Q, session)
if err != nil {
return nil, err
}
@@ -394,12 +467,125 @@ func sendError(c *fiber.Ctx, err error, description ...string) error {
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
}
func Introspect() error {
return nil
type RevokeReq struct {
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 = maskPhone(profile.Phone)
}
if profile.IDNo != nil && *profile.IDNo != "" {
profile.IDNo = u.P(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})
}
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:]
}
type CodeContext struct {

View File

@@ -6,15 +6,10 @@ import (
"errors"
"fmt"
"log/slog"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"golang.org/x/crypto/bcrypt"
)
func Authenticate() fiber.Handler {
@@ -118,72 +113,14 @@ func authBasic(_ context.Context, token string) (*AuthCtx, error) {
return nil, fmt.Errorf("客户端认证失败:%w", err)
}
return &AuthCtx{
Client: client,
Scopes: []string{},
}, nil
}
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("客户端密钥错误")
scopes := []string{}
if client.Permissions != nil {
for _, p := range client.Permissions {
scopes = append(scopes, p.Name)
}
}
// todo 查询客户端关联权限
// 组织授权信息(一次性请求)
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
return &AuthCtx{
Client: client,
Scopes: scopes,
}, nil
}

View File

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

View File

@@ -130,3 +130,8 @@ func Query(in any) url.Values {
return out
}
// 数据请求
type IdReq struct {
Id int32 `json:"id"`
}

View File

@@ -15,7 +15,7 @@ type Model struct {
ID int32 `json:"id" gorm:"column:id;primaryKey"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"column:deleted_at"`
}
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"
)

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

@@ -0,0 +1,58 @@
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") // 写入产品套餐
ScopeDiscount = string("discount") // 折扣
ScopeDiscountRead = string("discount:read") // 读取折扣列表
ScopeDiscountWrite = string("discount:write") // 写入折扣
ScopeResource = string("resource") // 用户套餐
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
ScopeResourceWrite = string("resource:write") // 写入用户套餐
ScopeUser = string("user") // 用户
ScopeUserRead = string("user:read") // 读取用户列表
ScopeUserReadOne = string("user:read:one") // 读取单个用户
ScopeUserWrite = string("user:write") // 写入用户
ScopeUserWriteBalance = string("user:write:balance") // 写入用户余额
ScopeUserWriteBind = string("user:write:bind") // 用户认领
ScopeCoupon = string("coupon") // 优惠券
ScopeCouponRead = string("coupon:read") // 读取优惠券列表
ScopeCouponWrite = string("coupon:write") // 写入优惠券
ScopeBatch = string("batch") // 批次
ScopeBatchRead = string("batch:read") // 读取批次列表
ScopeBatchWrite = string("batch:write") // 写入批次
ScopeChannel = string("channel") // IP
ScopeChannelRead = string("channel:read") // 读取 IP 列表
ScopeChannelWrite = string("channel:write") // 写入 IP
ScopeTrade = string("trade") // 交易
ScopeTradeRead = string("trade:read") // 读取交易列表
ScopeTradeWrite = string("trade:write") // 写入交易
ScopeBill = string("bill") // 账单
ScopeBillRead = string("bill:read") // 读取账单列表
ScopeBillWrite = string("bill:write") // 写入账单
)

View File

@@ -1,11 +1,14 @@
package web
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"platform/web/auth"
"platform/web/core"
"reflect"
"time"
"github.com/gofiber/fiber/v2"
)
@@ -19,6 +22,9 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
var authErr auth.AuthErr
var bizErr *core.BizErr
var servErr *core.ServErr
var timeErr *time.ParseError
var jsonErr *json.UnmarshalTypeError
var jsonSyntaxErr *json.SyntaxError
switch {
@@ -48,11 +54,32 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
code = fiber.StatusInternalServerError
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:
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)
return c.Status(code).SendString(message)
}

View File

@@ -9,18 +9,23 @@ import (
"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"`
Method m.TradeMethod `json:"method" validate:"required"`
}
func NewCancelTrade(data CompleteTradeData) *asynq.Task {
bytes, err := json.Marshal(data)
func NewCloseTradeTask(uid int32, tradeNo string, method m.TradeMethod) *asynq.Task {
bytes, err := json.Marshal(CloseTradeData{
UserId: uid,
TradeNo: tradeNo,
Method: method,
})
if err != nil {
slog.Error("序列化更新交易任务失败", "error", err)
return nil
}
return asynq.NewTask(CompleteTrade, bytes)
return asynq.NewTask(CloseTrade, bytes)
}

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

@@ -10,6 +10,7 @@ import (
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"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/utils"
)
@@ -18,6 +19,7 @@ var WechatPay *WechatPayClient
type WechatPayClient struct {
Native *native.NativeApiService
H5 *h5.H5ApiService
Notify *notify.Handler
}
@@ -71,6 +73,7 @@ func initWechatPay() error {
// 创建 WechatPay 服务
WechatPay = &WechatPayClient{
Native: &native.NativeApiService{Client: client},
H5: &h5.H5ApiService{Client: client},
Notify: handler,
}
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.PageAdmins(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.CreateAdmin(&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.UpdateAdmin(&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.RemoveAdmin(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

@@ -1,6 +1,7 @@
package handlers
import (
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
c "platform/web/core"
@@ -11,8 +12,8 @@ import (
"github.com/gofiber/fiber/v2"
)
// PageResourceBatch 分页查询套餐提取记录
func PageResourceBatch(ctx *fiber.Ctx) error {
// PageBatch 分页查询套餐提取记录
func PageBatch(ctx *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(ctx).PermitUser()
if err != nil {
@@ -55,3 +56,75 @@ type PageResourceBatchReq struct {
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.Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.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"`
}

View File

@@ -1,22 +1,104 @@
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"
)
// 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
BillNo *string `json:"bill_no"`
Type *int `json:"type"`
CreateAfter *time.Time `json:"create_after"`
CreateBefore *time.Time `json:"create_before"`
UserPhone *string `json:"user_phone,omitempty"`
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 获取账单列表
@@ -79,4 +161,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/core"
g "platform/web/globals"
"platform/web/globals/orm"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
@@ -14,16 +15,92 @@ import (
"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
AuthType s.ChannelAuthType `json:"auth_type"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
// 解析请求参数
var req PageChannelsByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 构建查询条件
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()
if err != nil {
@@ -86,30 +163,22 @@ func ListChannels(c *fiber.Ctx) error {
})
}
// endregion
// region CreateChannel
type CreateChannelReq struct {
ResourceId int32 `json:"resource_id" validate:"required"`
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
Protocol int `json:"protocol" validate:"required"`
Count int `json:"count" validate:"required"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *int `json:"isp"`
}
type CreateChannelRespItem struct {
Proto int `json:"-"`
Host string `json:"host"`
Port uint16 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
type ListChannelsReq struct {
core.PageReq
AuthType s.ChannelAuthType `json:"auth_type"`
ExpireAfter *time.Time `json:"expire_after"`
ExpireBefore *time.Time `json:"expire_before"`
}
// 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 {
@@ -122,6 +191,10 @@ func CreateChannel(c *fiber.Ctx) error {
}
// 创建通道
var isp *m.EdgeISP
if req.Isp != nil {
isp = u.X(m.ToEdgeISP(*req.Isp))
}
result, err := s.Channel.CreateChannels(
ip,
req.ResourceId,
@@ -129,7 +202,7 @@ func CreateChannel(c *fiber.Ctx) error {
req.AuthType == s.ChannelAuthTypePass,
req.Count,
s.EdgeFilter{
Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
Isp: isp,
Prov: req.Prov,
City: req.City,
},
@@ -154,16 +227,25 @@ func CreateChannel(c *fiber.Ctx) error {
return c.JSON(resp)
}
type CreateChannelResultType string
// endregion
// region RemoveChannels
type RemoveChannelsReq struct {
Batch string `json:"batch" validate:"required"`
type CreateChannelReq struct {
ResourceId int32 `json:"resource_id" validate:"required"`
AuthType s.ChannelAuthType `json:"auth_type" validate:"required"`
Protocol int `json:"protocol" validate:"required"`
Count int `json:"count" validate:"required"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *int `json:"isp"`
}
type CreateChannelRespItem struct {
Proto int `json:"-"`
Host string `json:"host"`
Port uint16 `json:"port"`
Username *string `json:"username,omitempty"`
Password *string `json:"password,omitempty"`
}
// RemoveChannels 删除通道
func RemoveChannels(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
@@ -186,4 +268,6 @@ func RemoveChannels(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
}
// endregion
type RemoveChannelsReq struct {
Batch string `json:"batch" validate:"required"`
}

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"
)
// region 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"`
}
// Identify 发起实名认证
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 更新用户实名认证状态
func IdentifyCallbackNew(c *fiber.Ctx) error {
@@ -110,18 +112,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
Success bool `json:"success" validate:"required"`
})
if err := c.QueryParser(req); err != nil {
return core.NewBizErr("解析请求参数失败", err)
return renderIdenResult(c, false, "解析请求参数失败")
}
// 获取 token
infoStr, err := g.Redis.GetDel(c.Context(), idenKey(req.Id)).Bytes()
if err != nil {
return core.NewBizErr("实名认证状态已失效", err)
return renderIdenResult(c, false, "实名认证状态已失效,请重新发起认证")
}
info := idenInfo{}
err = json.Unmarshal(infoStr, &info)
if err != nil {
return core.NewServErr("解析实名认证信息失败", err)
if err = json.Unmarshal(infoStr, &info); err != nil {
return renderIdenResult(c, false, "解析实名认证信息失败,请重新发起认证")
}
// 获取认证结果
@@ -131,17 +132,17 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
info.Token,
))
if err != nil {
return core.NewServErr("获取实名认证结果失败", err)
return renderIdenResult(c, false, "获取实名认证结果失败,请重新发起认证")
}
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" {
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)).
UpdateSimple(
q.User.IDType.Value(info.Type),
@@ -150,11 +151,41 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
q.User.IDToken.Value(info.Token),
)
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 {

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
}

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

@@ -0,0 +1,220 @@
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 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 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

@@ -15,8 +15,8 @@ import (
"github.com/gofiber/fiber/v2"
)
// ListResourceShort 分页短效套餐
func ListResourceShort(c *fiber.Ctx) error {
// PageResourceShort 分页查询当前用户短效套餐
func PageResourceShort(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil {
@@ -24,7 +24,7 @@ func ListResourceShort(c *fiber.Ctx) error {
}
// 解析请求参数
req := new(ListResourceShortReq)
req := new(PageResourceShortReq)
if err := c.BodyParser(req); err != nil {
return err
}
@@ -55,6 +55,19 @@ func ListResourceShort(c *fiber.Ctx) error {
if req.ExpireBefore != nil {
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).
Joins(q.Resource.Short).
@@ -86,7 +99,7 @@ func ListResourceShort(c *fiber.Ctx) error {
})
}
type ListResourceShortReq struct {
type PageResourceShortReq struct {
core.PageReq
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
@@ -95,10 +108,11 @@ type ListResourceShortReq struct {
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 - 过期
}
// ListResourceLong 分页长效套餐
func ListResourceLong(c *fiber.Ctx) error {
// PageResourceLong 分页查询当前用户长效套餐
func PageResourceLong(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil {
@@ -106,7 +120,7 @@ func ListResourceLong(c *fiber.Ctx) error {
}
// 解析请求参数
req := new(ListResourceLongReq)
req := new(PageResourceLongReq)
if err := c.BodyParser(req); err != nil {
return err
}
@@ -137,6 +151,19 @@ func ListResourceLong(c *fiber.Ctx) error {
if req.ExpireBefore != nil {
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).
Joins(q.Resource.Long).
@@ -168,7 +195,7 @@ func ListResourceLong(c *fiber.Ctx) error {
})
}
type ListResourceLongReq struct {
type PageResourceLongReq struct {
core.PageReq
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
@@ -177,6 +204,193 @@ type ListResourceLongReq struct {
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.ScopeResourceRead)
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.ScopeResourceRead)
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"`
}
// AllActiveResource 所有可用套餐
@@ -238,6 +452,24 @@ func AllActiveResource(c *fiber.Ctx) error {
type AllResourceReq struct {
}
func UpdateResourceByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceWrite)
if err != nil {
return err
}
var req s.UpdateResourceData
if err := c.BodyParser(&req); err != nil {
return err
}
if err := s.Resource.Update(&req); err != nil {
return err
}
return c.JSON(nil)
}
// StatisticResourceFree 统计每日可用
func StatisticResourceFree(c *fiber.Ctx) error {
// 检查权限
@@ -411,7 +643,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 {
return err
}
@@ -438,21 +670,28 @@ func ResourcePrice(c *fiber.Ctx) error {
}
// 获取套餐价格
amount, err := req.GetAmount()
// sku, err := s.Resource.GetSku(req.CreateResourceData.Code())
// if err != nil {
// return err
// }
// _, amount, discounted, couponApplied, err := s.Resource.GetPrice(sku, req.Count(), nil, nil)
// if err != nil {
// return err
// }
detail, err := req.TradeDetail(nil)
if err != nil {
return err
}
// 计算折扣
return c.JSON(ResourcePriceResp{
Price: amount.StringFixed(2),
Discounted: 1,
DiscountedPrice: amount.StringFixed(2),
Price: detail.Amount.StringFixed(2),
Discounted: detail.Actual.StringFixed(2),
})
}
type ResourcePriceResp struct {
Price string `json:"price"`
Discounted float32 `json:"discounted"`
DiscountedPrice string `json:"discounted_price"`
Price string `json:"price"`
Discounted string `json:"discounted_price"`
}

View File

@@ -5,10 +5,12 @@ import (
"fmt"
"log/slog"
"platform/pkg/env"
"platform/pkg/u"
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
@@ -16,18 +18,86 @@ import (
"github.com/valyala/fasthttp"
)
type TradeCreateReq struct {
s.CreateTradeData
Type m.TradeType `json:"type" validate:"required"`
Resource *s.CreateResourceData `json:"resource,omitempty"`
Recharge *s.RechargeProductInfo `json:"recharge,omitempty"`
// PageTradeByAdmin 分页查询所有订单
func PageTradeByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeRead)
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 {
PayUrl string `json:"pay_url"`
TradeNo string `json:"trade_no"`
type PageTradeByAdminReq struct {
core.PageReq
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"`
}
// 创建订单
func TradeCreate(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -40,40 +110,43 @@ func TradeCreate(c *fiber.Ctx) error {
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
switch req.Type {
case m.TradeTypePurchase:
if req.Resource == nil {
return core.NewBizErr("购买信息不能为空")
}
req.Product = req.Resource
case m.TradeTypeRecharge:
if req.Recharge == nil {
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 {
slog.Error("创建交易失败", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "创建交易失败"})
return core.NewServErr("处理购买产品信息失败", err)
}
return c.JSON(&TradeCreateResp{
PayUrl: result.PaymentUrl,
TradeNo: result.TradeNo,
})
return c.JSON(result)
}
type TradeCompleteReq struct {
s.ModifyTradeData
type TradeCreateReq struct {
*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 {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitUser()
authCtx, err := auth.GetAuthCtx(c).PermitUser()
if err != nil {
return err
}
@@ -85,7 +158,7 @@ func TradeComplete(c *fiber.Ctx) error {
}
// 检查订单状态
err = s.Trade.CompleteTrade(&req.ModifyTradeData)
err = s.Trade.CompleteTrade(authCtx.User, &req.TradeRef)
if err != nil {
return err
}
@@ -93,10 +166,11 @@ func TradeComplete(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
type TradeCancelReq struct {
s.ModifyTradeData
type TradeCompleteReq struct {
s.TradeRef
}
// 取消订单
func TradeCancel(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitUser()
@@ -111,7 +185,7 @@ func TradeCancel(c *fiber.Ctx) error {
}
// 取消交易
err = s.Trade.CancelTrade(&req.ModifyTradeData, time.Now())
err = s.Trade.CancelTrade(&req.TradeRef)
if err != nil {
slog.Error("取消交易失败", "trade_no", req.TradeNo, "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "取消交易失败"})
@@ -120,11 +194,14 @@ func TradeCancel(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
type TradeCheckReq struct {
s.ModifyTradeData
type TradeCancelReq struct {
s.TradeRef
}
// 检查订单
func TradeCheck(c *fiber.Ctx) error {
// 检查权限sse 接口暂时不检查权限
// 解析请求参数
req := new(TradeCheckReq)
if err := g.Validator.ParseQuery(c, req); err != nil {
@@ -141,7 +218,7 @@ func TradeCheck(c *fiber.Ctx) error {
interval := 5
for range expire / interval {
// 检查订单状态
result, err := s.Trade.CheckTrade(&req.ModifyTradeData)
result, err := s.Trade.CheckTrade(&req.TradeRef)
if err != nil {
slog.Error("检查订单状态失败", "trade_no", req.TradeNo, "error", err)
return
@@ -170,3 +247,7 @@ func TradeCheck(c *fiber.Ctx) error {
return nil
}
type TradeCheckReq struct {
s.TradeRef
}

View File

@@ -2,23 +2,288 @@ package handlers
import (
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal"
"golang.org/x/crypto/bcrypt"
"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"`
Email string `json:"email" validate:"omitempty,email"`
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
// 解析请求参数
req := new(PageUserByAdminReq)
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))
}
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).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
for _, user := range users {
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).
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 {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -49,15 +314,14 @@ func UpdateUser(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
// endregion
// region /update/account
type UpdateAccountReq struct {
Username string `json:"username" validate:"omitempty,min=3,max=20"`
Password string `json:"password" validate:"omitempty,min=6,max=20"`
type UpdateUserReq struct {
Username string `json:"username" validate:"omitempty,min=3,max=20"`
Email string `json:"email" validate:"omitempty,email"`
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
}
// 更新账号信息
func UpdateAccount(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -86,16 +350,12 @@ func UpdateAccount(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
// endregion
// region /update/password
type UpdatePasswordReq struct {
Phone string `json:"phone"`
Code string `json:"code"`
Password string `json:"password"`
type UpdateAccountReq struct {
Username string `json:"username" validate:"omitempty,min=3,max=20"`
Password string `json:"password" validate:"omitempty,min=6,max=20"`
}
// 更新账号密码
func UpdatePassword(c *fiber.Ctx) error {
// 检查权限
authCtx, err := auth.GetAuthCtx(c).PermitUser()
@@ -109,8 +369,13 @@ func UpdatePassword(c *fiber.Ctx) error {
return err
}
// 验证手机号
if req.Phone != authCtx.User.Phone {
return fiber.NewError(fiber.StatusBadRequest, "手机号码不正确")
}
// 验证手机令牌
if req.Phone == "" || req.Code == "" {
if req.Code == "" {
return fiber.NewError(fiber.StatusBadRequest, "手机号码和验证码不能为空")
}
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
@@ -135,4 +400,8 @@ func UpdatePassword(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
// endregion
type UpdatePasswordReq struct {
Phone string `json:"phone"`
Code string `json:"code"`
Password string `json:"password"`
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/gofiber/contrib/otelfiber/v2"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/requestid"
@@ -19,6 +20,14 @@ func ApplyMiddlewares(app *fiber.App) {
EnableStackTrace: true,
}))
// cors
app.Use(cors.New(cors.Config{
AllowCredentials: true,
AllowOriginsFunc: func(origin string) bool {
return true
},
}))
// logger
app.Use(logger.New(logger.Config{
Next: func(c *fiber.Ctx) bool {

View File

@@ -20,6 +20,8 @@ type Admin struct {
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"` // 最后登录代理
Roles []*AdminRole `json:"roles" gorm:"many2many:link_admin_role"`
}
// AdminStatus 管理员状态枚举

View File

@@ -11,4 +11,6 @@ type AdminRole struct {
Description *string `json:"description,omitempty" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Permissions []*Permission `json:"permissions" gorm:"many2many:link_admin_role_permission"`
}

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 *User `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
}

View File

@@ -13,10 +13,12 @@ type Bill struct {
TradeID *int32 `json:"trade_id,omitempty" gorm:"column:trade_id"` // 订单ID
ResourceID *int32 `json:"resource_id,omitempty" gorm:"column:resource_id"` // 套餐ID
RefundID *int32 `json:"refund_id,omitempty" gorm:"column:refund_id"` // 退款ID
CouponID *int32 `json:"coupon_id,omitempty" gorm:"column:coupon_id"` // 优惠券ID
BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号
Info *string `json:"info,omitempty" gorm:"column:info"` // 产品可读信息
Type BillType `json:"type" gorm:"column:type"` // 账单类型1-消费2-退款3-充值
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 账单金额
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 应付金额
Actual decimal.Decimal `json:"actual" gorm:"column:actual"` // 实付金额
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Trade *Trade `json:"trade,omitempty" gorm:"foreignKey:TradeID"`

View File

@@ -15,6 +15,8 @@ type Client struct {
Icon *string `json:"icon,omitempty" gorm:"column:icon"` // 图标URL
Status ClientStatus `json:"status" gorm:"column:status"` // 状态0-禁用1-正常
Type ClientType `json:"type" gorm:"column:type"` // 类型0-普通1-官方
Permissions []*Permission `json:"permissions" gorm:"many2many:link_client_permission"`
}
// ClientSpec 客户端安全规范枚举

View File

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

View File

@@ -3,6 +3,6 @@ package models
// LinkAdminRolePermission 管理员角色权限关联表
type LinkAdminRolePermission struct {
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
}

View File

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

View File

@@ -3,6 +3,6 @@ package models
// LinkUserRolePermission 用户角色权限关联表
type LinkUserRolePermission struct {
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
}

View File

@@ -17,4 +17,7 @@ type LogsUserUsage struct {
ISP *string `json:"isp,omitempty" gorm:"column:isp"` // 运营商
IP orm.Inet `json:"ip" gorm:"column:ip"` // IP地址
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

@@ -8,6 +8,7 @@ type Permission struct {
ParentID *int32 `json:"parent_id,omitempty" gorm:"column:parent_id"` // 父权限ID
Name string `json:"name" gorm:"column:name"` // 权限名称
Description *string `json:"description,omitempty" gorm:"column:description"` // 权限描述
Sort int `json:"sort" gorm:"column:sort"` // 排序
Parent *Permission `json:"parent,omitempty" gorm:"foreignKey:ParentID"`
Children []*Permission `json:"children,omitempty" gorm:"foreignKey:ParentID"`

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))
}

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

@@ -0,0 +1,20 @@
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"` // 定价
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
}

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

@@ -11,10 +11,12 @@ type Resource struct {
ResourceNo *string `json:"resource_no,omitempty" gorm:"column:resource_no"` // 套餐编号
Active bool `json:"active" gorm:"column:active"` // 套餐状态
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型1-短效动态2-长效动态
Code string `json:"code" gorm:"column:code"` // 产品编码
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`
Long *ResourceLong `json:"long,omitempty" gorm:"foreignKey:ResourceID"`
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`
Long *ResourceLong `json:"long,omitempty" gorm:"foreignKey:ResourceID"`
Product *Product `json:"product,omitempty" gorm:"foreignKey:Code;references:Code"`
}
// ResourceType 套餐类型枚举
@@ -25,10 +27,32 @@ const (
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
const (
ResourceModeTime ResourceMode = 1 // 包时
ResourceModeQuota ResourceMode = 2 // 包量
)
func (m ResourceMode) Code() string {
switch m {
case ResourceModeTime:
return "time"
case ResourceModeQuota:
return "quota"
default:
return "unknown"
}
}

View File

@@ -8,6 +8,7 @@ import (
type ResourceLong struct {
ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Code string `json:"code" gorm:"column:code"` // 套餐编码
Live int32 `json:"live" gorm:"column:live"` // 可用时长(小时)
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
@@ -15,4 +16,6 @@ type ResourceLong struct {
Used int32 `json:"used" gorm:"column:used"` // 总用量
Daily int32 `json:"daily" gorm:"column:daily"` // 当日用量
LastAt *time.Time `json:"last_at,omitempty" gorm:"column:last_at"` // 最后使用时间
Sku *ProductSku `json:"sku,omitempty" gorm:"foreignKey:Code;references:Code"`
}

View File

@@ -8,6 +8,7 @@ import (
type ResourceShort struct {
ID int32 `json:"id" gorm:"column:id"` // ID
ResourceID int32 `json:"resource_id" gorm:"column:resource_id"` // 套餐ID
Code string `json:"code" gorm:"column:code"` // 套餐编码
Live int32 `json:"live" gorm:"column:live"` // 可用时长(秒)
Type ResourceMode `json:"type" gorm:"column:type"` // 套餐类型1-包时2-包量
Quota int32 `json:"quota" gorm:"column:quota"` // 每日配额(包时)或总配额(包量)
@@ -15,4 +16,6 @@ type ResourceShort struct {
Used int32 `json:"used" gorm:"column:used"` // 总用量
Daily int32 `json:"daily" gorm:"column:daily"` // 当日用量
LastAt *time.Time `json:"last_at,omitempty" gorm:"column:last_at"` // 最后使用时间
Sku *ProductSku `json:"sku,omitempty" gorm:"foreignKey:Code;references:Code"`
}

View File

@@ -26,6 +26,8 @@ type Trade struct {
PaymentURL *string `json:"payment_url,omitempty" gorm:"column:payment_url"` // 支付链接
CompletedAt *time.Time `json:"completed_at,omitempty" gorm:"column:completed_at"` // 支付时间
CanceledAt *time.Time `json:"canceled_at,omitempty" gorm:"column:canceled_at"` // 取消时间
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
}
// TradeType 订单类型枚举

View File

@@ -12,10 +12,12 @@ import (
type User struct {
core.Model
AdminID *int32 `json:"admin_id,omitempty" gorm:"column:admin_id"` // 管理员ID
DiscountID *int32 `json:"discount_id,omitempty" gorm:"column:discount_id"` // 折扣ID
Phone string `json:"phone" gorm:"column:phone"` // 手机号码
Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
Password *string `json:"password,omitempty" gorm:"column:password"` // 用户密码
Source *UserSource `json:"source,omitempty" gorm:"column:source"` // 用户来源0-官网注册1-管理员添加2-代理商注册3-代理商添加
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
Status UserStatus `json:"status" gorm:"column:status"` // 用户状态0-禁用1-正常
@@ -29,7 +31,9 @@ type User struct {
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,omitempty" 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 用户状态枚举
@@ -48,3 +52,13 @@ const (
UserIDTypePersonal UserIDType = 1 // 个人认证
UserIDTypeEnterprise UserIDType = 2 // 企业认证
)
// UserSource 用户来源枚举
type UserSource int
const (
UserSourceReg UserSource = 0 // 官网注册
UserSourceAdd UserSource = 1 // 管理员添加
UserSourceAgentReg UserSource = 2 // 代理商注册
UserSourceAgentAdd UserSource = 3 // 代理商添加
)

View File

@@ -11,4 +11,6 @@ type UserRole struct {
Description *string `json:"description,omitempty" gorm:"column:description"` // 角色描述
Active bool `json:"active" gorm:"column:active"` // 是否激活
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Permissions []*Permission `json:"permissions" gorm:"many2many:link_user_role_permission"`
}

View File

@@ -41,6 +41,32 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
_admin.LastLogin = field.NewTime(tableName, "last_login")
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
_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()
@@ -65,6 +91,7 @@ type admin struct {
LastLogin field.Time
LastLoginIP field.Field
LastLoginUA field.String
Roles adminManyToManyRoles
fieldMap map[string]field.Expr
}
@@ -111,7 +138,7 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 14)
a.fieldMap = make(map[string]field.Expr, 15)
a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
@@ -126,18 +153,113 @@ func (a *admin) fillFieldMap() {
a.fieldMap["last_login"] = a.LastLogin
a.fieldMap["last_login_ip"] = a.LastLoginIP
a.fieldMap["last_login_ua"] = a.LastLoginUA
}
func (a admin) clone(db *gorm.DB) admin {
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
}
func (a admin) replaceDB(db *gorm.DB) admin {
a.adminDo.ReplaceDB(db)
a.Roles.db = db.Session(&gorm.Session{})
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 }
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.Active = field.NewBool(tableName, "active")
_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()
@@ -53,6 +68,7 @@ type adminRole struct {
Description field.String
Active field.Bool
Sort field.Int32
Permissions adminRoleManyToManyPermissions
fieldMap map[string]field.Expr
}
@@ -93,7 +109,7 @@ func (a *adminRole) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
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["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
@@ -102,18 +118,110 @@ func (a *adminRole) fillFieldMap() {
a.fieldMap["description"] = a.Description
a.fieldMap["active"] = a.Active
a.fieldMap["sort"] = a.Sort
}
func (a adminRole) clone(db *gorm.DB) adminRole {
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
}
func (a adminRole) replaceDB(db *gorm.DB) adminRole {
a.adminRoleDo.ReplaceDB(db)
a.Permissions.db = db.Session(&gorm.Session{})
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 }
func (a adminRoleDo) Debug() *adminRoleDo {

View File

@@ -0,0 +1,871 @@
// 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.Admin = balanceActivityHasOneAdmin{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Admin", "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("Admin.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Admin.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Admin.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Roles.Permissions", "models.Permission"),
},
},
}
_balanceActivity.User = balanceActivityBelongsToUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("User", "models.User"),
}
_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
}
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
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Bill.Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
},
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"),
},
}
_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
Admin balanceActivityHasOneAdmin
User balanceActivityBelongsToUser
Bill balanceActivityBelongsToBill
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.Admin.db = db.Session(&gorm.Session{Initialized: true})
b.Admin.db.Statement.ConnPool = 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
return b
}
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
b.balanceActivityDo.ReplaceDB(db)
b.Admin.db = db.Session(&gorm.Session{})
b.User.db = db.Session(&gorm.Session{})
b.Bill.db = db.Session(&gorm.Session{})
return b
}
type balanceActivityHasOneAdmin 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 balanceActivityHasOneAdmin) Where(conds ...field.Expr) *balanceActivityHasOneAdmin {
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 balanceActivityHasOneAdmin) WithContext(ctx context.Context) *balanceActivityHasOneAdmin {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityHasOneAdmin) Session(session *gorm.Session) *balanceActivityHasOneAdmin {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityHasOneAdmin) Model(m *models.BalanceActivity) *balanceActivityHasOneAdminTx {
return &balanceActivityHasOneAdminTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityHasOneAdmin) Unscoped() *balanceActivityHasOneAdmin {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityHasOneAdminTx struct{ tx *gorm.Association }
func (a balanceActivityHasOneAdminTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityHasOneAdminTx) 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 balanceActivityHasOneAdminTx) 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 balanceActivityHasOneAdminTx) 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 balanceActivityHasOneAdminTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityHasOneAdminTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityHasOneAdminTx) Unscoped() *balanceActivityHasOneAdminTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityBelongsToUser struct {
db *gorm.DB
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
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}
Refund 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 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.ResourceID = field.NewInt32(tableName, "resource_id")
_bill.RefundID = field.NewInt32(tableName, "refund_id")
_bill.CouponID = field.NewInt32(tableName, "coupon_id")
_bill.BillNo = field.NewString(tableName, "bill_no")
_bill.Info = field.NewString(tableName, "info")
_bill.Type = field.NewInt(tableName, "type")
_bill.Amount = field.NewField(tableName, "amount")
_bill.Actual = field.NewField(tableName, "actual")
_bill.User = billBelongsToUser{
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"),
},
},
}
@@ -54,6 +121,11 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Trade", "models.Trade"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Trade.User", "models.User"),
},
}
_bill.Resource = billBelongsToResource{
@@ -67,13 +139,56 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
},
Short: struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
},
Long: struct {
field.RelationField
Sku struct {
field.RelationField
}
}{
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"),
},
}
@@ -100,10 +215,12 @@ type bill struct {
TradeID field.Int32
ResourceID field.Int32
RefundID field.Int32
CouponID field.Int32
BillNo field.String
Info field.String
Type field.Int
Amount field.Field
Actual field.Field
User billBelongsToUser
Trade billBelongsToTrade
@@ -135,10 +252,12 @@ func (b *bill) updateTableName(table string) *bill {
b.TradeID = field.NewInt32(table, "trade_id")
b.ResourceID = field.NewInt32(table, "resource_id")
b.RefundID = field.NewInt32(table, "refund_id")
b.CouponID = field.NewInt32(table, "coupon_id")
b.BillNo = field.NewString(table, "bill_no")
b.Info = field.NewString(table, "info")
b.Type = field.NewInt(table, "type")
b.Amount = field.NewField(table, "amount")
b.Actual = field.NewField(table, "actual")
b.fillFieldMap()
@@ -155,7 +274,7 @@ func (b *bill) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (b *bill) fillFieldMap() {
b.fieldMap = make(map[string]field.Expr, 16)
b.fieldMap = make(map[string]field.Expr, 18)
b.fieldMap["id"] = b.ID
b.fieldMap["created_at"] = b.CreatedAt
b.fieldMap["updated_at"] = b.UpdatedAt
@@ -164,10 +283,12 @@ func (b *bill) fillFieldMap() {
b.fieldMap["trade_id"] = b.TradeID
b.fieldMap["resource_id"] = b.ResourceID
b.fieldMap["refund_id"] = b.RefundID
b.fieldMap["coupon_id"] = b.CouponID
b.fieldMap["bill_no"] = b.BillNo
b.fieldMap["info"] = b.Info
b.fieldMap["type"] = b.Type
b.fieldMap["amount"] = b.Amount
b.fieldMap["actual"] = b.Actual
}
@@ -200,6 +321,27 @@ type billBelongsToUser struct {
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
}
}
}
@@ -282,6 +424,10 @@ type billBelongsToTrade struct {
db *gorm.DB
field.RelationField
User struct {
field.RelationField
}
}
func (a billBelongsToTrade) Where(conds ...field.Expr) *billBelongsToTrade {
@@ -369,9 +515,24 @@ type billBelongsToResource struct {
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}

View File

@@ -53,8 +53,73 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
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"),
},
},
}
@@ -69,13 +134,56 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
},
Short: struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
},
Long: struct {
field.RelationField
Sku struct {
field.RelationField
}
}{
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"),
},
}
@@ -269,6 +377,27 @@ type channelBelongsToUser struct {
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
}
}
}
@@ -357,9 +486,24 @@ type channelBelongsToResource struct {
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
Long struct {
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.Status = field.NewInt(tableName, "status")
_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()
@@ -61,6 +76,7 @@ type client struct {
Icon field.String
Status field.Int
Type field.Int
Permissions clientManyToManyPermissions
fieldMap map[string]field.Expr
}
@@ -105,7 +121,7 @@ func (c *client) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
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["created_at"] = c.CreatedAt
c.fieldMap["updated_at"] = c.UpdatedAt
@@ -118,18 +134,110 @@ func (c *client) fillFieldMap() {
c.fieldMap["icon"] = c.Icon
c.fieldMap["status"] = c.Status
c.fieldMap["type"] = c.Type
}
func (c client) clone(db *gorm.DB) client {
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
}
func (c client) replaceDB(db *gorm.DB) client {
c.clientDo.ReplaceDB(db)
c.Permissions.db = db.Session(&gorm.Session{})
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 }
func (c clientDo) Debug() *clientDo {

View File

@@ -20,6 +20,7 @@ var (
Admin *admin
AdminRole *adminRole
Announcement *announcement
BalanceActivity *balanceActivity
Bill *bill
Channel *channel
Client *client
@@ -37,6 +38,9 @@ var (
LogsUserUsage *logsUserUsage
Permission *permission
Product *product
ProductDiscount *productDiscount
ProductSku *productSku
ProductSkuUser *productSkuUser
Proxy *proxy
Refund *refund
Resource *resource
@@ -54,6 +58,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Admin = &Q.Admin
AdminRole = &Q.AdminRole
Announcement = &Q.Announcement
BalanceActivity = &Q.BalanceActivity
Bill = &Q.Bill
Channel = &Q.Channel
Client = &Q.Client
@@ -71,6 +76,9 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
LogsUserUsage = &Q.LogsUserUsage
Permission = &Q.Permission
Product = &Q.Product
ProductDiscount = &Q.ProductDiscount
ProductSku = &Q.ProductSku
ProductSkuUser = &Q.ProductSkuUser
Proxy = &Q.Proxy
Refund = &Q.Refund
Resource = &Q.Resource
@@ -89,6 +97,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Admin: newAdmin(db, opts...),
AdminRole: newAdminRole(db, opts...),
Announcement: newAnnouncement(db, opts...),
BalanceActivity: newBalanceActivity(db, opts...),
Bill: newBill(db, opts...),
Channel: newChannel(db, opts...),
Client: newClient(db, opts...),
@@ -106,6 +115,9 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
LogsUserUsage: newLogsUserUsage(db, opts...),
Permission: newPermission(db, opts...),
Product: newProduct(db, opts...),
ProductDiscount: newProductDiscount(db, opts...),
ProductSku: newProductSku(db, opts...),
ProductSkuUser: newProductSkuUser(db, opts...),
Proxy: newProxy(db, opts...),
Refund: newRefund(db, opts...),
Resource: newResource(db, opts...),
@@ -125,6 +137,7 @@ type Query struct {
Admin admin
AdminRole adminRole
Announcement announcement
BalanceActivity balanceActivity
Bill bill
Channel channel
Client client
@@ -142,6 +155,9 @@ type Query struct {
LogsUserUsage logsUserUsage
Permission permission
Product product
ProductDiscount productDiscount
ProductSku productSku
ProductSkuUser productSkuUser
Proxy proxy
Refund refund
Resource resource
@@ -162,6 +178,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
Admin: q.Admin.clone(db),
AdminRole: q.AdminRole.clone(db),
Announcement: q.Announcement.clone(db),
BalanceActivity: q.BalanceActivity.clone(db),
Bill: q.Bill.clone(db),
Channel: q.Channel.clone(db),
Client: q.Client.clone(db),
@@ -179,6 +196,9 @@ func (q *Query) clone(db *gorm.DB) *Query {
LogsUserUsage: q.LogsUserUsage.clone(db),
Permission: q.Permission.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),
Refund: q.Refund.clone(db),
Resource: q.Resource.clone(db),
@@ -206,6 +226,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Admin: q.Admin.replaceDB(db),
AdminRole: q.AdminRole.replaceDB(db),
Announcement: q.Announcement.replaceDB(db),
BalanceActivity: q.BalanceActivity.replaceDB(db),
Bill: q.Bill.replaceDB(db),
Channel: q.Channel.replaceDB(db),
Client: q.Client.replaceDB(db),
@@ -223,6 +244,9 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
LogsUserUsage: q.LogsUserUsage.replaceDB(db),
Permission: q.Permission.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),
Refund: q.Refund.replaceDB(db),
Resource: q.Resource.replaceDB(db),
@@ -240,6 +264,7 @@ type queryCtx struct {
Admin *adminDo
AdminRole *adminRoleDo
Announcement *announcementDo
BalanceActivity *balanceActivityDo
Bill *billDo
Channel *channelDo
Client *clientDo
@@ -257,6 +282,9 @@ type queryCtx struct {
LogsUserUsage *logsUserUsageDo
Permission *permissionDo
Product *productDo
ProductDiscount *productDiscountDo
ProductSku *productSkuDo
ProductSkuUser *productSkuUserDo
Proxy *proxyDo
Refund *refundDo
Resource *resourceDo
@@ -274,6 +302,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Admin: q.Admin.WithContext(ctx),
AdminRole: q.AdminRole.WithContext(ctx),
Announcement: q.Announcement.WithContext(ctx),
BalanceActivity: q.BalanceActivity.WithContext(ctx),
Bill: q.Bill.WithContext(ctx),
Channel: q.Channel.WithContext(ctx),
Client: q.Client.WithContext(ctx),
@@ -291,6 +320,9 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
LogsUserUsage: q.LogsUserUsage.WithContext(ctx),
Permission: q.Permission.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),
Refund: q.Refund.WithContext(ctx),
Resource: q.Resource.WithContext(ctx),

View File

@@ -29,7 +29,7 @@ func newLinkAdminRole(db *gorm.DB, opts ...gen.DOOption) linkAdminRole {
_linkAdminRole.ALL = field.NewAsterisk(tableName)
_linkAdminRole.ID = field.NewInt32(tableName, "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()
@@ -61,7 +61,7 @@ func (l *linkAdminRole) updateTableName(table string) *linkAdminRole {
l.ALL = field.NewAsterisk(table)
l.ID = field.NewInt32(table, "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()
@@ -81,7 +81,7 @@ func (l *linkAdminRole) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 3)
l.fieldMap["id"] = l.ID
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 {

View File

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

View File

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

View File

@@ -41,8 +41,73 @@ func newLogsLogin(db *gorm.DB, opts ...gen.DOOption) logsLogin {
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"),
},
},
}
@@ -136,6 +201,27 @@ type logsLoginBelongsToUser struct {
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
}
}
}

View File

@@ -42,6 +42,27 @@ func newLogsRequest(db *gorm.DB, opts ...gen.DOOption) logsRequest {
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Client", "models.Client"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Client.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Client.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Client.Permissions.Children", "models.Permission"),
},
},
}
_logsRequest.User = logsRequestBelongsToUser{
@@ -50,8 +71,45 @@ func newLogsRequest(db *gorm.DB, opts ...gen.DOOption) logsRequest {
RelationField: field.NewRelation("User", "models.User"),
Admin: struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "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"),
},
},
}
@@ -156,6 +214,16 @@ type logsRequestHasOneClient struct {
db *gorm.DB
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
func (a logsRequestHasOneClient) Where(conds ...field.Expr) *logsRequestHasOneClient {
@@ -240,6 +308,21 @@ type logsRequestBelongsToUser struct {
Admin struct {
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
}
}
Discount struct {
field.RelationField
}
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
}
}
}

View File

@@ -37,6 +37,145 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
_logsUserUsage.ISP = field.NewString(tableName, "isp")
_logsUserUsage.IP = field.NewField(tableName, "ip")
_logsUserUsage.Time = field.NewTime(tableName, "time")
_logsUserUsage.User = logsUserUsageBelongsToUser{
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"),
},
},
}
_logsUserUsage.Resource = logsUserUsageBelongsToResource{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Resource", "models.Resource"),
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.User", "models.User"),
},
Short: struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
},
Long: struct {
field.RelationField
Sku struct {
field.RelationField
}
}{
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"),
},
}
_logsUserUsage.fillFieldMap()
@@ -57,6 +196,9 @@ type logsUserUsage struct {
ISP field.String
IP field.Field
Time field.Time
User logsUserUsageBelongsToUser
Resource logsUserUsageBelongsToResource
fieldMap map[string]field.Expr
}
@@ -99,7 +241,7 @@ func (l *logsUserUsage) GetFieldByName(fieldName string) (field.OrderExpr, bool)
}
func (l *logsUserUsage) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 10)
l.fieldMap = make(map[string]field.Expr, 12)
l.fieldMap["id"] = l.ID
l.fieldMap["user_id"] = l.UserID
l.fieldMap["resource_id"] = l.ResourceID
@@ -110,18 +252,237 @@ func (l *logsUserUsage) fillFieldMap() {
l.fieldMap["isp"] = l.ISP
l.fieldMap["ip"] = l.IP
l.fieldMap["time"] = l.Time
}
func (l logsUserUsage) clone(db *gorm.DB) logsUserUsage {
l.logsUserUsageDo.ReplaceConnPool(db.Statement.ConnPool)
l.User.db = db.Session(&gorm.Session{Initialized: true})
l.User.db.Statement.ConnPool = db.Statement.ConnPool
l.Resource.db = db.Session(&gorm.Session{Initialized: true})
l.Resource.db.Statement.ConnPool = db.Statement.ConnPool
return l
}
func (l logsUserUsage) replaceDB(db *gorm.DB) logsUserUsage {
l.logsUserUsageDo.ReplaceDB(db)
l.User.db = db.Session(&gorm.Session{})
l.Resource.db = db.Session(&gorm.Session{})
return l
}
type logsUserUsageBelongsToUser 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 logsUserUsageBelongsToUser) Where(conds ...field.Expr) *logsUserUsageBelongsToUser {
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 logsUserUsageBelongsToUser) WithContext(ctx context.Context) *logsUserUsageBelongsToUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a logsUserUsageBelongsToUser) Session(session *gorm.Session) *logsUserUsageBelongsToUser {
a.db = a.db.Session(session)
return &a
}
func (a logsUserUsageBelongsToUser) Model(m *models.LogsUserUsage) *logsUserUsageBelongsToUserTx {
return &logsUserUsageBelongsToUserTx{a.db.Model(m).Association(a.Name())}
}
func (a logsUserUsageBelongsToUser) Unscoped() *logsUserUsageBelongsToUser {
a.db = a.db.Unscoped()
return &a
}
type logsUserUsageBelongsToUserTx struct{ tx *gorm.Association }
func (a logsUserUsageBelongsToUserTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a logsUserUsageBelongsToUserTx) 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 logsUserUsageBelongsToUserTx) 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 logsUserUsageBelongsToUserTx) 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 logsUserUsageBelongsToUserTx) Clear() error {
return a.tx.Clear()
}
func (a logsUserUsageBelongsToUserTx) Count() int64 {
return a.tx.Count()
}
func (a logsUserUsageBelongsToUserTx) Unscoped() *logsUserUsageBelongsToUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type logsUserUsageBelongsToResource struct {
db *gorm.DB
field.RelationField
User struct {
field.RelationField
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}
func (a logsUserUsageBelongsToResource) Where(conds ...field.Expr) *logsUserUsageBelongsToResource {
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 logsUserUsageBelongsToResource) WithContext(ctx context.Context) *logsUserUsageBelongsToResource {
a.db = a.db.WithContext(ctx)
return &a
}
func (a logsUserUsageBelongsToResource) Session(session *gorm.Session) *logsUserUsageBelongsToResource {
a.db = a.db.Session(session)
return &a
}
func (a logsUserUsageBelongsToResource) Model(m *models.LogsUserUsage) *logsUserUsageBelongsToResourceTx {
return &logsUserUsageBelongsToResourceTx{a.db.Model(m).Association(a.Name())}
}
func (a logsUserUsageBelongsToResource) Unscoped() *logsUserUsageBelongsToResource {
a.db = a.db.Unscoped()
return &a
}
type logsUserUsageBelongsToResourceTx struct{ tx *gorm.Association }
func (a logsUserUsageBelongsToResourceTx) Find() (result *models.Resource, err error) {
return result, a.tx.Find(&result)
}
func (a logsUserUsageBelongsToResourceTx) Append(values ...*models.Resource) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a logsUserUsageBelongsToResourceTx) Replace(values ...*models.Resource) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a logsUserUsageBelongsToResourceTx) Delete(values ...*models.Resource) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a logsUserUsageBelongsToResourceTx) Clear() error {
return a.tx.Clear()
}
func (a logsUserUsageBelongsToResourceTx) Count() int64 {
return a.tx.Count()
}
func (a logsUserUsageBelongsToResourceTx) Unscoped() *logsUserUsageBelongsToResourceTx {
a.tx = a.tx.Unscoped()
return &a
}
type logsUserUsageDo struct{ gen.DO }
func (l logsUserUsageDo) Debug() *logsUserUsageDo {

View File

@@ -34,6 +34,7 @@ func newPermission(db *gorm.DB, opts ...gen.DOOption) permission {
_permission.ParentID = field.NewInt32(tableName, "parent_id")
_permission.Name = field.NewString(tableName, "name")
_permission.Description = field.NewString(tableName, "description")
_permission.Sort = field.NewInt(tableName, "sort")
_permission.Children = permissionHasManyChildren{
db: db.Session(&gorm.Session{}),
@@ -72,6 +73,7 @@ type permission struct {
ParentID field.Int32
Name field.String
Description field.String
Sort field.Int
Children permissionHasManyChildren
Parent permissionBelongsToParent
@@ -98,6 +100,7 @@ func (p *permission) updateTableName(table string) *permission {
p.ParentID = field.NewInt32(table, "parent_id")
p.Name = field.NewString(table, "name")
p.Description = field.NewString(table, "description")
p.Sort = field.NewInt(table, "sort")
p.fillFieldMap()
@@ -114,7 +117,7 @@ func (p *permission) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (p *permission) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 9)
p.fieldMap = make(map[string]field.Expr, 10)
p.fieldMap["id"] = p.ID
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
@@ -122,6 +125,7 @@ func (p *permission) fillFieldMap() {
p.fieldMap["parent_id"] = p.ParentID
p.fieldMap["name"] = p.Name
p.fieldMap["description"] = p.Description
p.fieldMap["sort"] = p.Sort
}

View File

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

View File

@@ -0,0 +1,534 @@
// 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 newProductSku(db *gorm.DB, opts ...gen.DOOption) productSku {
_productSku := productSku{}
_productSku.productSkuDo.UseDB(db, opts...)
_productSku.productSkuDo.UseModel(&models.ProductSku{})
tableName := _productSku.productSkuDo.TableName()
_productSku.ALL = field.NewAsterisk(tableName)
_productSku.ID = field.NewInt32(tableName, "id")
_productSku.CreatedAt = field.NewTime(tableName, "created_at")
_productSku.UpdatedAt = field.NewTime(tableName, "updated_at")
_productSku.DeletedAt = field.NewField(tableName, "deleted_at")
_productSku.ProductID = field.NewInt32(tableName, "product_id")
_productSku.DiscountId = field.NewInt32(tableName, "discount_id")
_productSku.Code = field.NewString(tableName, "code")
_productSku.Name = field.NewString(tableName, "name")
_productSku.Price = field.NewField(tableName, "price")
_productSku.Product = productSkuBelongsToProduct{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Product", "models.Product"),
}
_productSku.Discount = productSkuBelongsToDiscount{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Discount", "models.ProductDiscount"),
}
_productSku.fillFieldMap()
return _productSku
}
type productSku struct {
productSkuDo
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
ProductID field.Int32
DiscountId field.Int32
Code field.String
Name field.String
Price field.Field
Product productSkuBelongsToProduct
Discount productSkuBelongsToDiscount
fieldMap map[string]field.Expr
}
func (p productSku) Table(newTableName string) *productSku {
p.productSkuDo.UseTable(newTableName)
return p.updateTableName(newTableName)
}
func (p productSku) As(alias string) *productSku {
p.productSkuDo.DO = *(p.productSkuDo.As(alias).(*gen.DO))
return p.updateTableName(alias)
}
func (p *productSku) updateTableName(table string) *productSku {
p.ALL = field.NewAsterisk(table)
p.ID = field.NewInt32(table, "id")
p.CreatedAt = field.NewTime(table, "created_at")
p.UpdatedAt = field.NewTime(table, "updated_at")
p.DeletedAt = field.NewField(table, "deleted_at")
p.ProductID = field.NewInt32(table, "product_id")
p.DiscountId = field.NewInt32(table, "discount_id")
p.Code = field.NewString(table, "code")
p.Name = field.NewString(table, "name")
p.Price = field.NewField(table, "price")
p.fillFieldMap()
return p
}
func (p *productSku) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := p.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (p *productSku) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 11)
p.fieldMap["id"] = p.ID
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
p.fieldMap["deleted_at"] = p.DeletedAt
p.fieldMap["product_id"] = p.ProductID
p.fieldMap["discount_id"] = p.DiscountId
p.fieldMap["code"] = p.Code
p.fieldMap["name"] = p.Name
p.fieldMap["price"] = p.Price
}
func (p productSku) clone(db *gorm.DB) productSku {
p.productSkuDo.ReplaceConnPool(db.Statement.ConnPool)
p.Product.db = db.Session(&gorm.Session{Initialized: true})
p.Product.db.Statement.ConnPool = db.Statement.ConnPool
p.Discount.db = db.Session(&gorm.Session{Initialized: true})
p.Discount.db.Statement.ConnPool = db.Statement.ConnPool
return p
}
func (p productSku) replaceDB(db *gorm.DB) productSku {
p.productSkuDo.ReplaceDB(db)
p.Product.db = db.Session(&gorm.Session{})
p.Discount.db = db.Session(&gorm.Session{})
return p
}
type productSkuBelongsToProduct struct {
db *gorm.DB
field.RelationField
}
func (a productSkuBelongsToProduct) Where(conds ...field.Expr) *productSkuBelongsToProduct {
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 productSkuBelongsToProduct) WithContext(ctx context.Context) *productSkuBelongsToProduct {
a.db = a.db.WithContext(ctx)
return &a
}
func (a productSkuBelongsToProduct) Session(session *gorm.Session) *productSkuBelongsToProduct {
a.db = a.db.Session(session)
return &a
}
func (a productSkuBelongsToProduct) Model(m *models.ProductSku) *productSkuBelongsToProductTx {
return &productSkuBelongsToProductTx{a.db.Model(m).Association(a.Name())}
}
func (a productSkuBelongsToProduct) Unscoped() *productSkuBelongsToProduct {
a.db = a.db.Unscoped()
return &a
}
type productSkuBelongsToProductTx struct{ tx *gorm.Association }
func (a productSkuBelongsToProductTx) Find() (result *models.Product, err error) {
return result, a.tx.Find(&result)
}
func (a productSkuBelongsToProductTx) Append(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a productSkuBelongsToProductTx) Replace(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a productSkuBelongsToProductTx) Delete(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a productSkuBelongsToProductTx) Clear() error {
return a.tx.Clear()
}
func (a productSkuBelongsToProductTx) Count() int64 {
return a.tx.Count()
}
func (a productSkuBelongsToProductTx) Unscoped() *productSkuBelongsToProductTx {
a.tx = a.tx.Unscoped()
return &a
}
type productSkuBelongsToDiscount struct {
db *gorm.DB
field.RelationField
}
func (a productSkuBelongsToDiscount) Where(conds ...field.Expr) *productSkuBelongsToDiscount {
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 productSkuBelongsToDiscount) WithContext(ctx context.Context) *productSkuBelongsToDiscount {
a.db = a.db.WithContext(ctx)
return &a
}
func (a productSkuBelongsToDiscount) Session(session *gorm.Session) *productSkuBelongsToDiscount {
a.db = a.db.Session(session)
return &a
}
func (a productSkuBelongsToDiscount) Model(m *models.ProductSku) *productSkuBelongsToDiscountTx {
return &productSkuBelongsToDiscountTx{a.db.Model(m).Association(a.Name())}
}
func (a productSkuBelongsToDiscount) Unscoped() *productSkuBelongsToDiscount {
a.db = a.db.Unscoped()
return &a
}
type productSkuBelongsToDiscountTx struct{ tx *gorm.Association }
func (a productSkuBelongsToDiscountTx) Find() (result *models.ProductDiscount, err error) {
return result, a.tx.Find(&result)
}
func (a productSkuBelongsToDiscountTx) Append(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a productSkuBelongsToDiscountTx) Replace(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a productSkuBelongsToDiscountTx) Delete(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a productSkuBelongsToDiscountTx) Clear() error {
return a.tx.Clear()
}
func (a productSkuBelongsToDiscountTx) Count() int64 {
return a.tx.Count()
}
func (a productSkuBelongsToDiscountTx) Unscoped() *productSkuBelongsToDiscountTx {
a.tx = a.tx.Unscoped()
return &a
}
type productSkuDo struct{ gen.DO }
func (p productSkuDo) Debug() *productSkuDo {
return p.withDO(p.DO.Debug())
}
func (p productSkuDo) WithContext(ctx context.Context) *productSkuDo {
return p.withDO(p.DO.WithContext(ctx))
}
func (p productSkuDo) ReadDB() *productSkuDo {
return p.Clauses(dbresolver.Read)
}
func (p productSkuDo) WriteDB() *productSkuDo {
return p.Clauses(dbresolver.Write)
}
func (p productSkuDo) Session(config *gorm.Session) *productSkuDo {
return p.withDO(p.DO.Session(config))
}
func (p productSkuDo) Clauses(conds ...clause.Expression) *productSkuDo {
return p.withDO(p.DO.Clauses(conds...))
}
func (p productSkuDo) Returning(value interface{}, columns ...string) *productSkuDo {
return p.withDO(p.DO.Returning(value, columns...))
}
func (p productSkuDo) Not(conds ...gen.Condition) *productSkuDo {
return p.withDO(p.DO.Not(conds...))
}
func (p productSkuDo) Or(conds ...gen.Condition) *productSkuDo {
return p.withDO(p.DO.Or(conds...))
}
func (p productSkuDo) Select(conds ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Select(conds...))
}
func (p productSkuDo) Where(conds ...gen.Condition) *productSkuDo {
return p.withDO(p.DO.Where(conds...))
}
func (p productSkuDo) Order(conds ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Order(conds...))
}
func (p productSkuDo) Distinct(cols ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Distinct(cols...))
}
func (p productSkuDo) Omit(cols ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Omit(cols...))
}
func (p productSkuDo) Join(table schema.Tabler, on ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Join(table, on...))
}
func (p productSkuDo) LeftJoin(table schema.Tabler, on ...field.Expr) *productSkuDo {
return p.withDO(p.DO.LeftJoin(table, on...))
}
func (p productSkuDo) RightJoin(table schema.Tabler, on ...field.Expr) *productSkuDo {
return p.withDO(p.DO.RightJoin(table, on...))
}
func (p productSkuDo) Group(cols ...field.Expr) *productSkuDo {
return p.withDO(p.DO.Group(cols...))
}
func (p productSkuDo) Having(conds ...gen.Condition) *productSkuDo {
return p.withDO(p.DO.Having(conds...))
}
func (p productSkuDo) Limit(limit int) *productSkuDo {
return p.withDO(p.DO.Limit(limit))
}
func (p productSkuDo) Offset(offset int) *productSkuDo {
return p.withDO(p.DO.Offset(offset))
}
func (p productSkuDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *productSkuDo {
return p.withDO(p.DO.Scopes(funcs...))
}
func (p productSkuDo) Unscoped() *productSkuDo {
return p.withDO(p.DO.Unscoped())
}
func (p productSkuDo) Create(values ...*models.ProductSku) error {
if len(values) == 0 {
return nil
}
return p.DO.Create(values)
}
func (p productSkuDo) CreateInBatches(values []*models.ProductSku, batchSize int) error {
return p.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 (p productSkuDo) Save(values ...*models.ProductSku) error {
if len(values) == 0 {
return nil
}
return p.DO.Save(values)
}
func (p productSkuDo) First() (*models.ProductSku, error) {
if result, err := p.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.ProductSku), nil
}
}
func (p productSkuDo) Take() (*models.ProductSku, error) {
if result, err := p.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.ProductSku), nil
}
}
func (p productSkuDo) Last() (*models.ProductSku, error) {
if result, err := p.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.ProductSku), nil
}
}
func (p productSkuDo) Find() ([]*models.ProductSku, error) {
result, err := p.DO.Find()
return result.([]*models.ProductSku), err
}
func (p productSkuDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ProductSku, err error) {
buf := make([]*models.ProductSku, 0, batchSize)
err = p.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 (p productSkuDo) FindInBatches(result *[]*models.ProductSku, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return p.DO.FindInBatches(result, batchSize, fc)
}
func (p productSkuDo) Attrs(attrs ...field.AssignExpr) *productSkuDo {
return p.withDO(p.DO.Attrs(attrs...))
}
func (p productSkuDo) Assign(attrs ...field.AssignExpr) *productSkuDo {
return p.withDO(p.DO.Assign(attrs...))
}
func (p productSkuDo) Joins(fields ...field.RelationField) *productSkuDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Joins(_f))
}
return &p
}
func (p productSkuDo) Preload(fields ...field.RelationField) *productSkuDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Preload(_f))
}
return &p
}
func (p productSkuDo) FirstOrInit() (*models.ProductSku, error) {
if result, err := p.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.ProductSku), nil
}
}
func (p productSkuDo) FirstOrCreate() (*models.ProductSku, error) {
if result, err := p.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.ProductSku), nil
}
}
func (p productSkuDo) FindByPage(offset int, limit int) (result []*models.ProductSku, count int64, err error) {
result, err = p.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 = p.Offset(-1).Limit(-1).Count()
return
}
func (p productSkuDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = p.Count()
if err != nil {
return
}
err = p.Offset(offset).Limit(limit).Scan(result)
return
}
func (p productSkuDo) Scan(result interface{}) (err error) {
return p.DO.Scan(result)
}
func (p productSkuDo) Delete(models ...*models.ProductSku) (result gen.ResultInfo, err error) {
return p.DO.Delete(models)
}
func (p *productSkuDo) withDO(do gen.Dao) *productSkuDo {
p.DO = *do.(*gen.DO)
return p
}

View File

@@ -0,0 +1,726 @@
// 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 newProductSkuUser(db *gorm.DB, opts ...gen.DOOption) productSkuUser {
_productSkuUser := productSkuUser{}
_productSkuUser.productSkuUserDo.UseDB(db, opts...)
_productSkuUser.productSkuUserDo.UseModel(&models.ProductSkuUser{})
tableName := _productSkuUser.productSkuUserDo.TableName()
_productSkuUser.ALL = field.NewAsterisk(tableName)
_productSkuUser.ID = field.NewInt32(tableName, "id")
_productSkuUser.UserID = field.NewInt32(tableName, "user_id")
_productSkuUser.ProductSkuID = field.NewInt32(tableName, "product_sku_id")
_productSkuUser.DiscountId = field.NewInt32(tableName, "discount_id")
_productSkuUser.CreatedAt = field.NewTime(tableName, "created_at")
_productSkuUser.UpdatedAt = field.NewTime(tableName, "updated_at")
_productSkuUser.User = productSkuUserBelongsToUser{
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"),
},
},
}
_productSkuUser.ProductSku = productSkuUserBelongsToProductSku{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("ProductSku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("ProductSku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("ProductSku.Discount", "models.ProductDiscount"),
},
}
_productSkuUser.Discount = productSkuUserBelongsToDiscount{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Discount", "models.ProductDiscount"),
}
_productSkuUser.fillFieldMap()
return _productSkuUser
}
type productSkuUser struct {
productSkuUserDo
ALL field.Asterisk
ID field.Int32
UserID field.Int32
ProductSkuID field.Int32
DiscountId field.Int32
CreatedAt field.Time
UpdatedAt field.Time
User productSkuUserBelongsToUser
ProductSku productSkuUserBelongsToProductSku
Discount productSkuUserBelongsToDiscount
fieldMap map[string]field.Expr
}
func (p productSkuUser) Table(newTableName string) *productSkuUser {
p.productSkuUserDo.UseTable(newTableName)
return p.updateTableName(newTableName)
}
func (p productSkuUser) As(alias string) *productSkuUser {
p.productSkuUserDo.DO = *(p.productSkuUserDo.As(alias).(*gen.DO))
return p.updateTableName(alias)
}
func (p *productSkuUser) updateTableName(table string) *productSkuUser {
p.ALL = field.NewAsterisk(table)
p.ID = field.NewInt32(table, "id")
p.UserID = field.NewInt32(table, "user_id")
p.ProductSkuID = field.NewInt32(table, "product_sku_id")
p.DiscountId = field.NewInt32(table, "discount_id")
p.CreatedAt = field.NewTime(table, "created_at")
p.UpdatedAt = field.NewTime(table, "updated_at")
p.fillFieldMap()
return p
}
func (p *productSkuUser) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := p.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (p *productSkuUser) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 9)
p.fieldMap["id"] = p.ID
p.fieldMap["user_id"] = p.UserID
p.fieldMap["product_sku_id"] = p.ProductSkuID
p.fieldMap["discount_id"] = p.DiscountId
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
}
func (p productSkuUser) clone(db *gorm.DB) productSkuUser {
p.productSkuUserDo.ReplaceConnPool(db.Statement.ConnPool)
p.User.db = db.Session(&gorm.Session{Initialized: true})
p.User.db.Statement.ConnPool = db.Statement.ConnPool
p.ProductSku.db = db.Session(&gorm.Session{Initialized: true})
p.ProductSku.db.Statement.ConnPool = db.Statement.ConnPool
p.Discount.db = db.Session(&gorm.Session{Initialized: true})
p.Discount.db.Statement.ConnPool = db.Statement.ConnPool
return p
}
func (p productSkuUser) replaceDB(db *gorm.DB) productSkuUser {
p.productSkuUserDo.ReplaceDB(db)
p.User.db = db.Session(&gorm.Session{})
p.ProductSku.db = db.Session(&gorm.Session{})
p.Discount.db = db.Session(&gorm.Session{})
return p
}
type productSkuUserBelongsToUser 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 productSkuUserBelongsToUser) Where(conds ...field.Expr) *productSkuUserBelongsToUser {
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 productSkuUserBelongsToUser) WithContext(ctx context.Context) *productSkuUserBelongsToUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a productSkuUserBelongsToUser) Session(session *gorm.Session) *productSkuUserBelongsToUser {
a.db = a.db.Session(session)
return &a
}
func (a productSkuUserBelongsToUser) Model(m *models.ProductSkuUser) *productSkuUserBelongsToUserTx {
return &productSkuUserBelongsToUserTx{a.db.Model(m).Association(a.Name())}
}
func (a productSkuUserBelongsToUser) Unscoped() *productSkuUserBelongsToUser {
a.db = a.db.Unscoped()
return &a
}
type productSkuUserBelongsToUserTx struct{ tx *gorm.Association }
func (a productSkuUserBelongsToUserTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a productSkuUserBelongsToUserTx) 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 productSkuUserBelongsToUserTx) 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 productSkuUserBelongsToUserTx) 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 productSkuUserBelongsToUserTx) Clear() error {
return a.tx.Clear()
}
func (a productSkuUserBelongsToUserTx) Count() int64 {
return a.tx.Count()
}
func (a productSkuUserBelongsToUserTx) Unscoped() *productSkuUserBelongsToUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type productSkuUserBelongsToProductSku struct {
db *gorm.DB
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
func (a productSkuUserBelongsToProductSku) Where(conds ...field.Expr) *productSkuUserBelongsToProductSku {
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 productSkuUserBelongsToProductSku) WithContext(ctx context.Context) *productSkuUserBelongsToProductSku {
a.db = a.db.WithContext(ctx)
return &a
}
func (a productSkuUserBelongsToProductSku) Session(session *gorm.Session) *productSkuUserBelongsToProductSku {
a.db = a.db.Session(session)
return &a
}
func (a productSkuUserBelongsToProductSku) Model(m *models.ProductSkuUser) *productSkuUserBelongsToProductSkuTx {
return &productSkuUserBelongsToProductSkuTx{a.db.Model(m).Association(a.Name())}
}
func (a productSkuUserBelongsToProductSku) Unscoped() *productSkuUserBelongsToProductSku {
a.db = a.db.Unscoped()
return &a
}
type productSkuUserBelongsToProductSkuTx struct{ tx *gorm.Association }
func (a productSkuUserBelongsToProductSkuTx) Find() (result *models.ProductSku, err error) {
return result, a.tx.Find(&result)
}
func (a productSkuUserBelongsToProductSkuTx) Append(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a productSkuUserBelongsToProductSkuTx) Replace(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a productSkuUserBelongsToProductSkuTx) Delete(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a productSkuUserBelongsToProductSkuTx) Clear() error {
return a.tx.Clear()
}
func (a productSkuUserBelongsToProductSkuTx) Count() int64 {
return a.tx.Count()
}
func (a productSkuUserBelongsToProductSkuTx) Unscoped() *productSkuUserBelongsToProductSkuTx {
a.tx = a.tx.Unscoped()
return &a
}
type productSkuUserBelongsToDiscount struct {
db *gorm.DB
field.RelationField
}
func (a productSkuUserBelongsToDiscount) Where(conds ...field.Expr) *productSkuUserBelongsToDiscount {
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 productSkuUserBelongsToDiscount) WithContext(ctx context.Context) *productSkuUserBelongsToDiscount {
a.db = a.db.WithContext(ctx)
return &a
}
func (a productSkuUserBelongsToDiscount) Session(session *gorm.Session) *productSkuUserBelongsToDiscount {
a.db = a.db.Session(session)
return &a
}
func (a productSkuUserBelongsToDiscount) Model(m *models.ProductSkuUser) *productSkuUserBelongsToDiscountTx {
return &productSkuUserBelongsToDiscountTx{a.db.Model(m).Association(a.Name())}
}
func (a productSkuUserBelongsToDiscount) Unscoped() *productSkuUserBelongsToDiscount {
a.db = a.db.Unscoped()
return &a
}
type productSkuUserBelongsToDiscountTx struct{ tx *gorm.Association }
func (a productSkuUserBelongsToDiscountTx) Find() (result *models.ProductDiscount, err error) {
return result, a.tx.Find(&result)
}
func (a productSkuUserBelongsToDiscountTx) Append(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a productSkuUserBelongsToDiscountTx) Replace(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a productSkuUserBelongsToDiscountTx) Delete(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a productSkuUserBelongsToDiscountTx) Clear() error {
return a.tx.Clear()
}
func (a productSkuUserBelongsToDiscountTx) Count() int64 {
return a.tx.Count()
}
func (a productSkuUserBelongsToDiscountTx) Unscoped() *productSkuUserBelongsToDiscountTx {
a.tx = a.tx.Unscoped()
return &a
}
type productSkuUserDo struct{ gen.DO }
func (p productSkuUserDo) Debug() *productSkuUserDo {
return p.withDO(p.DO.Debug())
}
func (p productSkuUserDo) WithContext(ctx context.Context) *productSkuUserDo {
return p.withDO(p.DO.WithContext(ctx))
}
func (p productSkuUserDo) ReadDB() *productSkuUserDo {
return p.Clauses(dbresolver.Read)
}
func (p productSkuUserDo) WriteDB() *productSkuUserDo {
return p.Clauses(dbresolver.Write)
}
func (p productSkuUserDo) Session(config *gorm.Session) *productSkuUserDo {
return p.withDO(p.DO.Session(config))
}
func (p productSkuUserDo) Clauses(conds ...clause.Expression) *productSkuUserDo {
return p.withDO(p.DO.Clauses(conds...))
}
func (p productSkuUserDo) Returning(value interface{}, columns ...string) *productSkuUserDo {
return p.withDO(p.DO.Returning(value, columns...))
}
func (p productSkuUserDo) Not(conds ...gen.Condition) *productSkuUserDo {
return p.withDO(p.DO.Not(conds...))
}
func (p productSkuUserDo) Or(conds ...gen.Condition) *productSkuUserDo {
return p.withDO(p.DO.Or(conds...))
}
func (p productSkuUserDo) Select(conds ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Select(conds...))
}
func (p productSkuUserDo) Where(conds ...gen.Condition) *productSkuUserDo {
return p.withDO(p.DO.Where(conds...))
}
func (p productSkuUserDo) Order(conds ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Order(conds...))
}
func (p productSkuUserDo) Distinct(cols ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Distinct(cols...))
}
func (p productSkuUserDo) Omit(cols ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Omit(cols...))
}
func (p productSkuUserDo) Join(table schema.Tabler, on ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Join(table, on...))
}
func (p productSkuUserDo) LeftJoin(table schema.Tabler, on ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.LeftJoin(table, on...))
}
func (p productSkuUserDo) RightJoin(table schema.Tabler, on ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.RightJoin(table, on...))
}
func (p productSkuUserDo) Group(cols ...field.Expr) *productSkuUserDo {
return p.withDO(p.DO.Group(cols...))
}
func (p productSkuUserDo) Having(conds ...gen.Condition) *productSkuUserDo {
return p.withDO(p.DO.Having(conds...))
}
func (p productSkuUserDo) Limit(limit int) *productSkuUserDo {
return p.withDO(p.DO.Limit(limit))
}
func (p productSkuUserDo) Offset(offset int) *productSkuUserDo {
return p.withDO(p.DO.Offset(offset))
}
func (p productSkuUserDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *productSkuUserDo {
return p.withDO(p.DO.Scopes(funcs...))
}
func (p productSkuUserDo) Unscoped() *productSkuUserDo {
return p.withDO(p.DO.Unscoped())
}
func (p productSkuUserDo) Create(values ...*models.ProductSkuUser) error {
if len(values) == 0 {
return nil
}
return p.DO.Create(values)
}
func (p productSkuUserDo) CreateInBatches(values []*models.ProductSkuUser, batchSize int) error {
return p.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 (p productSkuUserDo) Save(values ...*models.ProductSkuUser) error {
if len(values) == 0 {
return nil
}
return p.DO.Save(values)
}
func (p productSkuUserDo) First() (*models.ProductSkuUser, error) {
if result, err := p.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.ProductSkuUser), nil
}
}
func (p productSkuUserDo) Take() (*models.ProductSkuUser, error) {
if result, err := p.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.ProductSkuUser), nil
}
}
func (p productSkuUserDo) Last() (*models.ProductSkuUser, error) {
if result, err := p.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.ProductSkuUser), nil
}
}
func (p productSkuUserDo) Find() ([]*models.ProductSkuUser, error) {
result, err := p.DO.Find()
return result.([]*models.ProductSkuUser), err
}
func (p productSkuUserDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ProductSkuUser, err error) {
buf := make([]*models.ProductSkuUser, 0, batchSize)
err = p.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 (p productSkuUserDo) FindInBatches(result *[]*models.ProductSkuUser, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return p.DO.FindInBatches(result, batchSize, fc)
}
func (p productSkuUserDo) Attrs(attrs ...field.AssignExpr) *productSkuUserDo {
return p.withDO(p.DO.Attrs(attrs...))
}
func (p productSkuUserDo) Assign(attrs ...field.AssignExpr) *productSkuUserDo {
return p.withDO(p.DO.Assign(attrs...))
}
func (p productSkuUserDo) Joins(fields ...field.RelationField) *productSkuUserDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Joins(_f))
}
return &p
}
func (p productSkuUserDo) Preload(fields ...field.RelationField) *productSkuUserDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Preload(_f))
}
return &p
}
func (p productSkuUserDo) FirstOrInit() (*models.ProductSkuUser, error) {
if result, err := p.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.ProductSkuUser), nil
}
}
func (p productSkuUserDo) FirstOrCreate() (*models.ProductSkuUser, error) {
if result, err := p.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.ProductSkuUser), nil
}
}
func (p productSkuUserDo) FindByPage(offset int, limit int) (result []*models.ProductSkuUser, count int64, err error) {
result, err = p.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 = p.Offset(-1).Limit(-1).Count()
return
}
func (p productSkuUserDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = p.Count()
if err != nil {
return
}
err = p.Offset(offset).Limit(limit).Scan(result)
return
}
func (p productSkuUserDo) Scan(result interface{}) (err error) {
return p.DO.Scan(result)
}
func (p productSkuUserDo) Delete(models ...*models.ProductSkuUser) (result gen.ResultInfo, err error) {
return p.DO.Delete(models)
}
func (p *productSkuUserDo) withDO(do gen.Dao) *productSkuUserDo {
p.DO = *do.(*gen.DO)
return p
}

View File

@@ -47,13 +47,99 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
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
}
}
}{
RelationField: field.NewRelation("Channels.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("Channels.User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Channels.User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Channels.User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Channels.User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.User.Roles.Permissions", "models.Permission"),
},
},
},
Resource: struct {
@@ -63,9 +149,24 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Channels.Resource", "models.Resource"),
@@ -76,13 +177,56 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
},
Short: struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Channels.Resource.Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Channels.Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.Resource.Short.Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.Resource.Short.Sku.Discount", "models.ProductDiscount"),
},
},
},
Long: struct {
field.RelationField
Sku struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Channels.Resource.Long", "models.ResourceLong"),
Sku: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.Resource.Long.Sku", "models.ProductSku"),
},
},
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Channels.Resource.Product", "models.Product"),
},
},
Proxy: struct {
@@ -209,6 +353,27 @@ type proxyHasManyChannels struct {
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
}
}
}
Resource struct {
@@ -218,9 +383,24 @@ type proxyHasManyChannels struct {
}
Short struct {
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
Long struct {
field.RelationField
Sku struct {
field.RelationField
}
}
Product struct {
field.RelationField
}
}
Proxy struct {

View File

@@ -35,16 +35,49 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
_resource.ResourceNo = field.NewString(tableName, "resource_no")
_resource.Active = field.NewBool(tableName, "active")
_resource.Type = field.NewInt(tableName, "type")
_resource.Code = field.NewString(tableName, "code")
_resource.Short = resourceHasOneShort{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Short", "models.ResourceShort"),
Sku: struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Short.Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Short.Sku.Discount", "models.ProductDiscount"),
},
},
}
_resource.Long = resourceHasOneLong{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Long", "models.ResourceLong"),
Sku: struct {
field.RelationField
}{
RelationField: field.NewRelation("Long.Sku", "models.ProductSku"),
},
}
_resource.Product = resourceHasOneProduct{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Product", "models.Product"),
}
_resource.User = resourceBelongsToUser{
@@ -53,8 +86,73 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
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"),
},
},
}
@@ -75,10 +173,13 @@ type resource struct {
ResourceNo field.String
Active field.Bool
Type field.Int
Code field.String
Short resourceHasOneShort
Long resourceHasOneLong
Product resourceHasOneProduct
User resourceBelongsToUser
fieldMap map[string]field.Expr
@@ -104,6 +205,7 @@ func (r *resource) updateTableName(table string) *resource {
r.ResourceNo = field.NewString(table, "resource_no")
r.Active = field.NewBool(table, "active")
r.Type = field.NewInt(table, "type")
r.Code = field.NewString(table, "code")
r.fillFieldMap()
@@ -120,7 +222,7 @@ func (r *resource) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (r *resource) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 11)
r.fieldMap = make(map[string]field.Expr, 13)
r.fieldMap["id"] = r.ID
r.fieldMap["created_at"] = r.CreatedAt
r.fieldMap["updated_at"] = r.UpdatedAt
@@ -129,6 +231,7 @@ func (r *resource) fillFieldMap() {
r.fieldMap["resource_no"] = r.ResourceNo
r.fieldMap["active"] = r.Active
r.fieldMap["type"] = r.Type
r.fieldMap["code"] = r.Code
}
@@ -138,6 +241,8 @@ func (r resource) clone(db *gorm.DB) resource {
r.Short.db.Statement.ConnPool = db.Statement.ConnPool
r.Long.db = db.Session(&gorm.Session{Initialized: true})
r.Long.db.Statement.ConnPool = db.Statement.ConnPool
r.Product.db = db.Session(&gorm.Session{Initialized: true})
r.Product.db.Statement.ConnPool = db.Statement.ConnPool
r.User.db = db.Session(&gorm.Session{Initialized: true})
r.User.db.Statement.ConnPool = db.Statement.ConnPool
return r
@@ -147,6 +252,7 @@ func (r resource) replaceDB(db *gorm.DB) resource {
r.resourceDo.ReplaceDB(db)
r.Short.db = db.Session(&gorm.Session{})
r.Long.db = db.Session(&gorm.Session{})
r.Product.db = db.Session(&gorm.Session{})
r.User.db = db.Session(&gorm.Session{})
return r
}
@@ -155,6 +261,16 @@ type resourceHasOneShort struct {
db *gorm.DB
field.RelationField
Sku struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
func (a resourceHasOneShort) Where(conds ...field.Expr) *resourceHasOneShort {
@@ -236,6 +352,10 @@ type resourceHasOneLong struct {
db *gorm.DB
field.RelationField
Sku struct {
field.RelationField
}
}
func (a resourceHasOneLong) Where(conds ...field.Expr) *resourceHasOneLong {
@@ -313,6 +433,87 @@ func (a resourceHasOneLongTx) Unscoped() *resourceHasOneLongTx {
return &a
}
type resourceHasOneProduct struct {
db *gorm.DB
field.RelationField
}
func (a resourceHasOneProduct) Where(conds ...field.Expr) *resourceHasOneProduct {
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 resourceHasOneProduct) WithContext(ctx context.Context) *resourceHasOneProduct {
a.db = a.db.WithContext(ctx)
return &a
}
func (a resourceHasOneProduct) Session(session *gorm.Session) *resourceHasOneProduct {
a.db = a.db.Session(session)
return &a
}
func (a resourceHasOneProduct) Model(m *models.Resource) *resourceHasOneProductTx {
return &resourceHasOneProductTx{a.db.Model(m).Association(a.Name())}
}
func (a resourceHasOneProduct) Unscoped() *resourceHasOneProduct {
a.db = a.db.Unscoped()
return &a
}
type resourceHasOneProductTx struct{ tx *gorm.Association }
func (a resourceHasOneProductTx) Find() (result *models.Product, err error) {
return result, a.tx.Find(&result)
}
func (a resourceHasOneProductTx) Append(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a resourceHasOneProductTx) Replace(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a resourceHasOneProductTx) Delete(values ...*models.Product) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a resourceHasOneProductTx) Clear() error {
return a.tx.Clear()
}
func (a resourceHasOneProductTx) Count() int64 {
return a.tx.Count()
}
func (a resourceHasOneProductTx) Unscoped() *resourceHasOneProductTx {
a.tx = a.tx.Unscoped()
return &a
}
type resourceBelongsToUser struct {
db *gorm.DB
@@ -320,6 +521,27 @@ type resourceBelongsToUser struct {
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
}
}
}

View File

@@ -29,6 +29,7 @@ func newResourceLong(db *gorm.DB, opts ...gen.DOOption) resourceLong {
_resourceLong.ALL = field.NewAsterisk(tableName)
_resourceLong.ID = field.NewInt32(tableName, "id")
_resourceLong.ResourceID = field.NewInt32(tableName, "resource_id")
_resourceLong.Code = field.NewString(tableName, "code")
_resourceLong.Live = field.NewInt32(tableName, "live")
_resourceLong.Type = field.NewInt(tableName, "type")
_resourceLong.Quota = field.NewInt32(tableName, "quota")
@@ -36,6 +37,21 @@ func newResourceLong(db *gorm.DB, opts ...gen.DOOption) resourceLong {
_resourceLong.Used = field.NewInt32(tableName, "used")
_resourceLong.Daily = field.NewInt32(tableName, "daily")
_resourceLong.LastAt = field.NewTime(tableName, "last_at")
_resourceLong.Sku = resourceLongHasOneSku{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Discount", "models.ProductDiscount"),
},
}
_resourceLong.fillFieldMap()
@@ -48,6 +64,7 @@ type resourceLong struct {
ALL field.Asterisk
ID field.Int32
ResourceID field.Int32
Code field.String
Live field.Int32
Type field.Int
Quota field.Int32
@@ -55,6 +72,7 @@ type resourceLong struct {
Used field.Int32
Daily field.Int32
LastAt field.Time
Sku resourceLongHasOneSku
fieldMap map[string]field.Expr
}
@@ -73,6 +91,7 @@ func (r *resourceLong) updateTableName(table string) *resourceLong {
r.ALL = field.NewAsterisk(table)
r.ID = field.NewInt32(table, "id")
r.ResourceID = field.NewInt32(table, "resource_id")
r.Code = field.NewString(table, "code")
r.Live = field.NewInt32(table, "live")
r.Type = field.NewInt(table, "type")
r.Quota = field.NewInt32(table, "quota")
@@ -96,9 +115,10 @@ func (r *resourceLong) GetFieldByName(fieldName string) (field.OrderExpr, bool)
}
func (r *resourceLong) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 9)
r.fieldMap = make(map[string]field.Expr, 11)
r.fieldMap["id"] = r.ID
r.fieldMap["resource_id"] = r.ResourceID
r.fieldMap["code"] = r.Code
r.fieldMap["live"] = r.Live
r.fieldMap["type"] = r.Type
r.fieldMap["quota"] = r.Quota
@@ -106,18 +126,110 @@ func (r *resourceLong) fillFieldMap() {
r.fieldMap["used"] = r.Used
r.fieldMap["daily"] = r.Daily
r.fieldMap["last_at"] = r.LastAt
}
func (r resourceLong) clone(db *gorm.DB) resourceLong {
r.resourceLongDo.ReplaceConnPool(db.Statement.ConnPool)
r.Sku.db = db.Session(&gorm.Session{Initialized: true})
r.Sku.db.Statement.ConnPool = db.Statement.ConnPool
return r
}
func (r resourceLong) replaceDB(db *gorm.DB) resourceLong {
r.resourceLongDo.ReplaceDB(db)
r.Sku.db = db.Session(&gorm.Session{})
return r
}
type resourceLongHasOneSku struct {
db *gorm.DB
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
func (a resourceLongHasOneSku) Where(conds ...field.Expr) *resourceLongHasOneSku {
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 resourceLongHasOneSku) WithContext(ctx context.Context) *resourceLongHasOneSku {
a.db = a.db.WithContext(ctx)
return &a
}
func (a resourceLongHasOneSku) Session(session *gorm.Session) *resourceLongHasOneSku {
a.db = a.db.Session(session)
return &a
}
func (a resourceLongHasOneSku) Model(m *models.ResourceLong) *resourceLongHasOneSkuTx {
return &resourceLongHasOneSkuTx{a.db.Model(m).Association(a.Name())}
}
func (a resourceLongHasOneSku) Unscoped() *resourceLongHasOneSku {
a.db = a.db.Unscoped()
return &a
}
type resourceLongHasOneSkuTx struct{ tx *gorm.Association }
func (a resourceLongHasOneSkuTx) Find() (result *models.ProductSku, err error) {
return result, a.tx.Find(&result)
}
func (a resourceLongHasOneSkuTx) Append(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a resourceLongHasOneSkuTx) Replace(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a resourceLongHasOneSkuTx) Delete(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a resourceLongHasOneSkuTx) Clear() error {
return a.tx.Clear()
}
func (a resourceLongHasOneSkuTx) Count() int64 {
return a.tx.Count()
}
func (a resourceLongHasOneSkuTx) Unscoped() *resourceLongHasOneSkuTx {
a.tx = a.tx.Unscoped()
return &a
}
type resourceLongDo struct{ gen.DO }
func (r resourceLongDo) Debug() *resourceLongDo {

View File

@@ -29,6 +29,7 @@ func newResourceShort(db *gorm.DB, opts ...gen.DOOption) resourceShort {
_resourceShort.ALL = field.NewAsterisk(tableName)
_resourceShort.ID = field.NewInt32(tableName, "id")
_resourceShort.ResourceID = field.NewInt32(tableName, "resource_id")
_resourceShort.Code = field.NewString(tableName, "code")
_resourceShort.Live = field.NewInt32(tableName, "live")
_resourceShort.Type = field.NewInt(tableName, "type")
_resourceShort.Quota = field.NewInt32(tableName, "quota")
@@ -36,6 +37,21 @@ func newResourceShort(db *gorm.DB, opts ...gen.DOOption) resourceShort {
_resourceShort.Used = field.NewInt32(tableName, "used")
_resourceShort.Daily = field.NewInt32(tableName, "daily")
_resourceShort.LastAt = field.NewTime(tableName, "last_at")
_resourceShort.Sku = resourceShortHasOneSku{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Sku", "models.ProductSku"),
Product: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Product", "models.Product"),
},
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Discount", "models.ProductDiscount"),
},
}
_resourceShort.fillFieldMap()
@@ -48,6 +64,7 @@ type resourceShort struct {
ALL field.Asterisk
ID field.Int32
ResourceID field.Int32
Code field.String
Live field.Int32
Type field.Int
Quota field.Int32
@@ -55,6 +72,7 @@ type resourceShort struct {
Used field.Int32
Daily field.Int32
LastAt field.Time
Sku resourceShortHasOneSku
fieldMap map[string]field.Expr
}
@@ -73,6 +91,7 @@ func (r *resourceShort) updateTableName(table string) *resourceShort {
r.ALL = field.NewAsterisk(table)
r.ID = field.NewInt32(table, "id")
r.ResourceID = field.NewInt32(table, "resource_id")
r.Code = field.NewString(table, "code")
r.Live = field.NewInt32(table, "live")
r.Type = field.NewInt(table, "type")
r.Quota = field.NewInt32(table, "quota")
@@ -96,9 +115,10 @@ func (r *resourceShort) GetFieldByName(fieldName string) (field.OrderExpr, bool)
}
func (r *resourceShort) fillFieldMap() {
r.fieldMap = make(map[string]field.Expr, 9)
r.fieldMap = make(map[string]field.Expr, 11)
r.fieldMap["id"] = r.ID
r.fieldMap["resource_id"] = r.ResourceID
r.fieldMap["code"] = r.Code
r.fieldMap["live"] = r.Live
r.fieldMap["type"] = r.Type
r.fieldMap["quota"] = r.Quota
@@ -106,18 +126,110 @@ func (r *resourceShort) fillFieldMap() {
r.fieldMap["used"] = r.Used
r.fieldMap["daily"] = r.Daily
r.fieldMap["last_at"] = r.LastAt
}
func (r resourceShort) clone(db *gorm.DB) resourceShort {
r.resourceShortDo.ReplaceConnPool(db.Statement.ConnPool)
r.Sku.db = db.Session(&gorm.Session{Initialized: true})
r.Sku.db.Statement.ConnPool = db.Statement.ConnPool
return r
}
func (r resourceShort) replaceDB(db *gorm.DB) resourceShort {
r.resourceShortDo.ReplaceDB(db)
r.Sku.db = db.Session(&gorm.Session{})
return r
}
type resourceShortHasOneSku struct {
db *gorm.DB
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
func (a resourceShortHasOneSku) Where(conds ...field.Expr) *resourceShortHasOneSku {
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 resourceShortHasOneSku) WithContext(ctx context.Context) *resourceShortHasOneSku {
a.db = a.db.WithContext(ctx)
return &a
}
func (a resourceShortHasOneSku) Session(session *gorm.Session) *resourceShortHasOneSku {
a.db = a.db.Session(session)
return &a
}
func (a resourceShortHasOneSku) Model(m *models.ResourceShort) *resourceShortHasOneSkuTx {
return &resourceShortHasOneSkuTx{a.db.Model(m).Association(a.Name())}
}
func (a resourceShortHasOneSku) Unscoped() *resourceShortHasOneSku {
a.db = a.db.Unscoped()
return &a
}
type resourceShortHasOneSkuTx struct{ tx *gorm.Association }
func (a resourceShortHasOneSkuTx) Find() (result *models.ProductSku, err error) {
return result, a.tx.Find(&result)
}
func (a resourceShortHasOneSkuTx) Append(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a resourceShortHasOneSkuTx) Replace(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a resourceShortHasOneSkuTx) Delete(values ...*models.ProductSku) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a resourceShortHasOneSkuTx) Clear() error {
return a.tx.Clear()
}
func (a resourceShortHasOneSkuTx) Count() int64 {
return a.tx.Count()
}
func (a resourceShortHasOneSkuTx) Unscoped() *resourceShortHasOneSkuTx {
a.tx = a.tx.Unscoped()
return &a
}
type resourceShortDo struct{ gen.DO }
func (r resourceShortDo) Debug() *resourceShortDo {

View File

@@ -47,8 +47,73 @@ func newSession(db *gorm.DB, opts ...gen.DOOption) 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"),
},
},
}
@@ -62,6 +127,11 @@ func newSession(db *gorm.DB, opts ...gen.DOOption) session {
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Client", "models.Client"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("Client.Permissions", "models.Permission"),
},
}
_session.fillFieldMap()
@@ -182,6 +252,27 @@ type sessionBelongsToUser struct {
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
}
}
}
@@ -345,6 +436,10 @@ type sessionBelongsToClient struct {
db *gorm.DB
field.RelationField
Permissions struct {
field.RelationField
}
}
func (a sessionBelongsToClient) Where(conds ...field.Expr) *sessionBelongsToClient {

View File

@@ -47,6 +47,81 @@ func newTrade(db *gorm.DB, opts ...gen.DOOption) trade {
_trade.PaymentURL = field.NewString(tableName, "payment_url")
_trade.CompletedAt = field.NewTime(tableName, "completed_at")
_trade.CanceledAt = field.NewTime(tableName, "canceled_at")
_trade.User = tradeBelongsToUser{
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"),
},
},
}
_trade.fillFieldMap()
@@ -77,6 +152,7 @@ type trade struct {
PaymentURL field.String
CompletedAt field.Time
CanceledAt field.Time
User tradeBelongsToUser
fieldMap map[string]field.Expr
}
@@ -129,7 +205,7 @@ func (t *trade) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (t *trade) fillFieldMap() {
t.fieldMap = make(map[string]field.Expr, 20)
t.fieldMap = make(map[string]field.Expr, 21)
t.fieldMap["id"] = t.ID
t.fieldMap["created_at"] = t.CreatedAt
t.fieldMap["updated_at"] = t.UpdatedAt
@@ -150,18 +226,128 @@ func (t *trade) fillFieldMap() {
t.fieldMap["payment_url"] = t.PaymentURL
t.fieldMap["completed_at"] = t.CompletedAt
t.fieldMap["canceled_at"] = t.CanceledAt
}
func (t trade) clone(db *gorm.DB) trade {
t.tradeDo.ReplaceConnPool(db.Statement.ConnPool)
t.User.db = db.Session(&gorm.Session{Initialized: true})
t.User.db.Statement.ConnPool = db.Statement.ConnPool
return t
}
func (t trade) replaceDB(db *gorm.DB) trade {
t.tradeDo.ReplaceDB(db)
t.User.db = db.Session(&gorm.Session{})
return t
}
type tradeBelongsToUser 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 tradeBelongsToUser) Where(conds ...field.Expr) *tradeBelongsToUser {
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 tradeBelongsToUser) WithContext(ctx context.Context) *tradeBelongsToUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a tradeBelongsToUser) Session(session *gorm.Session) *tradeBelongsToUser {
a.db = a.db.Session(session)
return &a
}
func (a tradeBelongsToUser) Model(m *models.Trade) *tradeBelongsToUserTx {
return &tradeBelongsToUserTx{a.db.Model(m).Association(a.Name())}
}
func (a tradeBelongsToUser) Unscoped() *tradeBelongsToUser {
a.db = a.db.Unscoped()
return &a
}
type tradeBelongsToUserTx struct{ tx *gorm.Association }
func (a tradeBelongsToUserTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a tradeBelongsToUserTx) 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 tradeBelongsToUserTx) 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 tradeBelongsToUserTx) 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 tradeBelongsToUserTx) Clear() error {
return a.tx.Clear()
}
func (a tradeBelongsToUserTx) Count() int64 {
return a.tx.Count()
}
func (a tradeBelongsToUserTx) Unscoped() *tradeBelongsToUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type tradeDo struct{ gen.DO }
func (t tradeDo) Debug() *tradeDo {

View File

@@ -32,10 +32,12 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
_user.UpdatedAt = field.NewTime(tableName, "updated_at")
_user.DeletedAt = field.NewField(tableName, "deleted_at")
_user.AdminID = field.NewInt32(tableName, "admin_id")
_user.DiscountID = field.NewInt32(tableName, "discount_id")
_user.Phone = field.NewString(tableName, "phone")
_user.Username = field.NewString(tableName, "username")
_user.Email = field.NewString(tableName, "email")
_user.Password = field.NewString(tableName, "password")
_user.Source = field.NewInt(tableName, "source")
_user.Name = field.NewString(tableName, "name")
_user.Avatar = field.NewString(tableName, "avatar")
_user.Status = field.NewInt(tableName, "status")
@@ -52,6 +54,58 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
}
_user.Discount = userBelongsToDiscount{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Discount", "models.ProductDiscount"),
}
_user.Roles = userManyToManyRoles{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("Roles.Permissions", "models.Permission"),
},
}
_user.fillFieldMap()
@@ -68,10 +122,12 @@ type user struct {
UpdatedAt field.Time
DeletedAt field.Field
AdminID field.Int32
DiscountID field.Int32
Phone field.String
Username field.String
Email field.String
Password field.String
Source field.Int
Name field.String
Avatar field.String
Status field.Int
@@ -86,6 +142,10 @@ type user struct {
LastLoginUA field.String
Admin userBelongsToAdmin
Discount userBelongsToDiscount
Roles userManyToManyRoles
fieldMap map[string]field.Expr
}
@@ -106,10 +166,12 @@ func (u *user) updateTableName(table string) *user {
u.UpdatedAt = field.NewTime(table, "updated_at")
u.DeletedAt = field.NewField(table, "deleted_at")
u.AdminID = field.NewInt32(table, "admin_id")
u.DiscountID = field.NewInt32(table, "discount_id")
u.Phone = field.NewString(table, "phone")
u.Username = field.NewString(table, "username")
u.Email = field.NewString(table, "email")
u.Password = field.NewString(table, "password")
u.Source = field.NewInt(table, "source")
u.Name = field.NewString(table, "name")
u.Avatar = field.NewString(table, "avatar")
u.Status = field.NewInt(table, "status")
@@ -138,16 +200,18 @@ func (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (u *user) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 22)
u.fieldMap = make(map[string]field.Expr, 26)
u.fieldMap["id"] = u.ID
u.fieldMap["created_at"] = u.CreatedAt
u.fieldMap["updated_at"] = u.UpdatedAt
u.fieldMap["deleted_at"] = u.DeletedAt
u.fieldMap["admin_id"] = u.AdminID
u.fieldMap["discount_id"] = u.DiscountID
u.fieldMap["phone"] = u.Phone
u.fieldMap["username"] = u.Username
u.fieldMap["email"] = u.Email
u.fieldMap["password"] = u.Password
u.fieldMap["source"] = u.Source
u.fieldMap["name"] = u.Name
u.fieldMap["avatar"] = u.Avatar
u.fieldMap["status"] = u.Status
@@ -167,12 +231,18 @@ func (u user) clone(db *gorm.DB) user {
u.userDo.ReplaceConnPool(db.Statement.ConnPool)
u.Admin.db = db.Session(&gorm.Session{Initialized: true})
u.Admin.db.Statement.ConnPool = db.Statement.ConnPool
u.Discount.db = db.Session(&gorm.Session{Initialized: true})
u.Discount.db.Statement.ConnPool = db.Statement.ConnPool
u.Roles.db = db.Session(&gorm.Session{Initialized: true})
u.Roles.db.Statement.ConnPool = db.Statement.ConnPool
return u
}
func (u user) replaceDB(db *gorm.DB) user {
u.userDo.ReplaceDB(db)
u.Admin.db = db.Session(&gorm.Session{})
u.Discount.db = db.Session(&gorm.Session{})
u.Roles.db = db.Session(&gorm.Session{})
return u
}
@@ -180,6 +250,19 @@ type userBelongsToAdmin struct {
db *gorm.DB
field.RelationField
Roles struct {
field.RelationField
Permissions struct {
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
}
}
func (a userBelongsToAdmin) Where(conds ...field.Expr) *userBelongsToAdmin {
@@ -257,6 +340,172 @@ func (a userBelongsToAdminTx) Unscoped() *userBelongsToAdminTx {
return &a
}
type userBelongsToDiscount struct {
db *gorm.DB
field.RelationField
}
func (a userBelongsToDiscount) Where(conds ...field.Expr) *userBelongsToDiscount {
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 userBelongsToDiscount) WithContext(ctx context.Context) *userBelongsToDiscount {
a.db = a.db.WithContext(ctx)
return &a
}
func (a userBelongsToDiscount) Session(session *gorm.Session) *userBelongsToDiscount {
a.db = a.db.Session(session)
return &a
}
func (a userBelongsToDiscount) Model(m *models.User) *userBelongsToDiscountTx {
return &userBelongsToDiscountTx{a.db.Model(m).Association(a.Name())}
}
func (a userBelongsToDiscount) Unscoped() *userBelongsToDiscount {
a.db = a.db.Unscoped()
return &a
}
type userBelongsToDiscountTx struct{ tx *gorm.Association }
func (a userBelongsToDiscountTx) Find() (result *models.ProductDiscount, err error) {
return result, a.tx.Find(&result)
}
func (a userBelongsToDiscountTx) Append(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a userBelongsToDiscountTx) Replace(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a userBelongsToDiscountTx) Delete(values ...*models.ProductDiscount) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a userBelongsToDiscountTx) Clear() error {
return a.tx.Clear()
}
func (a userBelongsToDiscountTx) Count() int64 {
return a.tx.Count()
}
func (a userBelongsToDiscountTx) Unscoped() *userBelongsToDiscountTx {
a.tx = a.tx.Unscoped()
return &a
}
type userManyToManyRoles struct {
db *gorm.DB
field.RelationField
Permissions struct {
field.RelationField
}
}
func (a userManyToManyRoles) Where(conds ...field.Expr) *userManyToManyRoles {
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 userManyToManyRoles) WithContext(ctx context.Context) *userManyToManyRoles {
a.db = a.db.WithContext(ctx)
return &a
}
func (a userManyToManyRoles) Session(session *gorm.Session) *userManyToManyRoles {
a.db = a.db.Session(session)
return &a
}
func (a userManyToManyRoles) Model(m *models.User) *userManyToManyRolesTx {
return &userManyToManyRolesTx{a.db.Model(m).Association(a.Name())}
}
func (a userManyToManyRoles) Unscoped() *userManyToManyRoles {
a.db = a.db.Unscoped()
return &a
}
type userManyToManyRolesTx struct{ tx *gorm.Association }
func (a userManyToManyRolesTx) Find() (result []*models.UserRole, err error) {
return result, a.tx.Find(&result)
}
func (a userManyToManyRolesTx) Append(values ...*models.UserRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a userManyToManyRolesTx) Replace(values ...*models.UserRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a userManyToManyRolesTx) Delete(values ...*models.UserRole) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a userManyToManyRolesTx) Clear() error {
return a.tx.Clear()
}
func (a userManyToManyRolesTx) Count() int64 {
return a.tx.Count()
}
func (a userManyToManyRolesTx) Unscoped() *userManyToManyRolesTx {
a.tx = a.tx.Unscoped()
return &a
}
type userDo struct{ gen.DO }
func (u userDo) Debug() *userDo {

View File

@@ -35,6 +35,21 @@ func newUserRole(db *gorm.DB, opts ...gen.DOOption) userRole {
_userRole.Description = field.NewString(tableName, "description")
_userRole.Active = field.NewBool(tableName, "active")
_userRole.Sort = field.NewInt32(tableName, "sort")
_userRole.Permissions = userRoleManyToManyPermissions{
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"),
},
}
_userRole.fillFieldMap()
@@ -53,6 +68,7 @@ type userRole struct {
Description field.String
Active field.Bool
Sort field.Int32
Permissions userRoleManyToManyPermissions
fieldMap map[string]field.Expr
}
@@ -93,7 +109,7 @@ func (u *userRole) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (u *userRole) fillFieldMap() {
u.fieldMap = make(map[string]field.Expr, 8)
u.fieldMap = make(map[string]field.Expr, 9)
u.fieldMap["id"] = u.ID
u.fieldMap["created_at"] = u.CreatedAt
u.fieldMap["updated_at"] = u.UpdatedAt
@@ -102,18 +118,110 @@ func (u *userRole) fillFieldMap() {
u.fieldMap["description"] = u.Description
u.fieldMap["active"] = u.Active
u.fieldMap["sort"] = u.Sort
}
func (u userRole) clone(db *gorm.DB) userRole {
u.userRoleDo.ReplaceConnPool(db.Statement.ConnPool)
u.Permissions.db = db.Session(&gorm.Session{Initialized: true})
u.Permissions.db.Statement.ConnPool = db.Statement.ConnPool
return u
}
func (u userRole) replaceDB(db *gorm.DB) userRole {
u.userRoleDo.ReplaceDB(db)
u.Permissions.db = db.Session(&gorm.Session{})
return u
}
type userRoleManyToManyPermissions struct {
db *gorm.DB
field.RelationField
Parent struct {
field.RelationField
}
Children struct {
field.RelationField
}
}
func (a userRoleManyToManyPermissions) Where(conds ...field.Expr) *userRoleManyToManyPermissions {
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 userRoleManyToManyPermissions) WithContext(ctx context.Context) *userRoleManyToManyPermissions {
a.db = a.db.WithContext(ctx)
return &a
}
func (a userRoleManyToManyPermissions) Session(session *gorm.Session) *userRoleManyToManyPermissions {
a.db = a.db.Session(session)
return &a
}
func (a userRoleManyToManyPermissions) Model(m *models.UserRole) *userRoleManyToManyPermissionsTx {
return &userRoleManyToManyPermissionsTx{a.db.Model(m).Association(a.Name())}
}
func (a userRoleManyToManyPermissions) Unscoped() *userRoleManyToManyPermissions {
a.db = a.db.Unscoped()
return &a
}
type userRoleManyToManyPermissionsTx struct{ tx *gorm.Association }
func (a userRoleManyToManyPermissionsTx) Find() (result []*models.Permission, err error) {
return result, a.tx.Find(&result)
}
func (a userRoleManyToManyPermissionsTx) 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 userRoleManyToManyPermissionsTx) 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 userRoleManyToManyPermissionsTx) 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 userRoleManyToManyPermissionsTx) Clear() error {
return a.tx.Clear()
}
func (a userRoleManyToManyPermissionsTx) Count() int64 {
return a.tx.Count()
}
func (a userRoleManyToManyPermissionsTx) Unscoped() *userRoleManyToManyPermissionsTx {
a.tx = a.tx.Unscoped()
return &a
}
type userRoleDo struct{ gen.DO }
func (u userRoleDo) Debug() *userRoleDo {

View File

@@ -4,19 +4,48 @@ import (
"platform/pkg/env"
auth2 "platform/web/auth"
"platform/web/handlers"
"time"
q "platform/web/queries"
"github.com/gofiber/fiber/v2"
)
func ApplyRouters(app *fiber.App) {
api := app.Group("/api")
userRouter(api)
adminRouter(api)
clientRouter(api)
// 回调
callbacks := app.Group("/callback")
callbacks.Get("/identify", handlers.IdentifyCallbackNew)
// 临时
if env.RunMode == env.RunModeDev {
debug := app.Group("/debug")
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
debug.Get("/proxy/register", handlers.DebugRegisterProxyBaiYin)
debug.Get("/iden/clear/:phone", handlers.DebugIdentifyClear)
debug.Get("/session/now", func(ctx *fiber.Ctx) error {
rs, err := q.Session.Where(q.Session.AccessTokenExpires.Gt(time.Now())).Find()
if err != nil {
return err
}
return ctx.JSON(rs)
})
}
}
// 用户接口路由
func userRouter(api fiber.Router) {
// 认证
auth := api.Group("/auth")
auth.Get("/authorize", auth2.AuthorizeGet)
auth.Post("/authorize", auth2.AuthorizePost)
auth.Post("/token", auth2.Token)
auth.Post("/revoke", handlers.Revoke)
auth.Post("/introspect", handlers.Introspect)
auth.Post("/verify/sms", handlers.SmsCode)
auth.Post("/revoke", auth2.Revoke)
auth.Post("/introspect", auth2.Introspect)
// 用户
user := api.Group("/user")
@@ -35,22 +64,21 @@ func ApplyRouters(app *fiber.App) {
// 套餐
resource := api.Group("/resource")
resource.Post("/all", handlers.AllActiveResource)
resource.Post("/list/short", handlers.ListResourceShort)
resource.Post("/list/long", handlers.ListResourceLong)
resource.Post("/list/short", handlers.PageResourceShort)
resource.Post("/list/long", handlers.PageResourceLong)
resource.Post("/create", handlers.CreateResource)
resource.Post("/price", handlers.ResourcePrice)
resource.Post("/statistics/free", handlers.StatisticResourceFree)
resource.Post("/statistics/usage", handlers.StatisticResourceUsage)
// 批次
batch := api.Group("/batch")
batch.Post("/page", handlers.PageResourceBatch)
batch.Post("/page", handlers.PageBatch)
// 通道
channel := api.Group("/channel")
channel.Post("/list", handlers.ListChannels)
channel.Post("/list", handlers.ListChannel)
channel.Post("/create", handlers.CreateChannel)
channel.Post("/remove", handlers.RemoveChannels)
// 交易
trade := api.Group("/trade")
@@ -72,7 +100,6 @@ func ApplyRouters(app *fiber.App) {
proxy.Post("/online", handlers.ProxyReportOnline)
proxy.Post("/offline", handlers.ProxyReportOffline)
proxy.Post("/update", handlers.ProxyReportUpdate)
proxy.Post("/register", handlers.ProxyRegisterBaiYin)
// 节点
edge := api.Group("/edge")
@@ -82,15 +109,114 @@ func ApplyRouters(app *fiber.App) {
// 前台
inquiry := api.Group("/inquiry")
inquiry.Post("/create", handlers.CreateInquiry)
// 回调
callbacks := app.Group("/callback")
callbacks.Get("/identify", handlers.IdentifyCallbackNew)
// 临时
if env.RunMode == env.RunModeDev {
debug := app.Group("/debug")
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
debug.Get("/proxy/register", handlers.DebugRegisterProxyBaiYin)
}
}
// 客户端接口路由
func clientRouter(api fiber.Router) {
client := api
// 验证短信令牌
client.Post("/verify/sms", handlers.SmsCode)
// 套餐定价查询
resource := client.Group("/resource")
resource.Post("/price", handlers.ResourcePrice)
// 通道管理
channel := client.Group("/channel")
channel.Post("/remove", handlers.RemoveChannels)
// 代理网关注册
proxy := client.Group("/proxy")
proxy.Post("/register/baidyin", handlers.ProxyRegisterBaiYin)
}
// 管理员接口路由
func adminRouter(api fiber.Router) {
api = api.Group("/admin")
// admin 管理员
var admin = api.Group("/admin")
admin.Post("/all", handlers.AllAdminByAdmin)
admin.Post("/page", handlers.PageAdminByAdmin)
admin.Post("/create", handlers.CreateAdmin)
admin.Post("/update", handlers.UpdateAdmin)
admin.Post("/remove", handlers.RemoveAdmin)
// admin-role 管理员角色
var adminRole = api.Group("/admin-role")
adminRole.Post("/list", handlers.AllAdminRoleByAdmin)
adminRole.Post("/page", handlers.PageAdminRoleByAdmin)
adminRole.Post("/create", handlers.CreateAdminRole)
adminRole.Post("/update", handlers.UpdateAdminRole)
adminRole.Post("/remove", handlers.RemoveAdminRole)
// permission 权限
var permission = api.Group("/permission")
permission.Post("/list", handlers.AllPermissionByAdmin)
permission.Post("/page", handlers.PagePermissionByAdmin)
// user 用户
var user = api.Group("/user")
user.Post("/page", handlers.PageUserByAdmin)
user.Post("/get", handlers.GetUserByAdmin)
user.Post("/create", handlers.CreateUserByAdmin)
user.Post("/update", handlers.UpdateUserByAdmin)
user.Post("/remove", handlers.RemoveUserByAdmin)
user.Post("/bind", handlers.BindAdmin)
user.Post("/update/balance", handlers.UpdateUserBalanceByAdmin)
// resource 套餐
var resource = api.Group("/resource")
resource.Post("/short/page", handlers.PageResourceShortByAdmin)
resource.Post("/long/page", handlers.PageResourceLongByAdmin)
resource.Post("/update", handlers.UpdateResourceByAdmin)
// batch 批次
var batch = api.Group("/batch")
batch.Post("/page", handlers.PageBatchByAdmin)
// channel 通道
var channel = api.Group("/channel")
channel.Post("/page", handlers.PageChannelByAdmin)
// trade 交易
var trade = api.Group("/trade")
trade.Post("/page", handlers.PageTradeByAdmin)
// bill 账单
var bill = api.Group("/bill")
bill.Post("/page", handlers.PageBillByAdmin)
// product 产品
var product = api.Group("/product")
product.Post("/all", handlers.AllProductByAdmin)
product.Post("/create", handlers.CreateProduct)
product.Post("/update", handlers.UpdateProduct)
product.Post("/remove", handlers.DeleteProduct)
product.Post("/sku/all", handlers.AllProductSkuByAdmin)
product.Post("/sku/page", handlers.PageProductSkuByAdmin)
product.Post("/sku/create", handlers.CreateProductSku)
product.Post("/sku/update", handlers.UpdateProductSku)
product.Post("/sku/remove", handlers.DeleteProductSku)
product.Post("/sku/update/discount/batch", handlers.BatchUpdateProductSkuDiscount)
// discount 折扣
var discount = api.Group("/discount")
discount.Post("/all", handlers.AllDiscountByAdmin)
discount.Post("/page", handlers.PageDiscountByAdmin)
discount.Post("/create", handlers.CreateDiscount)
discount.Post("/update", handlers.UpdateDiscount)
discount.Post("/remove", handlers.DeleteDiscount)
// coupon 优惠券
var coupon = api.Group("/coupon")
coupon.Post("/all", handlers.AllCouponByAdmin)
coupon.Post("/page", handlers.PageCouponByAdmin)
coupon.Post("/create", handlers.CreateCoupon)
coupon.Post("/update", handlers.UpdateCoupon)
coupon.Post("/remove", handlers.DeleteCoupon)
}

159
web/services/admin.go Normal file
View File

@@ -0,0 +1,159 @@
package services
import (
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"golang.org/x/crypto/bcrypt"
"gorm.io/gen/field"
)
var Admin = &adminService{}
type adminService struct{}
func (s *adminService) PageAdmins(req core.PageReq) (result []*m.Admin, count int64, err error) {
return q.Admin.
Preload(q.Admin.Roles).
Omit(q.Admin.Password).
Order(q.Admin.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *adminService) All() (result []*m.Admin, err error) {
return q.Admin.
Omit(q.Admin.Password).
Order(q.Admin.CreatedAt.Desc()).
Find()
}
type CreateAdmin struct {
Username string `json:"username" validate:"required,min=3,max=50"`
Password string `json:"password" validate:"required,min=6,max=50"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Status *m.AdminStatus `json:"status"`
Roles []int32 `json:"roles"`
}
func (s *adminService) CreateAdmin(create *CreateAdmin) error {
// 哈希密码
hash, err := bcrypt.GenerateFromPassword([]byte(create.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
return q.Q.Transaction(func(tx *q.Query) error {
// 创建管理员
admin := &m.Admin{
Username: create.Username,
Password: string(hash),
Name: create.Name,
Avatar: create.Avatar,
Phone: create.Phone,
Email: create.Email,
Status: u.Else(create.Status, m.AdminStatusEnabled),
}
if err := tx.Admin.Create(admin); err != nil {
return err
}
// 关联角色
if len(create.Roles) > 0 {
links := make([]*m.LinkAdminRole, len(create.Roles))
for i, roleID := range create.Roles {
links[i] = &m.LinkAdminRole{
AdminID: admin.ID,
RoleID: roleID,
}
}
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
return err
}
}
return nil
})
}
type UpdateAdmin struct {
Id int32 `json:"id" validate:"required"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Status *m.AdminStatus `json:"status"`
Roles *[]int32 `json:"roles"`
}
func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
simples := make([]field.AssignExpr, 0)
if update.Password != nil {
hash, err := bcrypt.GenerateFromPassword([]byte(*update.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
simples = append(simples, q.Admin.Password.Value(string(hash)))
}
if update.Name != nil {
simples = append(simples, q.Admin.Name.Value(*update.Name))
}
if update.Avatar != nil {
simples = append(simples, q.Admin.Avatar.Value(*update.Avatar))
}
if update.Phone != nil {
simples = append(simples, q.Admin.Phone.Value(*update.Phone))
}
if update.Email != nil {
simples = append(simples, q.Admin.Email.Value(*update.Email))
}
if update.Status != nil {
simples = append(simples, q.Admin.Status.Value(int(*update.Status)))
}
return q.Q.Transaction(func(tx *q.Query) error {
// 更新管理员基本信息
if len(simples) > 0 {
_, err := tx.Admin.
Where(tx.Admin.ID.Eq(update.Id), tx.Admin.Username.Neq("admin")).
UpdateSimple(simples...)
if err != nil {
return err
}
}
// 更新角色关联
if update.Roles != nil {
roles := *update.Roles
if _, err := tx.LinkAdminRole.Where(tx.LinkAdminRole.AdminID.Eq(update.Id)).Delete(); err != nil {
return err
}
if len(roles) > 0 {
links := make([]*m.LinkAdminRole, len(roles))
for i, roleID := range roles {
links[i] = &m.LinkAdminRole{
AdminID: update.Id,
RoleID: roleID,
}
}
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
return err
}
}
}
return nil
})
}
func (s *adminService) RemoveAdmin(id int32) error {
_, err := q.Admin.Where(q.Admin.ID.Eq(id), q.Admin.Username.Neq("admin")).UpdateColumn(q.Admin.DeletedAt, time.Now())
return err
}

144
web/services/admin_role.go Normal file
View File

@@ -0,0 +1,144 @@
package services
import (
"platform/pkg/u"
"platform/web/core"
g "platform/web/globals"
"platform/web/models"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gen/field"
)
var AdminRole = &adminRoleService{}
type adminRoleService struct{}
func (r *adminRoleService) ListRoles() (result []*m.AdminRole, err error) {
return q.AdminRole.
Order(q.AdminRole.Sort.Asc(), q.AdminRole.CreatedAt.Desc()).
Find()
}
func (r *adminRoleService) PageRoles(req core.PageReq) (result []*m.AdminRole, count int64, err error) {
return q.AdminRole.
Preload(q.AdminRole.Permissions).
Order(q.AdminRole.Sort.Asc(), q.AdminRole.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
}
func (r *adminRoleService) CreateAdminRole(create *CreateAdminRole) error {
return q.Q.Transaction(func(q *q.Query) error {
// 创建角色
role := &m.AdminRole{
Name: create.Name,
Description: create.Description,
Active: u.Else(create.Active, true),
Sort: u.Else(create.Sort, 0),
}
if err := q.AdminRole.Create(role); err != nil {
return err
}
// 替换权限
permissions := make([]*models.LinkAdminRolePermission, 0, len(create.Permissions))
for _, permissionID := range create.Permissions {
permissions = append(permissions, &models.LinkAdminRolePermission{
RoleID: role.ID,
PermissionID: permissionID,
})
}
if len(permissions) > 0 {
err := g.Redsync.WithLock(AdminRoleModifyLock, func() error {
return q.LinkAdminRolePermission.CreateInBatches(permissions, 1000)
})
if err != nil {
return err
}
}
return nil
})
}
type CreateAdminRole struct {
Name string `json:"name"`
Description *string `json:"description"`
Active *bool `json:"active"`
Sort *int32 `json:"sort"`
Permissions []int32 `json:"permissions"`
}
func (r *adminRoleService) UpdateAdminRole(update *UpdateAdminRole) error {
var simples = make([]field.AssignExpr, 0)
if update.Name != nil {
simples = append(simples, q.AdminRole.Name.Value(*update.Name))
}
if update.Description != nil {
simples = append(simples, q.AdminRole.Description.Value(*update.Description))
}
if update.Active != nil {
simples = append(simples, q.AdminRole.Active.Value(*update.Active))
}
if update.Sort != nil {
simples = append(simples, q.AdminRole.Sort.Value(*update.Sort))
}
err := q.Q.Transaction(func(q *q.Query) error {
// 修改角色
_, err := q.AdminRole.
Where(q.AdminRole.ID.Eq(update.Id)).
UpdateSimple(simples...)
if err != nil {
return err
}
// 修改角色关联权限
if update.Permissions != nil {
updatePermissions := *update.Permissions
permissions := make([]*models.LinkAdminRolePermission, len(updatePermissions))
for i, permissionID := range updatePermissions {
permissions[i] = &models.LinkAdminRolePermission{
RoleID: update.Id,
PermissionID: permissionID,
}
}
err = g.Redsync.WithLock(AdminRoleModifyLock, func() error {
if _, err := q.LinkAdminRolePermission.Where(q.LinkAdminRolePermission.RoleID.Eq(update.Id)).Delete(); err != nil {
return err
}
if err = q.LinkAdminRolePermission.CreateInBatches(permissions, 1000); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
return nil
})
return err
}
type UpdateAdminRole struct {
Id int32 `json:"id"`
Name *string `json:"name"`
Description *string `json:"description"`
Active *bool `json:"active"`
Sort *int32 `json:"sort"`
Permissions *[]int32 `json:"permissions"`
}
func (r *adminRoleService) RemoveAdminRole(id int32) error {
_, err := q.AdminRole.Where(q.AdminRole.ID.Eq(id)).UpdateColumn(q.AdminRole.DeletedAt, time.Now())
return err
}
var AdminRoleModifyLock = "platform:admin_role_permissions:modify"

View File

@@ -2,42 +2,35 @@ package services
import (
m "platform/web/models"
"github.com/shopspring/decimal"
q "platform/web/queries"
)
var Bill = &billService{}
type billService struct{}
func (s *billService) GenNo() string {
return ID.GenReadable("bil")
}
func newForRecharge(uid int32, billNo string, info string, amount decimal.Decimal, trade *m.Trade) *m.Bill {
return &m.Bill{
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) error {
return q.Bill.Create(&m.Bill{
UserID: uid,
BillNo: billNo,
TradeID: &trade.ID,
BillNo: ID.GenReadable("bil"),
TradeID: &tradeId,
Type: m.BillTypeRecharge,
Info: &info,
Amount: amount,
}
Info: &detail.Subject,
Amount: detail.Amount,
Actual: detail.Actual,
})
}
func newForConsume(uid int32, billNo string, info string, amount decimal.Decimal, resource *m.Resource, trade ...*m.Trade) *m.Bill {
var bill = &m.Bill{
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) error {
return q.Bill.Create(&m.Bill{
UserID: uid,
BillNo: billNo,
ResourceID: &resource.ID,
BillNo: ID.GenReadable("bil"),
ResourceID: &resourceId,
TradeID: tradeId,
CouponID: detail.CouponId,
Type: m.BillTypeConsume,
Info: &info,
Amount: amount,
}
if len(trade) > 0 {
bill.TradeID = &trade[0].ID
}
return bill
Info: &detail.Subject,
Amount: detail.Amount,
Actual: detail.Actual,
})
}

View File

@@ -174,7 +174,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
)
case isLongType:
rs, err = q.ResourceLong.Debug().
rs, err = q.ResourceLong.
Where(
q.ResourceLong.ID.Eq(*resource.LongId),
q.ResourceLong.Used.Eq(resource.Used),

139
web/services/coupon.go Normal file
View File

@@ -0,0 +1,139 @@
package services
import (
"errors"
"fmt"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"github.com/shopspring/decimal"
"gorm.io/gen/field"
"gorm.io/gorm"
)
var Coupon = &couponService{}
type couponService struct{}
func (s *couponService) All() (result []*m.Coupon, err error) {
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).Find()
}
func (s *couponService) Page(req *core.PageReq) (result []*m.Coupon, count int64, err error) {
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *couponService) Create(data CreateCouponData) error {
return q.Coupon.Create(&m.Coupon{
UserID: data.UserID,
Code: data.Code,
Remark: data.Remark,
Amount: data.Amount,
MinAmount: data.MinAmount,
Status: m.CouponStatusUnused,
ExpireAt: data.ExpireAt,
})
}
type CreateCouponData struct {
UserID *int32 `json:"user_id"`
Code string `json:"code" validate:"required"`
Remark *string `json:"remark"`
Amount decimal.Decimal `json:"amount" validate:"required"`
MinAmount decimal.Decimal `json:"min_amount"`
ExpireAt *time.Time `json:"expire_at"`
}
func (s *couponService) Update(data UpdateCouponData) error {
do := make([]field.AssignExpr, 0)
if data.UserID != nil {
do = append(do, q.Coupon.UserID.Value(*data.UserID))
}
if data.Code != nil {
do = append(do, q.Coupon.Code.Value(*data.Code))
}
if data.Remark != nil {
do = append(do, q.Coupon.Remark.Value(*data.Remark))
}
if data.Amount != nil {
do = append(do, q.Coupon.Amount.Value(*data.Amount))
}
if data.MinAmount != nil {
do = append(do, q.Coupon.MinAmount.Value(*data.MinAmount))
}
if data.Status != nil {
do = append(do, q.Coupon.Status.Value(int(*data.Status)))
}
if data.ExpireAt != nil {
do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt))
}
_, err := q.Coupon.Where(q.Coupon.ID.Eq(data.ID)).UpdateSimple(do...)
return err
}
type UpdateCouponData struct {
ID int32 `json:"id" validate:"required"`
UserID *int32 `json:"user_id"`
Code *string `json:"code"`
Remark *string `json:"remark"`
Amount *decimal.Decimal `json:"amount"`
MinAmount *decimal.Decimal `json:"min_amount"`
Status *m.CouponStatus `json:"status"`
ExpireAt *time.Time `json:"expire_at"`
}
func (s *couponService) Delete(id int32) error {
_, err := q.Coupon.Where(q.Coupon.ID.Eq(id)).UpdateColumn(q.Coupon.DeletedAt, time.Now())
return err
}
func (s *couponService) GetCouponAvailableByCode(code string, amount decimal.Decimal, uid *int32) (*m.Coupon, error) {
// 获取优惠券
coupon, err := q.Coupon.Where(
q.Coupon.Code.Eq(code),
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
q.Coupon.
Where(q.Coupon.ExpireAt.Gt(time.Now())).
Or(q.Coupon.ExpireAt.IsNull()),
).Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, core.NewBizErr("优惠券不存在或已失效")
}
if err != nil {
return nil, core.NewBizErr("获取优惠券数据失败", err)
}
// 检查最小使用额度
if amount.Cmp(coupon.MinAmount) < 0 {
return nil, core.NewBizErr(fmt.Sprintf("使用此优惠券的最小额度为 %s", coupon.MinAmount))
}
// 检查所属
if coupon.UserID != nil {
if uid == nil {
return nil, core.NewBizErr("检查优惠券所属用户失败")
}
if *coupon.UserID != *uid {
return nil, core.NewBizErr("优惠券不属于当前用户")
}
}
return coupon, nil
}
func (s *couponService) UseCoupon(q *q.Query, id int32) error {
_, err := q.Coupon.
Where(
q.Coupon.ID.Eq(id),
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
q.Coupon.ExpireAt.Gt(time.Now()),
).
UpdateSimple(
q.Coupon.Status.Value(int(m.CouponStatusUsed)),
)
return err
}

View File

@@ -0,0 +1,19 @@
package services
import (
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
)
var Permission = &permissionService{}
type permissionService struct{}
func (r *permissionService) ListPermissions() (result []*m.Permission, err error) {
return q.Permission.Order(q.Permission.Sort).Find()
}
func (p *permissionService) PagePermissions(req core.PageReq) (result []*m.Permission, count int64, err error) {
return q.Permission.Order(q.Permission.Sort).FindByPage(req.GetOffset(), req.GetLimit())
}

86
web/services/product.go Normal file
View File

@@ -0,0 +1,86 @@
package services
import (
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gen/field"
)
var Product = &productService{}
type productService struct{}
// 获取产品价格
func (s *productService) GetPrice(code string) {
q.ProductSku.Where(q.ProductSku.Code.Eq(code)).Find()
}
// 获取所有产品
func (s *productService) AllProducts() ([]*m.Product, error) {
return q.Product.
Order(q.Product.Sort.Asc(), q.Product.CreatedAt.Desc()).
Find()
}
// 新增产品
func (s *productService) CreateProduct(create *CreateProductData) error {
return q.Product.Create(&m.Product{
Code: create.Code,
Name: create.Name,
Description: create.Description,
Sort: create.Sort,
Status: m.ProductStatus(create.Status),
})
}
type CreateProductData struct {
Code string `json:"code"`
Name string `json:"name"`
Description *string `json:"description"`
Sort int32 `json:"sort"`
Status int `json:"status"`
}
// 更新产品
func (s *productService) UpdateProduct(update *UpdateProductData) error {
if update == nil {
return core.NewBizErr("更新数据不存在")
}
do := make([]field.AssignExpr, 0, 5)
if update.Code != nil {
do = append(do, q.Product.Code.Value(*update.Code))
}
if update.Name != nil {
do = append(do, q.Product.Name.Value(*update.Name))
}
if update.Description != nil {
do = append(do, q.Product.Description.Value(*update.Description))
}
if update.Sort != nil {
do = append(do, q.Product.Sort.Value(*update.Sort))
}
if update.Status != nil {
do = append(do, q.Product.Status.Value(*update.Status))
}
_, err := q.Product.Where(q.Product.ID.Eq(update.Id)).UpdateSimple(do...)
return err
}
type UpdateProductData struct {
Id int32 `json:"id"`
Code *string `json:"code"`
Name *string `json:"name"`
Description *string `json:"description"`
Sort *int32 `json:"sort"`
Status *int `json:"status"`
}
// 删除产品
func (s *productService) DeleteProduct(id int32) error {
_, err := q.Product.Where(q.Product.ID.Eq(id)).UpdateColumn(q.Product.DeletedAt, time.Now())
return err
}

View File

@@ -0,0 +1,59 @@
package services
import (
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gen/field"
)
var ProductDiscount = &productDiscountService{}
type productDiscountService struct{}
func (s *productDiscountService) All() (result []*m.ProductDiscount, err error) {
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).Find()
}
func (s *productDiscountService) Page(req *core.PageReq) (result []*m.ProductDiscount, count int64, err error) {
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *productDiscountService) Create(data CreateProductDiscountData) (err error) {
return q.ProductDiscount.Create(&m.ProductDiscount{
Name: data.Name,
Discount: data.Discount,
})
}
type CreateProductDiscountData struct {
Name string `json:"name"`
Discount int32 `json:"discount" validate:"min=0,max=100"`
}
func (s *productDiscountService) Update(data UpdateProductDiscountData) (err error) {
do := make([]field.AssignExpr, 0)
if data.Name != nil {
do = append(do, q.ProductDiscount.Name.Value(*data.Name))
}
if data.Discount != nil {
do = append(do, q.ProductDiscount.Discount.Value(*data.Discount))
}
_, err = q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(data.ID)).UpdateSimple(do...)
return err
}
type UpdateProductDiscountData struct {
ID int32 `json:"id"`
Name *string `json:"name"`
Discount *int32 `json:"discount" validate:"omitempty,min=0,max=100"`
}
func (s *productDiscountService) Delete(id int32) (err error) {
_, err = q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(id)).UpdateColumn(q.ProductDiscount.DeletedAt, time.Now())
return
}

109
web/services/product_sku.go Normal file
View File

@@ -0,0 +1,109 @@
package services
import (
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"github.com/shopspring/decimal"
"gorm.io/gen"
"gorm.io/gen/field"
)
var ProductSku = &productSkuService{}
type productSkuService struct{}
func (s *productSkuService) All(product_code string) (result []*m.ProductSku, err error) {
return q.ProductSku.
Joins(q.ProductSku.Product).
Where(q.Product.As("Product").Code.Eq(product_code)).
Select(q.ProductSku.ALL).
Order(q.ProductSku.CreatedAt.Desc()).
Find()
}
func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []*m.ProductSku, count int64, err error) {
do := make([]gen.Condition, 0)
if productId != nil {
do = append(do, q.ProductSku.ProductID.Eq(*productId))
}
return q.ProductSku.
Joins(q.ProductSku.Discount).
Where(do...).
Order(q.ProductSku.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
}
func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
price, err := decimal.NewFromString(create.Price)
if err != nil {
return core.NewBizErr("产品价格的格式不正确", err)
}
return q.ProductSku.Create(&m.ProductSku{
ProductID: create.ProductID,
DiscountId: create.DiscountID,
Code: create.Code,
Name: create.Name,
Price: price,
})
}
type CreateProductSkuData struct {
ProductID int32 `json:"product_id"`
DiscountID int32 `json:"discount_id"`
Code string `json:"code"`
Name string `json:"name"`
Price string `json:"price"`
}
func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
do := make([]field.AssignExpr, 0)
if update.Price != nil {
price, err := decimal.NewFromString(*update.Price)
if err != nil {
return core.NewBizErr("产品价格的格式不正确", err)
}
do = append(do, q.ProductSku.Price.Value(price))
}
if update.DiscountID != nil {
do = append(do, q.ProductSku.DiscountId.Value(*update.DiscountID))
}
if update.Code != nil {
do = append(do, q.ProductSku.Code.Value(*update.Code))
}
if update.Name != nil {
do = append(do, q.ProductSku.Name.Value(*update.Name))
}
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
return err
}
type UpdateProductSkuData struct {
ID int32 `json:"id"`
DiscountID *int32 `json:"discount_id"`
Code *string `json:"code"`
Name *string `json:"name"`
Price *string `json:"price"`
}
func (s *productSkuService) Delete(id int32) (err error) {
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(id)).UpdateColumn(q.ProductSku.DeletedAt, time.Now())
return
}
func (s *productSkuService) BatchUpdateDiscount(data BatchUpdateSkuDiscountData) (err error) {
_, err = q.ProductSku.Where(q.ProductSku.ProductID.Eq(data.ProductID)).UpdateSimple(
q.ProductSku.DiscountId.Value(data.DiscountID),
)
return
}
type BatchUpdateSkuDiscountData struct {
ProductID int32 `json:"product_id"`
DiscountID int32 `json:"discount_id"`
}

View File

@@ -1,8 +1,6 @@
package services
import (
"encoding/json"
"errors"
"fmt"
"platform/pkg/u"
"platform/web/core"
@@ -11,107 +9,62 @@ import (
"time"
"github.com/shopspring/decimal"
"gorm.io/gen/field"
)
var Resource = &resourceService{}
type resourceService struct{}
func (s *resourceService) CreateResourceByBalance(uid int32, now time.Time, data *CreateResourceData) error {
// CreateResourceByBalance 通过余额购买套餐
func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateResourceData) error {
now := time.Now()
// 找到用户
user, err := q.User.
Where(q.User.ID.Eq(uid)).
Take()
// 获取 sku
detail, err := data.TradeDetail(user)
if err != nil {
return err
}
// 检查余额
amount, err := data.GetAmount()
if err != nil {
return err
}
newBalance := user.Balance.Sub(amount)
if newBalance.IsNegative() {
return ErrBalanceNotEnough
return core.NewServErr("获取产品支付信息失败", err)
}
return q.Q.Transaction(func(q *q.Query) error {
// 更新用户余额
_, err = q.User.
Where(
q.User.ID.Eq(uid),
q.User.Balance.Eq(user.Balance),
).
UpdateSimple(q.User.Balance.Value(newBalance))
if err != nil {
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
return core.NewServErr("更新用户余额失败", err)
}
// 保存套餐
resource, err := createResource(q, uid, now, data)
resource, err := s.Create(q, user.ID, now, data)
if err != nil {
return core.NewServErr("创建套餐失败", err)
}
// 生成账单
subject, err := data.GetSubject()
if err != nil {
return err
}
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), subject, amount, resource))
err = Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
// 核销优惠券
if detail.CouponId != nil {
err = Coupon.UseCoupon(q, *detail.CouponId)
if err != nil {
return core.NewServErr("核销优惠券失败", err)
}
}
return nil
})
}
func (s *resourceService) CreateResourceByTrade(uid int32, now time.Time, data *CreateResourceData, trade *m.Trade) error { // 检查交易
if trade == nil {
return core.NewBizErr("交易数据不能为空")
}
if trade.Status != m.TradeStatusSuccess {
return core.NewBizErr("交易状态不正确")
}
return q.Q.Transaction(func(q *q.Query) error {
// 保存套餐
resource, err := createResource(q, uid, now, data)
if err != nil {
return core.NewServErr("创建套餐失败", err)
}
// 生成账单
subject, err := data.GetSubject()
if err != nil {
return err
}
amount, err := data.GetAmount()
if err != nil {
return err
}
err = q.Bill.Create(newForConsume(uid, Bill.GenNo(), subject, amount, resource, trade))
if err != nil {
return core.NewServErr("生成账单失败", err)
}
return nil
})
}
func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceData) (*m.Resource, error) {
func (s *resourceService) Create(q *q.Query, uid int32, now time.Time, data *CreateResourceData) (*m.Resource, error) {
// 套餐基本信息
var resource = m.Resource{
UserID: uid,
ResourceNo: u.P(ID.GenReadable("res")),
Active: true,
Type: data.Type,
Code: data.Type.Code(),
}
switch data.Type {
@@ -125,6 +78,7 @@ func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceDa
Live: short.Live,
Type: short.Mode,
Quota: short.Quota,
Code: data.Code(),
}
if short.Mode == m.ResourceModeTime {
if short.Expire == nil {
@@ -144,6 +98,7 @@ func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceDa
Live: long.Live,
Type: long.Mode,
Quota: long.Quota,
Code: data.Code(),
}
if long.Mode == m.ResourceModeTime {
if long.Expire == nil {
@@ -164,14 +119,95 @@ func createResource(q *q.Query, uid int32, now time.Time, data *CreateResourceDa
return &resource, nil
}
func (s *resourceService) Update(data *UpdateResourceData) error {
if data.Active == nil {
return core.NewBizErr("更新套餐失败active 不能为空")
}
do := make([]field.AssignExpr, 0)
if data.Active != nil {
do = append(do, q.Resource.Active.Value(*data.Active))
}
_, err := q.Resource.
Where(q.Resource.ID.Eq(data.Id)).
UpdateSimple(do...)
if err != nil {
return core.NewServErr("更新套餐失败", err)
}
return nil
}
type UpdateResourceData struct {
core.IdReq
Active *bool `json:"active"`
}
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, couponCode *string) (*m.ProductSku, *m.ProductDiscount, *m.Coupon, decimal.Decimal, decimal.Decimal, error) {
sku, err := q.ProductSku.
Joins(q.ProductSku.Discount).
Where(q.ProductSku.Code.Eq(skuCode)).
Take()
if err != nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("产品不可用", err)
}
// 原价
price := sku.Price
amount := price.Mul(decimal.NewFromInt32(count))
// 折扣价
discount := sku.Discount
if discount == nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("价格查询失败", err)
}
discountRate := discount.Rate()
if user != nil && user.DiscountID != nil { // 用户特殊优惠
uDiscount, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(*user.DiscountID)).Take()
if err != nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("客户特殊价查询失败", err)
}
uDiscountRate := uDiscount.Rate()
if uDiscountRate.Cmp(discountRate) > 0 {
discountRate = uDiscountRate
discount = uDiscount
}
}
discounted := amount.Mul(discountRate)
// 优惠价
uid := (*int32)(nil)
if user != nil {
uid = &user.ID
}
coupon := (*m.Coupon)(nil)
couponApplied := discounted.Copy()
if couponCode != nil {
var err error
coupon, err = Coupon.GetCouponAvailableByCode(*couponCode, discounted, uid)
if err != nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, err
}
couponApplied = discounted.Sub(coupon.Amount)
}
return sku, discount, coupon, discounted, couponApplied, nil
}
type CreateResourceData struct {
Type m.ResourceType `json:"type" validate:"required"`
Short *CreateShortResourceData `json:"short,omitempty"`
Long *CreateLongResourceData `json:"long,omitempty"`
Type m.ResourceType `json:"type" validate:"required"`
Short *CreateShortResourceData `json:"short,omitempty"`
Long *CreateLongResourceData `json:"long,omitempty"`
CouponCode *string `json:"coupon,omitempty"`
}
type CreateShortResourceData struct {
Live int32 `json:"live" validate:"required,min=180"`
Live int32 `json:"live" validate:"required"`
Mode m.ResourceMode `json:"mode" validate:"required"`
Quota int32 `json:"quota" validate:"required"`
Expire *int32 `json:"expire"`
@@ -190,169 +226,58 @@ type CreateLongResourceData struct {
price *decimal.Decimal
}
func (c *CreateResourceData) GetType() m.TradeType {
return m.TradeTypePurchase
}
func (c *CreateResourceData) GetSubject() (string, error) {
func (data *CreateResourceData) Count() int32 {
switch {
default:
return "", errors.New("无效的套餐类型")
case c.Type == m.ResourceTypeShort && c.Short != nil:
return c.Short.GetSubject()
case c.Type == m.ResourceTypeLong && c.Long != nil:
return c.Long.GetSubject()
return 0
case data.Type == m.ResourceTypeShort && data.Short != nil:
return data.Short.Quota
case data.Type == m.ResourceTypeLong && data.Long != nil:
return data.Long.Quota
}
}
func (c *CreateResourceData) GetAmount() (decimal.Decimal, error) {
func (data *CreateResourceData) Code() string {
switch {
default:
return decimal.Zero, errors.New("无效的套餐类型")
return ""
case c.Type == m.ResourceTypeShort && c.Short != nil:
return c.Short.GetAmount()
case c.Type == m.ResourceTypeLong && c.Long != nil:
return c.Long.GetAmount()
case data.Type == m.ResourceTypeShort && data.Short != nil:
return fmt.Sprintf(
"mode=%s,live=%d,expire=%d",
data.Short.Mode.Code(), data.Short.Live, u.Else(data.Short.Expire, 0),
)
case data.Type == m.ResourceTypeLong && data.Long != nil:
return fmt.Sprintf(
"mode=%s,live=%d,expire=%d",
data.Long.Mode.Code(), data.Long.Live, u.Else(data.Long.Expire, 0),
)
}
}
func (c *CreateResourceData) Serialize() (string, error) {
bytes, err := json.Marshal(c)
return string(bytes), err
}
func (c *CreateResourceData) Deserialize(str string) error {
return json.Unmarshal([]byte(str), c)
}
func (data *CreateShortResourceData) GetSubject() (string, error) {
if data.name == "" {
var mode string
switch data.Mode {
default:
return "", errors.New("无效的套餐模式")
case m.ResourceModeTime:
mode = "包时"
case m.ResourceModeQuota:
mode = "包量"
}
data.name = fmt.Sprintf("短效动态%s %v 分钟", mode, data.Live/60)
func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error) {
sku, discount, coupon, amount, actual, err := Resource.CalcPrice(data.Code(), data.Count(), user, data.CouponCode)
if err != nil {
return nil, err
}
return data.name, nil
}
func (data *CreateShortResourceData) GetAmount() (decimal.Decimal, error) {
if data.price == nil {
var factor int32
switch data.Mode {
default:
return decimal.Zero, errors.New("无效的套餐模式")
case m.ResourceModeTime:
if data.Expire == nil {
return decimal.Zero, errors.New("包时套餐过期时间不能为空")
}
factor = data.Quota * *data.Expire
case m.ResourceModeQuota:
factor = data.Quota
}
var base = data.Live
if base == 180 {
base = 150
}
var dec = decimal.Decimal{}.
Add(decimal.NewFromInt32(base * factor)).
Div(decimal.NewFromInt(30000))
if dec.IsZero() {
return decimal.Zero, errors.New("计算金额错误")
}
data.price = &dec
var discountId *int32 = nil
if discount != nil {
discountId = &discount.ID
}
return *data.price, nil
}
func (data *CreateLongResourceData) GetSubject() (string, error) {
if data.name == "" {
var mode string
switch data.Mode {
default:
return "", errors.New("无效的套餐模式")
case m.ResourceModeTime:
mode = "包时"
case m.ResourceModeQuota:
mode = "包量"
}
data.name = fmt.Sprintf("长效动态%s %d 小时", mode, data.Live)
var couponId *int32 = nil
if coupon != nil {
couponId = &coupon.ID
}
return data.name, nil
}
func (data *CreateLongResourceData) GetAmount() (decimal.Decimal, error) {
if data.price == nil {
var factor int32 = 0
switch data.Mode {
default:
return decimal.Zero, errors.New("无效的套餐模式")
case m.ResourceModeTime:
if data.Expire == nil {
return decimal.Zero, errors.New("包时套餐过期时间不能为空")
}
factor = *data.Expire * data.Quota
case m.ResourceModeQuota:
factor = data.Quota
}
var base int32
switch data.Live {
default:
return decimal.Zero, errors.New("无效的套餐时长")
case 1:
base = 30
case 4:
base = 80
case 8:
base = 120
case 12:
base = 180
case 24:
base = 350
}
// data.price = big.NewRat(int64(base*factor), 100)
var dec = decimal.Decimal{}.
Add(decimal.NewFromInt32(base * factor)).
Div(decimal.NewFromInt(100))
if dec.IsZero() {
return decimal.Zero, errors.New("计算金额错误")
}
data.price = &dec
}
return *data.price, nil
}
// 交易后创建套餐
type ResourceOnTradeComplete struct{}
func (r ResourceOnTradeComplete) Check(t m.TradeType) (ProductInfo, bool) {
if t == m.TradeTypePurchase {
return &CreateResourceData{}, true
}
return nil, false
}
func (r ResourceOnTradeComplete) OnTradeComplete(info ProductInfo, trade *m.Trade) error {
return Resource.CreateResourceByTrade(trade.UserID, time.Time(*trade.CompletedAt), info.(*CreateResourceData), trade)
return &TradeDetail{
data,
m.TradeTypePurchase,
sku.Name,
amount, actual,
discountId, discount,
couponId, coupon,
}, nil
}
// 服务错误

View File

@@ -1,7 +1,9 @@
package services
import (
"bytes"
"context"
"encoding/gob"
"errors"
"fmt"
"io"
@@ -21,88 +23,36 @@ import (
wecahtpaycore "github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/smartwalle/alipay/v3"
"github.com/wechatpay-apiv3/wechatpay-go/services/partnerpayments/h5"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
"gorm.io/gorm"
)
func init() {
gob.Register(&CreateResourceData{})
gob.Register(&UpdateBalanceData{})
}
var Trade = &tradeService{}
type tradeService struct {
}
// 创建交易
func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeData) (*CreateTradeResult, error) {
platform := data.Platform
method := data.Method
tType := data.Product.GetType()
expire := time.Now().Add(30 * time.Minute)
subject, err := data.Product.GetSubject()
func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productData ProductData) (*CreateTradeResult, error) {
if user == nil {
return nil, core.NewBizErr("用户未登录")
}
detail, err := productData.TradeDetail(user)
if err != nil {
return nil, err
}
amount, err := data.Product.GetAmount()
if err != nil {
return nil, err
return nil, core.NewServErr("获取产品支付信息失败", err)
}
// 实际支付金额,只在创建真实订单时使用
amountReal := amount
if env.RunMode == env.RunModeDev {
amountReal = decimal.NewFromFloat(0.01)
}
// 附加优惠券
if data.CouponCode != nil {
coupon, err := q.Coupon.
Where(
q.Coupon.Code.Eq(*data.CouponCode),
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
).
Take()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("优惠券不存在或已失效")
}
return nil, err
}
expireAt := time.Time(u.Z(coupon.ExpireAt))
if !expireAt.IsZero() && expireAt.Before(now) {
_, err = q.Coupon.
Where(q.Coupon.ID.Eq(coupon.ID)).
Update(q.Coupon.Status, m.CouponStatusExpired)
if err != nil {
return nil, err
}
return nil, errors.New("优惠券已过期")
}
if amount.Cmp(coupon.MinAmount) < 0 {
return nil, errors.New("订单金额未达到使用优惠券的条件")
}
if coupon.UserID != nil {
switch *coupon.UserID {
// 指定用户的优惠券
case uid:
amount = amount.Sub(coupon.Amount)
if expireAt.IsZero() {
_, err = q.Coupon.
Where(q.Coupon.ID.Eq(coupon.ID)).
Update(q.Coupon.Status, int(m.CouponStatusUsed))
if err != nil {
return nil, err
}
}
// 该优惠券不属于当前用户
default:
return nil, errors.New("优惠券不属于当前用户")
}
} else {
// 公开优惠券
amount = amount.Sub(coupon.Amount)
}
}
now := time.Now()
platform := tradeData.Platform
method := tradeData.Method
expireIn := time.Duration(env.TradeExpire) * time.Second
expireAt := now.Add(expireIn)
// 生成订单号
tradeNo, err := ID.GenSerial()
@@ -110,6 +60,12 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
return nil, core.NewServErr("生成订单号失败", err)
}
// 实际支付金额,只在创建真实订单时使用
amountReal := detail.Actual
if env.RunMode == env.RunModeDev {
amountReal = decimal.NewFromFloat(0.01)
}
// 提交支付订单
var paymentUrl string
switch {
@@ -122,9 +78,9 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
Trade: alipay.Trade{
ProductCode: "FAST_INSTANT_TRADE_PAY",
OutTradeNo: tradeNo,
Subject: subject,
Subject: detail.Subject,
TotalAmount: amountReal.StringFixed(2),
TimeExpire: expire.Format("2006-01-02 15:04:05"),
TimeExpire: expireAt.Format("2006-01-02 15:04:05"),
},
})
if err != nil {
@@ -138,8 +94,8 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
Appid: &env.WechatPayAppId,
Mchid: &env.WechatPayMchId,
OutTradeNo: &tradeNo,
Description: &subject,
TimeExpire: &expire,
Description: &detail.Subject,
TimeExpire: &expireAt,
NotifyUrl: &env.WechatPayCallbackUrl,
Amount: &native.Amount{
Total: u.P(amountReal.Mul(decimal.NewFromInt(100)).Round(0).IntPart()),
@@ -150,6 +106,24 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
}
paymentUrl = *resp.CodeUrl
// 微信 + 手机网站
case method == m.TradeMethodWechat && platform == m.TradePlatformMobile:
resp, _, err := g.WechatPay.H5.Prepay(context.Background(), h5.PrepayRequest{
SpAppid: &env.WechatPayAppId,
SpMchid: &env.WechatPayMchId,
OutTradeNo: &tradeNo,
Description: &detail.Subject,
TimeExpire: &expireAt,
NotifyUrl: &env.WechatPayCallbackUrl,
Amount: &h5.Amount{
Total: u.P(amountReal.Mul(decimal.NewFromInt(100)).Round(0).IntPart()),
},
})
if err != nil {
return nil, err
}
paymentUrl = *resp.H5Url
// 商福通 + 电脑网站
case
method == m.TradeMethodSftAlipay && platform == m.TradePlatformPC,
@@ -161,18 +135,17 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
payType = g.SftAlipay
case m.TradeMethodSftWechat:
payType = g.SftWeChat
default:
panic("unhandled default case")
}
resp, err := g.SFTPay.PaymentScanPay(&g.PaymentScanPayReq{
MchOrderNo: tradeNo,
Subject: subject,
Body: subject,
Subject: detail.Subject,
Body: detail.Subject,
Amount: amountReal.Mul(decimal.NewFromInt(100)).Round(0).IntPart(),
PayType: payType,
Currency: "cny",
ClientIp: "123.52.74.23",
OrderTimeout: u.P(expire.Format("2006-01-02 15:04:05")),
OrderTimeout: u.P(expireAt.Format("2006-01-02 15:04:05")),
})
if err != nil {
return nil, err
@@ -183,24 +156,24 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
case
method == m.TradeMethodSftAlipay && platform == m.TradePlatformMobile,
method == m.TradeMethodSftWechat && platform == m.TradePlatformMobile:
var payType g.SftPayType
switch method {
case m.TradeMethodSftAlipay:
payType = g.SftAlipay
case m.TradeMethodSftWechat:
payType = g.SftWeChat
default:
panic("unhandled default case")
}
resp, err := g.SFTPay.PaymentH5Pay(&g.PaymentH5PayReq{
MchOrderNo: tradeNo,
Subject: subject,
Body: subject,
Subject: detail.Subject,
Body: detail.Subject,
Amount: amountReal.Mul(decimal.NewFromInt(100)).Round(0).IntPart(),
PayType: payType,
Currency: "cny",
ClientIp: "123.52.74.23",
OrderTimeout: u.P(expire.Format("2006-01-02 15:04:05")),
OrderTimeout: u.P(expireAt.Format("2006-01-02 15:04:05")),
})
if err != nil {
return nil, err
@@ -215,11 +188,11 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
// 保存订单
err = q.Trade.Create(&m.Trade{
UserID: uid,
UserID: user.ID,
InnerNo: tradeNo,
Type: tType,
Subject: subject,
Amount: amount,
Type: detail.Type,
Subject: detail.Subject,
Amount: detail.Actual,
Method: method,
Platform: platform,
PaymentURL: &paymentUrl,
@@ -229,295 +202,240 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
}
// 缓存产品数据
serialized, err := data.Product.Serialize()
if err != nil {
return nil, core.NewServErr("序列化产品信息失败", err)
}
w := bytes.Buffer{}
gob.NewEncoder(&w).Encode(detail)
err = g.Redis.Set(
context.Background(),
tradeProductKey(tradeNo),
serialized,
time.Duration(env.TradeExpire+10)*time.Second,
w.Bytes(),
expireIn,
).Err()
if err != nil {
return nil, core.NewServErr("保存购买信息失败", err)
}
// 提交异步关闭事件
closeAt := now.Add(time.Duration(env.TradeExpire) * time.Second)
_, err = g.Asynq.Enqueue(e.NewCancelTrade(e.CompleteTradeData{
TradeNo: tradeNo,
Method: method,
}), asynq.ProcessAt(closeAt))
_, err = g.Asynq.Enqueue(e.NewCloseTradeTask(user.ID, tradeNo, method), asynq.ProcessAt(expireAt))
if err != nil {
return nil, core.NewServErr("提交异步关闭事件失败", err)
}
return &CreateTradeResult{
PaymentUrl: paymentUrl,
TradeNo: tradeNo,
PayUrl: paymentUrl,
TradeNo: tradeNo,
}, nil
}
// 完成交易
func (s *tradeService) CompleteTrade(data *ModifyTradeData) error {
return g.Redsync.WithLock(tradeLockKey(data.TradeNo), func() error {
func (s *tradeService) CompleteTrade(user *m.User, ref *TradeRef) error {
// 检查订单状态
result, err := s.CheckTrade(data)
if err != nil {
return core.NewServErr("检查订单状态失败", err)
}
if result.Status != m.TradeStatusSuccess {
switch result.Status {
case m.TradeStatusPending:
return core.NewBizErr("订单未支付")
case m.TradeStatusCanceled:
return core.NewBizErr("订单已过期")
}
}
// 更新交易状态
trade, err := completeTrade(&OnTradeCompletedData{
data.TradeNo,
result.TransId,
result.Success,
})
if err != nil {
return core.NewServErr("处理交易失败", err)
}
// 处理交易完成事件
err = afterTradeComplete(trade)
if err != nil {
return core.NewServErr("处理交易完成事件失败", err)
}
return nil
})
}
func (s *tradeService) OnTradeCompleted(data *OnTradeCompletedData) error {
return g.Redsync.WithLock(tradeLockKey(data.TradeNo), func() error {
// 更新交易状态
trade, err := completeTrade(data)
if err != nil {
return core.NewServErr("处理交易失败", err)
}
// 处理交易完成事件
err = afterTradeComplete(trade)
if err != nil {
return core.NewServErr("处理交易完成事件失败", err)
}
return nil
})
}
func completeTrade(data *OnTradeCompletedData) (*m.Trade, error) {
var trade = new(m.Trade)
var err = q.Q.Transaction(func(tx *q.Query) error {
var tradeNo = data.TradeNo
var transId = data.TransId
var payment = data.Payment
var acquirer = data.Acquirer
var paidAt = data.Time
// 获取交易信息
var err error
trade, err = q.Trade.
Where(q.Trade.InnerNo.Eq(tradeNo)).
Take()
if err != nil {
return core.NewBizErr("获取交易信息失败", err)
}
// 检查交易状态
switch trade.Status {
case m.TradeStatusCanceled:
return core.NewBizErr("交易已取消")
case m.TradeStatusSuccess:
return nil // 跳过更新交易信息
case m.TradeStatusPending:
}
// 更新交易信息
trade.Status = m.TradeStatusSuccess
trade.OuterNo = &transId
trade.Payment = payment
trade.Acquirer = u.P(acquirer)
trade.CompletedAt = u.P(paidAt)
rs, err := q.Trade.
Where(q.Trade.InnerNo.Eq(tradeNo), q.Trade.Status.Eq(int(m.TradeStatusPending))).
Updates(trade)
if rs.RowsAffected == 0 {
return core.NewBizErr("交易状态已发生变化")
}
if err != nil {
return core.NewServErr("更新交易信息失败", err)
}
return nil
})
// 检查订单状态
result, err := s.CheckTrade(ref)
if err != nil {
return nil, err
} else {
return trade, err
return core.NewServErr("检查订单状态失败", err)
}
if result.Status != m.TradeStatusSuccess {
switch result.Status {
case m.TradeStatusPending:
return core.NewBizErr("订单未支付")
case m.TradeStatusCanceled:
return core.NewBizErr("订单已过期")
}
}
}
func afterTradeComplete(trade *m.Trade) error {
// 恢复购买信息
productData, err := g.Redis.Get(context.Background(), tradeProductKey(trade.InnerNo)).Result()
// 更新交易状态
err = s.OnCompleteTrade(user, ref.TradeNo, result.TransId, &result.Success)
if err != nil {
return core.NewServErr("处理交易失败", err)
}
return nil
}
func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo string, result *TradeSuccessResult) error {
// 获取交易信息
trade, err := q.Trade.
Where(q.Trade.InnerNo.Eq(interNo)).
Take()
if err != nil {
return core.NewBizErr("获取交易信息失败", err)
}
// 检查交易状态
switch trade.Status {
case m.TradeStatusCanceled:
return core.NewBizErr("交易已取消")
case m.TradeStatusSuccess:
return nil // 跳过更新交易信息
case m.TradeStatusPending:
}
// 恢复购买信息;如果反序列化失败,检查开头 init 函数中是否注册了对应的 struct 类型
detailBytes, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Bytes()
if err != nil {
return core.NewServErr("恢复购买信息失败", err)
}
// 执行资源创建
var ComplementEvents = []CompleteEvent{
ResourceOnTradeComplete{},
UserOnTradeComplete{},
var detail TradeDetail
r := bytes.NewReader(detailBytes)
if err := gob.NewDecoder(r).Decode(&detail); err != nil {
return core.NewServErr("解析购买信息失败", err)
}
for _, event := range ComplementEvents {
info, ok := event.Check(trade.Type)
if !ok {
continue
err = q.Q.Transaction(func(q *q.Query) error {
// 更新交易信息
_, err := q.Trade.
Where(
q.Trade.InnerNo.Eq(interNo),
q.Trade.Status.Eq(int(m.TradeStatusPending)),
).
UpdateSimple(
q.Trade.Status.Value(int(m.TradeStatusSuccess)),
q.Trade.OuterNo.Value(outerNo),
q.Trade.Payment.Value(result.Actual),
q.Trade.Acquirer.Value(int(result.Acquirer)),
q.Trade.CompletedAt.Value(result.Time),
)
if err != nil {
return core.NewServErr("更新交易信息失败", err)
}
err = info.Deserialize(productData)
if err != nil {
return core.NewServErr("反序列化购买信息失败", err)
switch trade.Type {
case m.TradeTypeRecharge:
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil); err != nil {
return err
}
// 生成账单
err = Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
case m.TradeTypePurchase:
data, ok := detail.Product.(*CreateResourceData)
if !ok {
return core.NewServErr("购买信息解析失败", nil)
}
// 保存套餐
resource, err := Resource.Create(q, user.ID, result.Time, data)
if err != nil {
return core.NewServErr("创建套餐失败", err)
}
// 生成账单
err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
// 核销优惠券
if detail.CouponId != nil {
err = Coupon.UseCoupon(q, *detail.CouponId)
if err != nil {
return core.NewServErr("核销优惠券失败", err)
}
}
}
err = event.OnTradeComplete(info, trade)
if err != nil {
return core.NewServErr("处理交易完成事件失败", err)
}
return nil
})
if err != nil {
return err
}
return nil
}
// 取消交易
func (s *tradeService) CancelTrade(data *ModifyTradeData, now time.Time) error {
tradeNo := data.TradeNo
method := data.Method
func (s *tradeService) CancelTrade(ref *TradeRef) error {
now := time.Now()
return g.Redsync.WithLock(tradeLockKey(tradeNo), func() error {
switch method {
case m.TradeMethodAlipay:
resp, err := g.Alipay.TradeCancel(context.Background(), alipay.TradeCancel{
OutTradeNo: tradeNo,
})
if err != nil {
return core.NewServErr("上游取消交易失败", err)
}
if resp.Code != alipay.CodeSuccess {
slog.Error("支付宝交易取消失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg)
return errors.New("上游取消交易失败")
}
case m.TradeMethodWechat:
resp, err := g.WechatPay.Native.CloseOrder(context.Background(), native.CloseOrderRequest{
Mchid: &env.WechatPayMchId,
OutTradeNo: &tradeNo,
})
if err != nil {
return core.NewServErr("上游取消交易失败", err)
}
if resp.Response.StatusCode != http.StatusNoContent {
body, err := io.ReadAll(resp.Response.Body)
if err != nil {
slog.Error("读取微信交易取消响应失败", "error", err)
return core.NewServErr("上游取消交易失败", err)
}
slog.Error("微信交易取消失败", "code", resp.Response.StatusCode, "body", string(body))
return errors.New("上游取消交易失败")
}
case m.TradeMethodSft, m.TradeMethodSftAlipay, m.TradeMethodSftWechat:
_, err := g.SFTPay.OrderClose(&g.OrderCloseReq{
MchOrderNo: &tradeNo,
})
if err != nil {
slog.Debug(fmt.Sprintf("订单无需关闭: %s", err.Error()))
return nil
}
default:
return ErrTransactionNotSupported
}
err := cancelTrade(tradeNo, now)
switch ref.Method {
case m.TradeMethodAlipay:
resp, err := g.Alipay.TradeCancel(context.Background(), alipay.TradeCancel{
OutTradeNo: ref.TradeNo,
})
if err != nil {
return err
return core.NewServErr("上游取消交易失败", err)
}
if resp.Code != alipay.CodeSuccess {
slog.Error("支付宝交易取消失败", "code", resp.Code, "sub_code", resp.SubCode, "msg", resp.Msg)
return errors.New("上游取消交易失败")
}
return nil
})
}
func (s *tradeService) OnTradeCanceled(tradeNo string, now time.Time) error {
err := g.Redsync.WithLock(tradeLockKey(tradeNo), func() error {
return cancelTrade(tradeNo, now)
})
if err != nil {
return core.NewServErr("处理交易取消失败", err)
case m.TradeMethodWechat:
resp, err := g.WechatPay.Native.CloseOrder(context.Background(), native.CloseOrderRequest{
Mchid: &env.WechatPayMchId,
OutTradeNo: &ref.TradeNo,
})
if err != nil {
return core.NewServErr("上游取消交易失败", err)
}
if resp.Response.StatusCode != http.StatusNoContent {
body, err := io.ReadAll(resp.Response.Body)
if err != nil {
slog.Error("读取微信交易取消响应失败", "error", err)
return core.NewServErr("上游取消交易失败", err)
}
slog.Error("微信交易取消失败", "code", resp.Response.StatusCode, "body", string(body))
return errors.New("上游取消交易失败")
}
case m.TradeMethodSft, m.TradeMethodSftAlipay, m.TradeMethodSftWechat:
_, err := g.SFTPay.OrderClose(&g.OrderCloseReq{
MchOrderNo: &ref.TradeNo,
})
if err != nil {
slog.Debug(fmt.Sprintf("订单无需关闭: %s", err.Error()))
return nil
}
default:
return ErrTransactionNotSupported
}
err := s.OnCancelTrade(ref.TradeNo, now)
if err != nil {
return err
}
return nil
}
func cancelTrade(tradeNo string, now time.Time) error {
return q.Q.Transaction(func(q *q.Query) error {
// 获取交易信息
var status m.TradeStatus
err := q.Trade.
Where(q.Trade.InnerNo.Eq(tradeNo)).
Select(q.Trade.Status).
Scan(&status)
if err != nil {
return core.NewBizErr("获取交易信息失败", err)
}
func (s *tradeService) OnCancelTrade(tradeNo string, now time.Time) error {
_, err := q.Trade.
Where(
q.Trade.InnerNo.Eq(tradeNo),
q.Trade.Status.Eq(int(m.TradeStatusPending)),
).
UpdateSimple(
q.Trade.Status.Value(int(m.TradeStatusCanceled)),
q.Trade.CanceledAt.Value(now),
)
if err != nil {
return core.NewServErr("更新交易状态失败", err)
}
// 检查交易状态
switch status {
case m.TradeStatusCanceled:
return core.NewBizErr("交易已取消")
case m.TradeStatusSuccess:
return core.NewBizErr("交易已完成")
case m.TradeStatusPending:
}
// 更新交易状态
_, err = q.Trade.
Where(q.Trade.InnerNo.Eq(tradeNo)).
UpdateSimple(
q.Trade.Status.Value(int(m.TradeStatusCanceled)),
q.Trade.CanceledAt.Value(now),
)
if err != nil {
return core.NewServErr("更新交易状态失败", err)
}
return nil
})
return nil
}
// 交易退款
func (s *tradeService) RefundTrade(data *ModifyTradeData) error {
func (s *tradeService) RefundTrade(ref *TradeRef) error {
panic("todo")
}
func (s *tradeService) OnTradeRefunded(q *q.Query, tradeNo string, now time.Time) error {
func (s *tradeService) OnRefundTrade(q *q.Query, tradeNo string, now time.Time) error {
panic("todo")
}
// 检查交易状态
func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, error) {
var tradeNo = data.TradeNo
var method = data.Method
func (s *tradeService) CheckTrade(ref *TradeRef) (*CheckTradeResult, error) {
var tradeNo = ref.TradeNo
var method = ref.Method
// 检查交易号是否存在
var result = new(CheckTradeResult)
var result CheckTradeResult
switch method {
// 支付宝
@@ -547,9 +465,8 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
case alipay.TradeStatusSuccess, alipay.TradeStatusFinished:
result.Status = m.TradeStatusSuccess
result.Success = &TradeSuccessResult{}
result.Success.Acquirer = m.TradeAcquirerAlipay
result.Success.Payment, err = decimal.NewFromString(resp.TotalAmount)
result.Success.Actual, err = decimal.NewFromString(resp.ReceiptAmount)
if err != nil {
return nil, err
}
@@ -593,9 +510,8 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
case "SUCCESS", "REFUND":
result.Status = m.TradeStatusSuccess
result.Success = &TradeSuccessResult{}
result.Success.Acquirer = m.TradeAcquirerWechat
result.Success.Payment = decimal.NewFromInt(*resp.Amount.PayerTotal).Div(decimal.NewFromInt(100))
result.Success.Actual = decimal.NewFromInt(*resp.Amount.PayerTotal).Div(decimal.NewFromInt(100))
result.Success.Time, err = time.Parse(time.RFC3339, *resp.SuccessTime)
if err != nil {
return nil, err
@@ -613,12 +529,12 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
return nil, err
}
// 填充返回值
if resp.PayOrderId == nil {
return nil, errors.New("商福通交易号不存在")
}
// 填充返回值
result.TransId = *resp.PayOrderId
switch resp.State {
case g.SftInit, g.SftTradeAwait, g.SftTradeFail:
@@ -629,7 +545,6 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
case g.SftTradeSuccess, g.SftTradeRefund, g.SftRefundIng:
result.Status = m.TradeStatusSuccess
result.Success = &TradeSuccessResult{}
switch resp.PayType {
case "WECHAT":
result.Success.Acquirer = m.TradeAcquirerWechat
@@ -638,7 +553,7 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
case "UNIONPAY":
result.Success.Acquirer = m.TradeAcquirerUnionPay
}
result.Success.Payment = decimal.NewFromInt(resp.Amount).Div(decimal.NewFromInt(100))
result.Success.Actual = decimal.NewFromInt(resp.Amount).Div(decimal.NewFromInt(100))
result.Success.Time, err = time.Parse("2006-01-02 15:04:05", *resp.PayTime)
if err != nil {
return nil, err
@@ -650,7 +565,7 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
return nil, ErrTransactionNotSupported
}
return result, nil
return &result, nil
}
func tradeProductKey(no string) string {
@@ -662,18 +577,16 @@ func tradeLockKey(no string) string {
}
type CreateTradeData struct {
Platform m.TradePlatform `json:"platform" validate:"required"`
Method m.TradeMethod `json:"method" validate:"required"`
CouponCode *string `json:"coupon_code"`
Product ProductInfo
Platform m.TradePlatform `json:"platform" validate:"required"`
Method m.TradeMethod `json:"method" validate:"required"`
}
type CreateTradeResult struct {
TradeNo string
PaymentUrl string
PayUrl string `json:"pay_url"`
TradeNo string `json:"trade_no"`
}
type ModifyTradeData struct {
type TradeRef struct {
TradeNo string `json:"trade_no" query:"trade_no" validate:"required"`
Method m.TradeMethod `json:"method" validate:"required"`
}
@@ -681,12 +594,12 @@ type ModifyTradeData struct {
type CheckTradeResult struct {
TransId string
Status m.TradeStatus
Success *TradeSuccessResult
Success TradeSuccessResult
}
type TradeSuccessResult struct {
Acquirer m.TradeAcquirer
Payment decimal.Decimal
Actual decimal.Decimal
Time time.Time
}
@@ -696,17 +609,20 @@ type OnTradeCompletedData struct {
*TradeSuccessResult
}
type ProductInfo interface {
GetType() m.TradeType
GetSubject() (string, error)
GetAmount() (decimal.Decimal, error)
Serialize() (string, error)
Deserialize(str string) error
type ProductData interface {
TradeDetail(user *m.User) (*TradeDetail, error)
}
type CompleteEvent interface {
Check(t m.TradeType) (ProductInfo, bool)
OnTradeComplete(info ProductInfo, trade *m.Trade) error
type TradeDetail struct {
Product ProductData `json:"product"`
Type m.TradeType `json:"type"`
Subject string `json:"subject"`
Amount decimal.Decimal `json:"amount"`
Actual decimal.Decimal `json:"actual"`
DiscountId *int32 `json:"discount_id,omitempty"`
Discount *m.ProductDiscount `json:"-"` // 不应缓存
CouponId *int32 `json:"coupon_id,omitempty"`
Coupon *m.Coupon `json:"-"` // 不应缓存
}
type TradeErr string

View File

@@ -1,75 +1,80 @@
package services
import (
"encoding/json"
"errors"
"fmt"
"platform/pkg/u"
"platform/web/core"
g "platform/web/globals"
m "platform/web/models"
q "platform/web/queries"
"time"
"github.com/shopspring/decimal"
"golang.org/x/crypto/bcrypt"
"gorm.io/gen/field"
"gorm.io/gorm"
)
var User = &userService{}
type userService struct{}
func (s *userService) UpdateBalanceByTrade(uid int32, info *RechargeProductInfo, trade *m.Trade) (err error) {
err = g.Redsync.WithLock(userBalanceKey(uid), func() error {
return q.Q.Transaction(func(q *q.Query) error {
err = updateBalance(q, uid, info)
if err != nil {
return err
}
// 生成账单
subject, err := info.GetSubject()
if err != nil {
return err
}
amount, err := info.GetAmount()
if err != nil {
return err
}
err = q.Bill.Create(newForRecharge(uid, Bill.GenNo(), subject, amount, trade))
if err != nil {
return core.NewServErr("生成账单失败", err)
}
return nil
})
})
if err != nil {
return core.NewServErr("更新用户余额失败")
}
return nil
}
func updateBalance(q *q.Query, uid int32, info *RechargeProductInfo) error {
func (s *userService) Get(q *q.Query, uid int32) (*m.User, error) {
user, err := q.User.
Where(q.User.ID.Eq(uid)).Take()
if err != nil {
return core.NewServErr("查询用户失败", err)
return nil, core.NewServErr("查询用户失败", err)
}
return user, nil
}
func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Decimal, adminId *int32) error {
if user == nil {
return core.NewServErr("用户不存在")
}
amount, err := info.GetAmount()
if err != nil {
return err
amount := newBalance.Sub(user.Balance)
if amount.IsZero() {
return nil
}
return q.Q.Transaction(func(q *q.Query) error {
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
})
}
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
balance := user.Balance.Add(amount)
if balance.IsNegative() {
return core.NewServErr("用户余额不足")
}
_, err = q.User.
Where(q.User.ID.Eq(user.ID)).
UpdateSimple(q.User.Balance.Value(balance))
// 更新余额
_, err := q.User.
Where(
q.User.ID.Eq(user.ID),
q.User.Balance.Eq(user.Balance),
).
UpdateSimple(
q.User.Balance.Value(balance),
)
if err != nil {
return core.NewServErr("更新用户余额失败", err)
}
// 新增动账记录
err = q.BalanceActivity.Create(&m.BalanceActivity{
UserID: user.ID,
AdminID: adminId,
Amount: amount.StringFixed(2),
BalancePrev: user.Balance.StringFixed(2),
BalanceCurr: balance.StringFixed(2),
Remark: &remark,
})
if err != nil {
return core.NewServErr("新增动账记录失败", err)
}
return nil
}
@@ -77,41 +82,154 @@ func userBalanceKey(uid int32) string {
return fmt.Sprintf("user:%d:balance", uid)
}
type RechargeProductInfo struct {
type UpdateBalanceData struct {
Amount int `json:"amount"`
}
func (r *RechargeProductInfo) GetType() m.TradeType {
return m.TradeTypeRecharge
func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
amount := decimal.NewFromInt(int64(data.Amount)).Div(decimal.NewFromInt(100))
return &TradeDetail{
data,
m.TradeTypeRecharge,
fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)),
amount, amount,
nil, nil,
nil, nil,
}, nil
}
func (r *RechargeProductInfo) GetSubject() (string, error) {
amount, _ := r.GetAmount()
return fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)), nil
}
func (r *RechargeProductInfo) GetAmount() (decimal.Decimal, error) {
return decimal.NewFromInt(int64(r.Amount)).Div(decimal.NewFromInt(100)), nil
}
func (r *RechargeProductInfo) Serialize() (string, error) {
bytes, err := json.Marshal(r)
return string(bytes), err
}
func (r *RechargeProductInfo) Deserialize(str string) error {
return json.Unmarshal([]byte(str), r)
}
type UserOnTradeComplete struct{}
func (u UserOnTradeComplete) Check(t m.TradeType) (ProductInfo, bool) {
if t == m.TradeTypeRecharge {
return &RechargeProductInfo{}, true
// CreateByAdmin 管理员创建用户的数据
func (s *userService) CreateByAdmin(data CreateUserByAdminData) error {
var hashedPwd *string
if data.Password != nil {
hash, err := bcrypt.GenerateFromPassword([]byte(*data.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
hashedPwd = u.P(string(hash))
}
return nil, false
source := m.UserSourceAdd
err := q.User.Create(&m.User{
Phone: data.Phone,
AdminID: data.AdminID,
DiscountID: data.DiscountID,
Username: data.Username,
Email: data.Email,
Password: hashedPwd,
Source: &source,
Name: data.Name,
Avatar: data.Avatar,
Status: u.Else(data.Status, m.UserStatusEnabled),
ContactQQ: data.ContactQQ,
ContactWechat: data.ContactWechat,
})
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
}
if err != nil {
return err
}
return nil
}
func (u UserOnTradeComplete) OnTradeComplete(info ProductInfo, trade *m.Trade) error {
return User.UpdateBalanceByTrade(trade.UserID, info.(*RechargeProductInfo), trade)
type CreateUserByAdminData struct {
Phone string `json:"phone" validate:"required"`
AdminID *int32 `json:"admin_id"`
DiscountID *int32 `json:"discount_id"`
Username *string `json:"username"`
Email *string `json:"email"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Status *m.UserStatus `json:"status"`
ContactQQ *string `json:"contact_qq"`
ContactWechat *string `json:"contact_wechat"`
}
// UpdateByAdmin 管理员更新用户的数据
func (s *userService) UpdateByAdmin(data UpdateUserByAdminData) error {
do := make([]field.AssignExpr, 0)
if data.Phone != nil {
do = append(do, q.User.Phone.Value(*data.Phone))
}
if data.AdminID != nil {
do = append(do, q.User.AdminID.Value(*data.AdminID))
}
if data.DiscountID != nil {
do = append(do, q.User.DiscountID.Value(*data.DiscountID))
}
if data.Username != nil {
do = append(do, q.User.Username.Value(*data.Username))
}
if data.Email != nil {
do = append(do, q.User.Email.Value(*data.Email))
}
if data.Password != nil {
hash, err := bcrypt.GenerateFromPassword([]byte(*data.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
do = append(do, q.User.Password.Value(string(hash)))
}
if data.Name != nil {
do = append(do, q.User.Name.Value(*data.Name))
}
if data.Avatar != nil {
do = append(do, q.User.Avatar.Value(*data.Avatar))
}
if data.Status != nil {
do = append(do, q.User.Status.Value(int(*data.Status)))
}
if data.IDType != nil {
do = append(do, q.User.IDType.Value(int(*data.IDType)))
}
if data.IDNo != nil {
do = append(do, q.User.IDNo.Value(*data.IDNo))
}
if data.IDToken != nil {
do = append(do, q.User.IDToken.Value(*data.IDToken))
}
if data.ContactQQ != nil {
do = append(do, q.User.ContactQQ.Value(*data.ContactQQ))
}
if data.ContactWechat != nil {
do = append(do, q.User.ContactWechat.Value(*data.ContactWechat))
}
if len(do) == 0 {
return nil
}
_, err := q.User.Where(q.User.ID.Eq(data.ID)).UpdateSimple(do...)
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
}
return err
}
type UpdateUserByAdminData struct {
ID int32 `json:"id" validate:"required"`
Phone *string `json:"phone"`
AdminID *int32 `json:"admin_id"`
DiscountID *int32 `json:"discount_id"`
Username *string `json:"username"`
Email *string `json:"email"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Status *m.UserStatus `json:"status"`
IDType *m.UserIDType `json:"id_type"`
IDNo *string `json:"id_no"`
IDToken *string `json:"id_token"`
ContactQQ *string `json:"contact_qq"`
ContactWechat *string `json:"contact_wechat"`
}
func (s *userService) RemoveByAdmin(id int32) error {
_, err := q.User.Where(q.User.ID.Eq(id)).UpdateColumn(q.User.DeletedAt, time.Now())
return err
}

View File

@@ -6,29 +6,34 @@ import (
"fmt"
"log/slog"
"platform/web/events"
q "platform/web/queries"
s "platform/web/services"
"time"
"github.com/hibiken/asynq"
)
func HandleCompleteTrade(_ context.Context, task *asynq.Task) (err error) {
event := new(events.CompleteTradeData)
err = json.Unmarshal(task.Payload(), event)
if err != nil {
func HandleCompleteTrade(_ context.Context, task *asynq.Task) error {
var event events.CloseTradeData
if err := json.Unmarshal(task.Payload(), &event); err != nil {
return fmt.Errorf("解析任务参数失败: %w", err)
}
data := &s.ModifyTradeData{
data := s.TradeRef{
TradeNo: event.TradeNo,
Method: event.Method,
}
err = s.Trade.CompleteTrade(data)
// 尝试完成交易
user, err := s.User.Get(q.Q, event.UserId)
if err != nil {
return fmt.Errorf("获取用户失败: %w", err)
}
if err := s.Trade.CompleteTrade(user, &data); err != nil {
slog.Debug("完成交易失败[异步结束订单]", "err", err)
err = s.Trade.CancelTrade(data, time.Now())
if err != nil {
// 交易无法完成,关闭交易
if err := s.Trade.CancelTrade(&data); err != nil {
return fmt.Errorf("取消交易失败[异步结束订单]: %w", err)
}
}

102
web/views/iden-result.html Normal file
View File

@@ -0,0 +1,102 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>认证结果</title>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
background: #f5f5f5;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.card {
background: #fff;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
padding: 48px 40px;
max-width: 400px;
width: 100%;
text-align: center;
}
.icon {
width: 72px;
height: 72px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
font-size: 36px;
line-height: 1;
}
.icon--success {
background: #e6f9f0;
}
.icon--fail {
background: #fff1f0;
}
.title {
font-size: 22px;
font-weight: 600;
margin-bottom: 12px;
}
.title--success {
color: #0a6640;
}
.title--fail {
color: #a8071a;
}
.message {
font-size: 15px;
color: #666;
line-height: 1.6;
}
.divider {
border: none;
border-top: 1px solid #f0f0f0;
margin: 28px 0;
}
.hint {
font-size: 13px;
color: #aaa;
}
</style>
</head>
<body>
<div class="card">
{{if .Success}}
<div class="icon icon--success"></div>
<h1 class="title title--success">认证成功</h1>
{{else}}
<div class="icon icon--fail"></div>
<h1 class="title title--fail">认证失败</h1>
{{end}}
<p class="message">{{.Message}}</p>
</div>
</body>
</html>

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