Compare commits
37 Commits
62c624c88e
...
v1.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 32e56b1a0f | |||
| b436a6cade | |||
| dd08655e2c | |||
| 9fe6cb4bf5 | |||
| cf4bc4932a | |||
| dbc909c736 | |||
| 71554da541 | |||
| 8f89503c88 | |||
| 80f04c92ec | |||
| ccbc6f0b67 | |||
| d273731e31 | |||
| 65f8ee360b | |||
| 042c8d1a51 | |||
| a0b0be2b8e | |||
| 8fc1d30578 | |||
| a4d9c28702 | |||
| ccb8db555e | |||
| e70f2337cb | |||
| d59f4ca37f | |||
| 0edc883084 | |||
| d26106eb00 | |||
| 6e14ea65d0 | |||
| 982cbb4cab | |||
| a964fe4d69 | |||
| 6db3caaecb | |||
| fd475d3e63 | |||
| 9b3546b45f | |||
| b8c8c7d7b1 | |||
| 58b8849d8d | |||
| 46d326638b | |||
| cfbe751af7 | |||
| 624a5ff2c0 | |||
| 7d7b979b66 | |||
| 3040b10eed | |||
| 3fb3a8026f | |||
| fccb83c0e5 | |||
| c684523cb8 |
58
.env.example
Normal file
58
.env.example
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# 应用配置
|
||||||
|
RUN_MODE=development
|
||||||
|
DEBUG_HTTP_DUMP=false
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=app
|
||||||
|
DB_USERNAME=dev
|
||||||
|
DB_PASSWORD=dev
|
||||||
|
|
||||||
|
# redis 配置
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
# otel 配置
|
||||||
|
OTEL_HOST=127.0.0.1
|
||||||
|
OTEL_PORT=4317
|
||||||
|
OTEL_NAME_SUFFIX=dev
|
||||||
|
|
||||||
|
# 白银节点
|
||||||
|
BAIYIN_CLOUD_URL=
|
||||||
|
BAIYIN_TOKEN_URL=
|
||||||
|
|
||||||
|
# 京东实名
|
||||||
|
IDEN_ACCESS_KEY=
|
||||||
|
IDEN_SECRET_KEY=
|
||||||
|
IDEN_CALLBACK_URL=
|
||||||
|
|
||||||
|
# 支付宝(暂时弃用,但是需要配置)
|
||||||
|
ALIPAY_APP_ID=
|
||||||
|
ALIPAY_APP_PRIVATE_KEY=
|
||||||
|
ALIPAY_PUBLIC_KEY=
|
||||||
|
ALIPAY_API_CERT=
|
||||||
|
|
||||||
|
# 微信支付(暂时弃用,但是需要配置)
|
||||||
|
WECHATPAY_APP_ID=
|
||||||
|
WECHATPAY_MCH_ID=
|
||||||
|
WECHATPAY_MCH_PRIVATE_KEY_SERIAL=
|
||||||
|
WECHATPAY_MCH_PRIVATE_KEY=
|
||||||
|
WECHATPAY_PUBLIC_KEY_ID=
|
||||||
|
WECHATPAY_PUBLIC_KEY=
|
||||||
|
WECHATPAY_API_CERT=
|
||||||
|
WECHATPAY_CALLBACK_URL=
|
||||||
|
|
||||||
|
# 阿里云
|
||||||
|
ALIYUN_ACCESS_KEY=
|
||||||
|
ALIYUN_ACCESS_KEY_SECRET=
|
||||||
|
ALIYUN_SMS_SIGNATURE=
|
||||||
|
ALIYUN_SMS_TEMPLATE_LOGIN=
|
||||||
|
|
||||||
|
# 商福通
|
||||||
|
SFTPAY_ENABLE=
|
||||||
|
SFTPAY_APP_ID=
|
||||||
|
SFTPAY_ROUTE_ID=
|
||||||
|
SFTPAY_APP_PRIVATE_KEY=
|
||||||
|
SFTPAY_PUBLIC_KEY=
|
||||||
|
SFTPAY_RETURN_URL=
|
||||||
@@ -19,7 +19,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
ENV TZ=Asia/Shanghai
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
COPY --from=builder /build/bin/platform_linux_amd64 /app/platform
|
COPY --from=builder /build/bin/platform_linux_amd64 /app/platform
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -1,19 +1,23 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
用户请求需要检查数据权限
|
---
|
||||||
|
|
||||||
用反射实现环境变量解析,以简化函数签名
|
用反射实现环境变量解析,以简化函数签名
|
||||||
|
|
||||||
|
错误提示增强,展示整链路信息
|
||||||
|
|
||||||
|
交易信息持久化
|
||||||
|
|
||||||
|
订单关闭问题,在前端关闭窗口后直接调用了全部订单接口,应改成先确认再关闭
|
||||||
|
- 取消订单接口改成只允许管理员调用
|
||||||
|
- 新增关闭订单接口,关闭订单的逻辑是先尝试完成,如果订单未支付则取消订单
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
分离 task 的客户端,支持多进程(prefork 必要!)
|
分离 task 的客户端,支持多进程(prefork 必要!)
|
||||||
|
|
||||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|
||||||
|
|
||||||
慢速请求底层调用埋点监控
|
慢速请求底层调用埋点监控
|
||||||
|
|
||||||
数据库转模型文件
|
|
||||||
|
|
||||||
冷数据迁移方案
|
冷数据迁移方案
|
||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
@@ -47,13 +51,6 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|||||||
3. 异步回调事件,收到支付成功事件后自动完成订单
|
3. 异步回调事件,收到支付成功事件后自动完成订单
|
||||||
4. 用户退出支付界面,客户端主动发起关闭订单
|
4. 用户退出支付界面,客户端主动发起关闭订单
|
||||||
|
|
||||||
### 产品字典表
|
|
||||||
|
|
||||||
| 代码 | 产品 |
|
|
||||||
| ----- | ------------ |
|
|
||||||
| short | 短效动态代理 |
|
|
||||||
| long | 长效动态代理 |
|
|
||||||
|
|
||||||
### 节点分配与存储逻辑
|
### 节点分配与存储逻辑
|
||||||
|
|
||||||
提取:
|
提取:
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ func main() {
|
|||||||
m.Inquiry{},
|
m.Inquiry{},
|
||||||
m.ProductDiscount{},
|
m.ProductDiscount{},
|
||||||
m.BalanceActivity{},
|
m.BalanceActivity{},
|
||||||
|
m.CouponUser{},
|
||||||
)
|
)
|
||||||
g.Execute()
|
g.Execute()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,15 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5433:5432"
|
- "5433:5432"
|
||||||
|
|
||||||
|
asynqmon:
|
||||||
|
image: hibiken/asynqmon:latest
|
||||||
|
environment:
|
||||||
|
- REDIS_ADDR=redis:6379
|
||||||
|
ports:
|
||||||
|
- "9800:8080"
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
|
|||||||
36
go.mod
36
go.mod
@@ -23,11 +23,12 @@ require (
|
|||||||
github.com/smartwalle/alipay/v3 v3.2.28
|
github.com/smartwalle/alipay/v3 v3.2.28
|
||||||
github.com/valyala/fasthttp v1.68.0
|
github.com/valyala/fasthttp v1.68.0
|
||||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.21
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.21
|
||||||
go.opentelemetry.io/otel v1.38.0
|
go.opentelemetry.io/otel v1.43.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0
|
go.opentelemetry.io/otel/sdk v1.43.0
|
||||||
golang.org/x/crypto v0.45.0
|
go.opentelemetry.io/otel/trace v1.43.0
|
||||||
golang.org/x/sync v0.18.0
|
golang.org/x/crypto v0.49.0
|
||||||
|
golang.org/x/sync v0.20.0
|
||||||
gorm.io/datatypes v1.2.7
|
gorm.io/datatypes v1.2.7
|
||||||
gorm.io/driver/postgres v1.6.0
|
gorm.io/driver/postgres v1.6.0
|
||||||
gorm.io/gen v0.3.27
|
gorm.io/gen v0.3.27
|
||||||
@@ -59,7 +60,7 @@ require (
|
|||||||
github.com/gofiber/utils v1.1.0 // indirect
|
github.com/gofiber/utils v1.1.0 // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
@@ -86,20 +87,19 @@ require (
|
|||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/contrib v1.38.0 // indirect
|
go.opentelemetry.io/contrib v1.38.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
golang.org/x/mod v0.33.0 // indirect
|
||||||
golang.org/x/mod v0.30.0 // indirect
|
golang.org/x/net v0.52.0 // indirect
|
||||||
golang.org/x/net v0.47.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.39.0 // indirect
|
golang.org/x/tools v0.42.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||||
google.golang.org/grpc v1.77.0 // indirect
|
google.golang.org/grpc v1.80.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gorm.io/driver/mysql v1.6.0 // indirect
|
gorm.io/driver/mysql v1.6.0 // indirect
|
||||||
gorm.io/hints v1.1.2 // indirect
|
gorm.io/hints v1.1.2 // indirect
|
||||||
|
|||||||
80
go.sum
80
go.sum
@@ -154,8 +154,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@@ -277,22 +277,22 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
|||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/contrib v1.38.0 h1:msaHYZ13HfLIbqXsGwZZQBg5zgxwumlZ1mCkXn3E7LM=
|
go.opentelemetry.io/contrib v1.38.0 h1:msaHYZ13HfLIbqXsGwZZQBg5zgxwumlZ1mCkXn3E7LM=
|
||||||
go.opentelemetry.io/contrib v1.38.0/go.mod h1:4Vp7Az5Dez02V1lCi9OqLvSmSz0lbZu/O2r4XZsqwB0=
|
go.opentelemetry.io/contrib v1.38.0/go.mod h1:4Vp7Az5Dez02V1lCi9OqLvSmSz0lbZu/O2r4XZsqwB0=
|
||||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
@@ -309,8 +309,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
|||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@@ -321,8 +321,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -344,8 +344,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|||||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -357,8 +357,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -379,8 +379,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -403,8 +403,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -419,35 +419,35 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 h1:ZdyUkS9po3H7G0tuh955QVyyotWvOD4W0aEapeGeUYk=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846/go.mod h1:Fk4kyraUvqD7i5H6S43sj2W98fbZa75lpZz/eUyhfO0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
|||||||
8
pkg/env/env.go
vendored
8
pkg/env/env.go
vendored
@@ -24,7 +24,6 @@ var (
|
|||||||
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
|
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
|
||||||
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
|
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
|
||||||
DebugHttpDump = false // 是否打印请求和响应的原始数据
|
DebugHttpDump = false // 是否打印请求和响应的原始数据
|
||||||
DebugExternalChange = true // 是否实际执行外部非幂等接口调用,在开发调试时可以关闭,避免对外部数据产生影响
|
|
||||||
|
|
||||||
DbHost = "localhost"
|
DbHost = "localhost"
|
||||||
DbPort = "5432"
|
DbPort = "5432"
|
||||||
@@ -36,8 +35,9 @@ var (
|
|||||||
RedisPort = "6379"
|
RedisPort = "6379"
|
||||||
RedisPassword = ""
|
RedisPassword = ""
|
||||||
|
|
||||||
OtelHost string
|
OtelHost string
|
||||||
OtelPort string
|
OtelPort string
|
||||||
|
OtelNameSuffix string
|
||||||
|
|
||||||
BaiyinCloudUrl string
|
BaiyinCloudUrl string
|
||||||
BaiyinTokenUrl string
|
BaiyinTokenUrl string
|
||||||
@@ -106,7 +106,6 @@ func Init() {
|
|||||||
errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil))
|
errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil))
|
||||||
errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil))
|
errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil))
|
||||||
errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil))
|
errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil))
|
||||||
errs = append(errs, parse(&DebugExternalChange, "DEBUG_EXTERNAL_CHANGE", true, nil))
|
|
||||||
|
|
||||||
errs = append(errs, parse(&DbHost, "DB_HOST", true, nil))
|
errs = append(errs, parse(&DbHost, "DB_HOST", true, nil))
|
||||||
errs = append(errs, parse(&DbPort, "DB_PORT", true, nil))
|
errs = append(errs, parse(&DbPort, "DB_PORT", true, nil))
|
||||||
@@ -120,6 +119,7 @@ func Init() {
|
|||||||
|
|
||||||
errs = append(errs, parse(&OtelHost, "OTEL_HOST", true, nil))
|
errs = append(errs, parse(&OtelHost, "OTEL_HOST", true, nil))
|
||||||
errs = append(errs, parse(&OtelPort, "OTEL_PORT", true, nil))
|
errs = append(errs, parse(&OtelPort, "OTEL_PORT", true, nil))
|
||||||
|
errs = append(errs, parse(&OtelNameSuffix, "OTEL_NAME_SUFFIX", true, nil))
|
||||||
|
|
||||||
errs = append(errs, parse(&BaiyinCloudUrl, "BAIYIN_CLOUD_URL", false, nil))
|
errs = append(errs, parse(&BaiyinCloudUrl, "BAIYIN_CLOUD_URL", false, nil))
|
||||||
errs = append(errs, parse(&BaiyinTokenUrl, "BAIYIN_TOKEN_URL", false, nil))
|
errs = append(errs, parse(&BaiyinTokenUrl, "BAIYIN_TOKEN_URL", false, nil))
|
||||||
|
|||||||
51
pkg/u/u.go
51
pkg/u/u.go
@@ -53,6 +53,18 @@ func X[T comparable](v T) *T {
|
|||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// N 零值视为 nil
|
||||||
|
func N[T comparable](v *T) *T {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var zero T
|
||||||
|
if *v == zero {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
// 数组
|
// 数组
|
||||||
// ====================
|
// ====================
|
||||||
@@ -69,24 +81,15 @@ func Map[T any, R any](src []T, convert func(T) R) []R {
|
|||||||
// 时间
|
// 时间
|
||||||
// ====================
|
// ====================
|
||||||
|
|
||||||
func DateHead(date time.Time) time.Time {
|
func IsSameDate(date1, date2 time.Time) bool {
|
||||||
var y, m, d = date.Date()
|
var y1, m1, d1 = date1.Local().Date()
|
||||||
return time.Date(y, m, d, 0, 0, 0, 0, date.Location())
|
var y2, m2, d2 = date2.Local().Date()
|
||||||
}
|
return y1 == y2 && m1 == m2 && d1 == d2
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Today() time.Time {
|
func Today() time.Time {
|
||||||
return DateHead(time.Now())
|
var y, m, d = time.Now().Date()
|
||||||
}
|
return time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||||
|
|
||||||
func IsSameDate(date1, date2 time.Time) bool {
|
|
||||||
var y1, m1, d1 = date1.Date()
|
|
||||||
var y2, m2, d2 = date2.Date()
|
|
||||||
return y1 == y2 && m1 == m2 && d1 == d2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsToday(date time.Time) bool {
|
func IsToday(date time.Time) bool {
|
||||||
@@ -110,3 +113,21 @@ func CombineErrors(errs []error) error {
|
|||||||
}
|
}
|
||||||
return combinedErr
|
return combinedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
// 业务
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
func MaskPhone(phone string) string {
|
||||||
|
if len(phone) < 11 {
|
||||||
|
return phone
|
||||||
|
}
|
||||||
|
return phone[:3] + "****" + phone[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaskIdNo(idNo string) string {
|
||||||
|
if len(idNo) < 18 {
|
||||||
|
return idNo
|
||||||
|
}
|
||||||
|
return idNo[:3] + "*********" + idNo[14:]
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ if ($confrim -ne "y") {
|
|||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
docker build -t repo.lanhuip.com:8554/lanhu/platform:latest .
|
docker build -t repo.lanhuip.com/lanhu/platform:latest .
|
||||||
docker build -t repo.lanhuip.com:8554/lanhu/platform:$($args[0]) .
|
docker build -t repo.lanhuip.com/lanhu/platform:$($args[0]) .
|
||||||
|
|
||||||
docker push repo.lanhuip.com:8554/lanhu/platform:latest
|
docker push repo.lanhuip.com/lanhu/platform:latest
|
||||||
docker push repo.lanhuip.com:8554/lanhu/platform:$($args[0])
|
docker push repo.lanhuip.com/lanhu/platform:$($args[0])
|
||||||
|
|||||||
@@ -1,21 +1,293 @@
|
|||||||
-- ====================
|
-- ====================
|
||||||
-- region 填充数据
|
-- region 客户端
|
||||||
-- ====================
|
-- ====================
|
||||||
|
|
||||||
insert into client (type, spec, name, client_id, client_secret, redirect_uri) values (1, 3, 'web', 'web', '$2a$10$Ss12mXQgpYyo1CKIZ3URouDm.Lc2KcYJzsvEK2PTIXlv6fHQht45a', '');
|
insert into client (type, spec, name, client_id, client_secret, redirect_uri) values (1, 3, '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 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', '长效动态', '长效动态');
|
-- region 管理员
|
||||||
insert into product (code, name, description) values ('static', '长效静态', '长效静态');
|
-- ====================
|
||||||
|
|
||||||
|
insert into admin (username, password, name, lock) values ('admin', '', '超级管理员', true);
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- region 产品
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
delete from product where true;
|
||||||
|
|
||||||
|
insert into product (code, name, description, sort) values ('short', '短效动态', '短效动态', 1);
|
||||||
|
insert into product (code, name, description, sort) values ('long', '长效动态', '长效动态', 2);
|
||||||
|
insert into product (code, name, description, sort) values ('static', '长效静态', '长效静态', 3);
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- region 套餐
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
delete from product_sku where true;
|
||||||
|
|
||||||
|
insert into product_sku (product_id, code, name, price, price_min, sort) values
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=3&expire=0', '短效动态包量 3 分钟', 10.00, 10.00, 1),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=5&expire=0', '短效动态包量 5 分钟', 10.00, 10.00, 2),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=10&expire=0', '短效动态包量 10 分钟', 10.00, 10.00, 3),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=15&expire=0', '短效动态包量 15 分钟', 10.00, 10.00, 4),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=quota&live=30&expire=0', '短效动态包量 30 分钟', 10.00, 10.00, 5),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=7', '短效动态包时 3 分钟 7 天', 10.00, 10.00, 6),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=7', '短效动态包时 5 分钟 7 天', 10.00, 10.00, 7),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=7', '短效动态包时 10 分钟 7 天', 10.00, 10.00, 8),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=7', '短效动态包时 15 分钟 7 天', 10.00, 10.00, 9),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=7', '短效动态包时 30 分钟 7 天', 10.00, 10.00, 10),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=15', '短效动态包时 3 分钟 15 天', 10.00, 10.00, 11),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=15', '短效动态包时 5 分钟 15 天', 10.00, 10.00, 12),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=15', '短效动态包时 10 分钟 15 天', 10.00, 10.00, 13),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=15', '短效动态包时 15 分钟 15 天', 10.00, 10.00, 14),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=15', '短效动态包时 30 分钟 15 天', 10.00, 10.00, 15),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=30', '短效动态包时 3 分钟 30 天', 10.00, 10.00, 16),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=30', '短效动态包时 5 分钟 30 天', 10.00, 10.00, 17),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=30', '短效动态包时 10 分钟 30 天', 10.00, 10.00, 18),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=30', '短效动态包时 15 分钟 30 天', 10.00, 10.00, 19),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=30', '短效动态包时 30 分钟 30 天', 10.00, 10.00, 20),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=90', '短效动态包时 3 分钟 90 天', 10.00, 10.00, 21),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=90', '短效动态包时 5 分钟 90 天', 10.00, 10.00, 22),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=90', '短效动态包时 10 分钟 90 天', 10.00, 10.00, 23),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=90', '短效动态包时 15 分钟 90 天', 10.00, 10.00, 24),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=90', '短效动态包时 30 分钟 90 天', 10.00, 10.00, 25),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=180', '短效动态包时 3 分钟 180 天', 10.00, 10.00, 26),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=180', '短效动态包时 5 分钟 180 天', 10.00, 10.00, 27),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=180', '短效动态包时 10 分钟 180 天', 10.00, 10.00, 28),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=180', '短效动态包时 15 分钟 180 天', 10.00, 10.00, 29),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=180', '短效动态包时 30 分钟 180 天', 10.00, 10.00, 30),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=3&expire=365', '短效动态包时 3 分钟 365 天', 10.00, 10.00, 31),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=5&expire=365', '短效动态包时 5 分钟 365 天', 10.00, 10.00, 32),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=10&expire=365', '短效动态包时 10 分钟 365 天', 10.00, 10.00, 33),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=15&expire=365', '短效动态包时 15 分钟 365 天', 10.00, 10.00, 34),
|
||||||
|
((select id from product where code = 'short' and deleted_at is null), 'mode=time&live=30&expire=365', '短效动态包时 30 分钟 365 天', 10.00, 10.00, 35)
|
||||||
|
;
|
||||||
|
|
||||||
|
insert into product_sku (product_id, code, name, price, price_min, sort) values
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=60&expire=0', '长效动态包量 1 小时', 10.00, 10.00, 1),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=240&expire=0', '长效动态包量 4 小时', 10.00, 10.00, 2),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=480&expire=0', '长效动态包量 8 小时', 10.00, 10.00, 3),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=720&expire=0', '长效动态包量 12 小时', 10.00, 10.00, 4),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=quota&live=1440&expire=0', '长效动态包量 24 小时', 10.00, 10.00, 5),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=7', '长效动态包时 1 小时 7 天', 10.00, 10.00, 6),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=7', '长效动态包时 4 小时 7 天', 10.00, 10.00, 7),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=7', '长效动态包时 8 小时 7 天', 10.00, 10.00, 8),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=7', '长效动态包时 12 小时 7 天', 10.00, 10.00, 9),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=7', '长效动态包时 24 小时 7 天', 10.00, 10.00, 10),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=15', '长效动态包时 1 小时 15 天', 10.00, 10.00, 11),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=15', '长效动态包时 4 小时 15 天', 10.00, 10.00, 12),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=15', '长效动态包时 8 小时 15 天', 10.00, 10.00, 13),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=15', '长效动态包时 12 小时 15 天', 10.00, 10.00, 14),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=15', '长效动态包时 24 小时 15 天', 10.00, 10.00, 15),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=30', '长效动态包时 1 小时 30 天', 10.00, 10.00, 16),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=30', '长效动态包时 4 小时 30 天', 10.00, 10.00, 17),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=30', '长效动态包时 8 小时 30 天', 10.00, 10.00, 18),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=30', '长效动态包时 12 小时 30 天', 10.00, 10.00, 19),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=30', '长效动态包时 24 小时 30 天', 10.00, 10.00, 20),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=90', '长效动态包时 1 小时 90 天', 10.00, 10.00, 21),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=90', '长效动态包时 4 小时 90 天', 10.00, 10.00, 22),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=90', '长效动态包时 8 小时 90 天', 10.00, 10.00, 23),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=90', '长效动态包时 12 小时 90 天', 10.00, 10.00, 24),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=90', '长效动态包时 24 小时 90 天', 10.00, 10.00, 25),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=180', '长效动态包时 1 小时 180 天', 10.00, 10.00, 26),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=180', '长效动态包时 4 小时 180 天', 10.00, 10.00, 27),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=180', '长效动态包时 8 小时 180 天', 10.00, 10.00, 28),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=180', '长效动态包时 12 小时 180 天', 10.00, 10.00, 29),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=180','长效动态包时 24 小时 180 天', 10.00, 10.00, 30),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=60&expire=365', '长效动态包时 1 小时 365 天', 10.00, 10.00, 31),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=240&expire=365', '长效动态包时 4 小时 365 天', 10.00, 10.00, 32),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=480&expire=365', '长效动态包时 8 小时 365 天', 10.00, 10.00, 33),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=720&expire=365', '长效动态包时 12 小时 365 天', 10.00, 10.00, 34),
|
||||||
|
((select id from product where code = 'long' and deleted_at is null), 'mode=time&live=1440&expire=365','长效动态包时 24 小时 365 天', 10.00, 10.00, 35)
|
||||||
|
;
|
||||||
|
|
||||||
|
-- ====================
|
||||||
|
-- region 权限
|
||||||
|
-- ====================
|
||||||
|
|
||||||
delete from permission where true;
|
delete from permission where true;
|
||||||
insert into permission
|
|
||||||
(name, description)
|
-- --------------------------
|
||||||
values
|
-- level 1
|
||||||
('permission:read', '读取权限列表'),
|
-- --------------------------
|
||||||
('permission:write', '写入权限'),
|
insert into permission (name, description, sort) values
|
||||||
('admin-role:read', '读取管理员角色列表'),
|
('permission', '权限', 1),
|
||||||
('admin-role:write', '写入管理员角色')
|
('admin_role', '管理员角色', 2),
|
||||||
;
|
('admin', '管理员', 3),
|
||||||
|
('product', '产品', 4),
|
||||||
|
('product_sku', '产品套餐', 5),
|
||||||
|
('discount', '折扣', 6),
|
||||||
|
('resource', '用户套餐', 7),
|
||||||
|
('user', '用户', 8),
|
||||||
|
('coupon', '优惠券', 9),
|
||||||
|
('batch', '批次', 10),
|
||||||
|
('channel', 'IP', 11),
|
||||||
|
('trade', '交易', 12),
|
||||||
|
('bill', '账单', 13),
|
||||||
|
('balance_activity', '余额变动', 14),
|
||||||
|
('proxy', '代理', 15),
|
||||||
|
('coupon_user', '已发放优惠券', 16);
|
||||||
|
|
||||||
|
-- --------------------------
|
||||||
|
-- level 2
|
||||||
|
-- --------------------------
|
||||||
|
|
||||||
|
-- permission 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'permission' and deleted_at is null), 'permission:read', '读取权限列表', 1),
|
||||||
|
((select id from permission where name = 'permission' and deleted_at is null), 'permission:write', '编辑权限', 2);
|
||||||
|
|
||||||
|
-- admin_role 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:read', '读取管理员角色列表', 1),
|
||||||
|
((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:write', '编辑管理员角色', 2);
|
||||||
|
|
||||||
|
-- admin 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'admin' and deleted_at is null), 'admin:read', '读取管理员列表', 1),
|
||||||
|
((select id from permission where name = 'admin' and deleted_at is null), 'admin:write', '编辑管理员', 2);
|
||||||
|
|
||||||
|
-- product 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'product' and deleted_at is null), 'product:read', '读取产品列表', 1),
|
||||||
|
((select id from permission where name = 'product' and deleted_at is null), 'product:write', '编辑产品', 2);
|
||||||
|
|
||||||
|
-- product_sku 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:read', '读取产品套餐列表', 1),
|
||||||
|
((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:write', '编辑产品套餐', 2);
|
||||||
|
|
||||||
|
-- discount 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'discount' and deleted_at is null), 'discount:read', '读取折扣列表', 1),
|
||||||
|
((select id from permission where name = 'discount' and deleted_at is null), 'discount:write', '编辑折扣', 2);
|
||||||
|
|
||||||
|
-- resource 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'resource' and deleted_at is null), 'resource:read', '读取用户套餐列表', 1),
|
||||||
|
((select id from permission where name = 'resource' and deleted_at is null), 'resource:write', '编辑用户套餐', 2),
|
||||||
|
((select id from permission where name = 'resource' and deleted_at is null), 'resource:short', '短效动态套餐', 3),
|
||||||
|
((select id from permission where name = 'resource' and deleted_at is null), 'resource:long', '长效动态套餐', 4);
|
||||||
|
|
||||||
|
-- user 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'user' and deleted_at is null), 'user:read', '读取用户列表', 1),
|
||||||
|
((select id from permission where name = 'user' and deleted_at is null), 'user:write', '编辑用户', 2);
|
||||||
|
|
||||||
|
-- coupon 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:read', '读取优惠券列表', 1),
|
||||||
|
((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:write', '编辑优惠券', 2);
|
||||||
|
|
||||||
|
-- batch 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'batch' and deleted_at is null), 'batch:read', '读取批次列表', 1),
|
||||||
|
((select id from permission where name = 'batch' and deleted_at is null), 'batch:write', '编辑批次', 2);
|
||||||
|
|
||||||
|
-- channel 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'channel' and deleted_at is null), 'channel:read', '读取 IP 列表', 1),
|
||||||
|
((select id from permission where name = 'channel' and deleted_at is null), 'channel:write', '编辑 IP', 2);
|
||||||
|
|
||||||
|
-- proxy 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'proxy' and deleted_at is null), 'proxy:read', '读取代理列表', 1),
|
||||||
|
((select id from permission where name = 'proxy' and deleted_at is null), 'proxy:write', '编辑代理', 2);
|
||||||
|
|
||||||
|
-- trade 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'trade' and deleted_at is null), 'trade:read', '读取交易列表', 1),
|
||||||
|
((select id from permission where name = 'trade' and deleted_at is null), 'trade:write', '编辑交易', 2);
|
||||||
|
|
||||||
|
-- bill 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'bill' and deleted_at is null), 'bill:read', '读取账单列表', 1),
|
||||||
|
((select id from permission where name = 'bill' and deleted_at is null), 'bill:write', '编辑账单', 2);
|
||||||
|
|
||||||
|
-- balance_activity 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'balance_activity' and deleted_at is null), 'balance_activity:read', '读取余额变动列表', 1);
|
||||||
|
|
||||||
|
-- coupon_user 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'coupon_user' and deleted_at is null), 'coupon_user:read', '读取已发放优惠券列表', 1),
|
||||||
|
((select id from permission where name = 'coupon_user' and deleted_at is null), 'coupon_user:write', '编辑已发放优惠券', 2);
|
||||||
|
|
||||||
|
-- --------------------------
|
||||||
|
-- level 3
|
||||||
|
-- --------------------------
|
||||||
|
|
||||||
|
-- product_sku:write 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'product_sku:write' and deleted_at is null), 'product_sku:write:status', '更改产品套餐状态', 1);
|
||||||
|
|
||||||
|
-- proxy:write 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'proxy:write' and deleted_at is null), 'proxy:write:status', '更改代理状态', 1);
|
||||||
|
|
||||||
|
-- resource:short 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'resource:short' and deleted_at is null), 'resource:short:read', '读取用户短效动态套餐列表', 1);
|
||||||
|
|
||||||
|
-- resource:long 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'resource:long' and deleted_at is null), 'resource:long:read', '读取用户长效动态套餐列表', 1);
|
||||||
|
|
||||||
|
-- user:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'user:read' and deleted_at is null), 'user:read:one', '读取单个用户', 1),
|
||||||
|
((select id from permission where name = 'user:read' and deleted_at is null), 'user:read:not_bind', '读取未绑定管理员的用户列表', 2);
|
||||||
|
|
||||||
|
-- user:write 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:balance', '编辑用户余额', 1),
|
||||||
|
((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:bind', '用户认领', 2);
|
||||||
|
|
||||||
|
-- batch:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'batch:read' and deleted_at is null), 'batch:read:of_user', '读取指定用户的批次列表', 1);
|
||||||
|
|
||||||
|
-- channel:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'channel:read' and deleted_at is null), 'channel:read:of_user', '读取指定用户的 IP 列表', 1);
|
||||||
|
|
||||||
|
-- trade:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'trade:read' and deleted_at is null), 'trade:read:of_user', '读取指定用户的交易列表', 1);
|
||||||
|
|
||||||
|
-- bill:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'bill:read' and deleted_at is null), 'bill:read:of_user', '读取指定用户的账单列表', 1);
|
||||||
|
|
||||||
|
-- balance_activity:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'balance_activity:read' and deleted_at is null), 'balance_activity:read:of_user', '读取指定用户的余额变动列表', 1);
|
||||||
|
|
||||||
|
-- coupon:write 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'coupon:write' and deleted_at is null), 'coupon:write:assign', '发放优惠券', 1);
|
||||||
|
|
||||||
|
-- coupon_user:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'coupon_user:read' and deleted_at is null), 'coupon_user:read:of_user', '读取指定用户的已发放优惠券列表', 1);
|
||||||
|
|
||||||
|
-- --------------------------
|
||||||
|
-- level 4
|
||||||
|
-- --------------------------
|
||||||
|
|
||||||
|
-- user:write:balance 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'user:write:balance' and deleted_at is null), 'user:write:balance:inc', '增加用户余额', 1),
|
||||||
|
((select id from permission where name = 'user:write:balance' and deleted_at is null), 'user:write:balance:dec', '减少用户余额', 2);
|
||||||
|
|
||||||
|
-- resource:short:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'resource:short:read' and deleted_at is null), 'resource:short:read:of_user', '读取指定用户的短效动态套餐列表', 1);
|
||||||
|
|
||||||
|
-- resource:long:read 子权限
|
||||||
|
insert into permission (parent_id, name, description, sort) values
|
||||||
|
((select id from permission where name = 'resource:long:read' and deleted_at is null), 'resource:long:read:of_user', '读取指定用户的长效动态套餐列表', 1);
|
||||||
|
|
||||||
-- endregion
|
-- endregion
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ create table admin (
|
|||||||
last_login timestamptz,
|
last_login timestamptz,
|
||||||
last_login_ip inet,
|
last_login_ip inet,
|
||||||
last_login_ua text,
|
last_login_ua text,
|
||||||
|
lock bool not null default false,
|
||||||
created_at timestamptz default current_timestamp,
|
created_at timestamptz default current_timestamp,
|
||||||
updated_at timestamptz default current_timestamp,
|
updated_at timestamptz default current_timestamp,
|
||||||
deleted_at timestamptz
|
deleted_at timestamptz
|
||||||
@@ -217,6 +218,7 @@ comment on column admin.status is '状态:0-禁用,1-正常';
|
|||||||
comment on column admin.last_login is '最后登录时间';
|
comment on column admin.last_login is '最后登录时间';
|
||||||
comment on column admin.last_login_ip is '最后登录地址';
|
comment on column admin.last_login_ip is '最后登录地址';
|
||||||
comment on column admin.last_login_ua is '最后登录代理';
|
comment on column admin.last_login_ua is '最后登录代理';
|
||||||
|
comment on column admin.lock is '是否锁定编辑';
|
||||||
comment on column admin.created_at is '创建时间';
|
comment on column admin.created_at is '创建时间';
|
||||||
comment on column admin.updated_at is '更新时间';
|
comment on column admin.updated_at is '更新时间';
|
||||||
comment on column admin.deleted_at is '删除时间';
|
comment on column admin.deleted_at is '删除时间';
|
||||||
@@ -757,6 +759,10 @@ create table product_sku (
|
|||||||
code text not null unique,
|
code text not null unique,
|
||||||
name text not null,
|
name text not null,
|
||||||
price decimal not null,
|
price decimal not null,
|
||||||
|
price_min decimal not null,
|
||||||
|
status int not null default 1,
|
||||||
|
sort int not null default 0,
|
||||||
|
count_min int not null default 1,
|
||||||
created_at timestamptz default current_timestamp,
|
created_at timestamptz default current_timestamp,
|
||||||
updated_at timestamptz default current_timestamp,
|
updated_at timestamptz default current_timestamp,
|
||||||
deleted_at timestamptz
|
deleted_at timestamptz
|
||||||
@@ -772,7 +778,10 @@ comment on column product_sku.product_id is '产品ID';
|
|||||||
comment on column product_sku.discount_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.code is 'SKU 代码:格式为 key=value,key=value,...,其中,key:value 是 SKU 的属性,多个属性用逗号分隔';
|
||||||
comment on column product_sku.name is 'SKU 可读名称';
|
comment on column product_sku.name is 'SKU 可读名称';
|
||||||
comment on column product_sku.price is '定价';
|
comment on column product_sku.price_min is '最低价格';
|
||||||
|
comment on column product_sku.status is 'SKU状态:0-禁用,1-正常';
|
||||||
|
comment on column product_sku.sort is '排序';
|
||||||
|
comment on column product_sku.count_min is '最小购买数量';
|
||||||
comment on column product_sku.created_at is '创建时间';
|
comment on column product_sku.created_at is '创建时间';
|
||||||
comment on column product_sku.updated_at is '更新时间';
|
comment on column product_sku.updated_at is '更新时间';
|
||||||
comment on column product_sku.deleted_at is '删除时间';
|
comment on column product_sku.deleted_at is '删除时间';
|
||||||
@@ -809,6 +818,7 @@ create table resource (
|
|||||||
code text,
|
code text,
|
||||||
type int not null,
|
type int not null,
|
||||||
active bool not null default true,
|
active bool not null default true,
|
||||||
|
checkip bool not null default true,
|
||||||
created_at timestamptz default current_timestamp,
|
created_at timestamptz default current_timestamp,
|
||||||
updated_at timestamptz default current_timestamp,
|
updated_at timestamptz default current_timestamp,
|
||||||
deleted_at timestamptz
|
deleted_at timestamptz
|
||||||
@@ -823,9 +833,10 @@ comment on table resource is '套餐表';
|
|||||||
comment on column resource.id is '套餐ID';
|
comment on column resource.id is '套餐ID';
|
||||||
comment on column resource.user_id is '用户ID';
|
comment on column resource.user_id is '用户ID';
|
||||||
comment on column resource.resource_no is '套餐编号';
|
comment on column resource.resource_no is '套餐编号';
|
||||||
comment on column resource.active is '套餐状态';
|
|
||||||
comment on column resource.type is '套餐类型:1-短效动态,2-长效动态';
|
|
||||||
comment on column resource.code is '产品编码';
|
comment on column resource.code is '产品编码';
|
||||||
|
comment on column resource.type is '套餐类型:1-短效动态,2-长效动态';
|
||||||
|
comment on column resource.active is '套餐状态';
|
||||||
|
comment on column resource.checkip is '提取时是否检查 ip 地址';
|
||||||
comment on column resource.created_at is '创建时间';
|
comment on column resource.created_at is '创建时间';
|
||||||
comment on column resource.updated_at is '更新时间';
|
comment on column resource.updated_at is '更新时间';
|
||||||
comment on column resource.deleted_at is '删除时间';
|
comment on column resource.deleted_at is '删除时间';
|
||||||
@@ -984,7 +995,7 @@ create table bill (
|
|||||||
trade_id int,
|
trade_id int,
|
||||||
resource_id int,
|
resource_id int,
|
||||||
refund_id int,
|
refund_id int,
|
||||||
coupon_id int,
|
coupon_user_id int,
|
||||||
bill_no text not null,
|
bill_no text not null,
|
||||||
info text,
|
info text,
|
||||||
type int not null,
|
type int not null,
|
||||||
@@ -999,7 +1010,7 @@ create index idx_bill_user_id on bill (user_id) where deleted_at is null;
|
|||||||
create index idx_bill_trade_id on bill (trade_id) where deleted_at is null;
|
create index idx_bill_trade_id on bill (trade_id) where deleted_at is null;
|
||||||
create index idx_bill_resource_id on bill (resource_id) where deleted_at is null;
|
create index idx_bill_resource_id on bill (resource_id) where deleted_at is null;
|
||||||
create index idx_bill_refund_id on bill (refund_id) where deleted_at is null;
|
create index idx_bill_refund_id on bill (refund_id) where deleted_at is null;
|
||||||
create index idx_bill_coupon_id on bill (coupon_id) where deleted_at is null;
|
create index idx_bill_coupon_id on bill (coupon_user_id) where deleted_at is null;
|
||||||
create index idx_bill_created_at on bill (created_at) where deleted_at is null;
|
create index idx_bill_created_at on bill (created_at) where deleted_at is null;
|
||||||
|
|
||||||
-- bill表字段注释
|
-- bill表字段注释
|
||||||
@@ -1009,6 +1020,7 @@ comment on column bill.user_id is '用户ID';
|
|||||||
comment on column bill.trade_id is '订单ID';
|
comment on column bill.trade_id is '订单ID';
|
||||||
comment on column bill.resource_id is '套餐ID';
|
comment on column bill.resource_id is '套餐ID';
|
||||||
comment on column bill.refund_id is '退款ID';
|
comment on column bill.refund_id is '退款ID';
|
||||||
|
comment on column bill.coupon_user_id is '优惠券发放ID';
|
||||||
comment on column bill.bill_no is '易读账单号';
|
comment on column bill.bill_no is '易读账单号';
|
||||||
comment on column bill.info is '产品可读信息';
|
comment on column bill.info is '产品可读信息';
|
||||||
comment on column bill.type is '账单类型:1-消费,2-退款,3-充值';
|
comment on column bill.type is '账单类型:1-消费,2-退款,3-充值';
|
||||||
@@ -1051,36 +1063,59 @@ comment on column balance_activity.created_at is '创建时间';
|
|||||||
-- coupon 优惠券
|
-- coupon 优惠券
|
||||||
drop table if exists coupon cascade;
|
drop table if exists coupon cascade;
|
||||||
create table coupon (
|
create table coupon (
|
||||||
id int generated by default as identity primary key,
|
id int generated by default as identity primary key,
|
||||||
user_id int,
|
name text not null,
|
||||||
code text not null,
|
amount decimal(12, 2) not null default 0,
|
||||||
remark text,
|
min_amount decimal(12, 2) not null default 0,
|
||||||
amount decimal(12, 2) not null default 0,
|
count int not null default 0,
|
||||||
min_amount decimal(12, 2) not null default 0,
|
status int not null default 1,
|
||||||
status int not null default 0,
|
expire_type int not null default 0,
|
||||||
expire_at timestamptz,
|
expire_at timestamptz,
|
||||||
created_at timestamptz default current_timestamp,
|
expire_in int,
|
||||||
updated_at timestamptz default current_timestamp,
|
created_at timestamptz default current_timestamp,
|
||||||
deleted_at timestamptz
|
updated_at timestamptz default current_timestamp,
|
||||||
|
deleted_at timestamptz
|
||||||
);
|
);
|
||||||
create index idx_coupon_user_id on coupon (user_id) where deleted_at is null;
|
|
||||||
create index idx_coupon_code on coupon (code) where deleted_at is null;
|
|
||||||
create index idx_coupon_created_at on coupon (created_at) where deleted_at is null;
|
|
||||||
|
|
||||||
-- coupon表字段注释
|
-- coupon表字段注释
|
||||||
comment on table coupon is '优惠券表';
|
comment on table coupon is '优惠券表';
|
||||||
comment on column coupon.id is '优惠券ID';
|
comment on column coupon.id is '优惠券ID';
|
||||||
comment on column coupon.user_id is '用户ID';
|
comment on column coupon.name is '优惠券名称';
|
||||||
comment on column coupon.code is '优惠券代码';
|
|
||||||
comment on column coupon.remark is '优惠券备注';
|
|
||||||
comment on column coupon.amount is '优惠券金额';
|
comment on column coupon.amount is '优惠券金额';
|
||||||
comment on column coupon.min_amount is '最低消费金额';
|
comment on column coupon.min_amount is '最低消费金额';
|
||||||
comment on column coupon.status is '优惠券状态:0-未使用,1-已使用,2-已过期';
|
comment on column coupon.count is '优惠券数量';
|
||||||
comment on column coupon.expire_at is '过期时间';
|
comment on column coupon.status is '优惠券状态:0-禁用,1-正常';
|
||||||
|
comment on column coupon.expire_type is '过期类型:0-不过期,1-固定日期,2-相对日期(从发放时间算起)';
|
||||||
|
comment on column coupon.expire_at is '过期时间,固定日期必填';
|
||||||
|
comment on column coupon.expire_in is '过期时长(天),相对日期必填';
|
||||||
comment on column coupon.created_at is '创建时间';
|
comment on column coupon.created_at is '创建时间';
|
||||||
comment on column coupon.updated_at is '更新时间';
|
comment on column coupon.updated_at is '更新时间';
|
||||||
comment on column coupon.deleted_at is '删除时间';
|
comment on column coupon.deleted_at is '删除时间';
|
||||||
|
|
||||||
|
-- coupon_user 优惠券发放
|
||||||
|
drop table if exists coupon_user cascade;
|
||||||
|
create table coupon_user (
|
||||||
|
id int generated by default as identity primary key,
|
||||||
|
coupon_id int not null,
|
||||||
|
user_id int not null,
|
||||||
|
status int not null default 0,
|
||||||
|
expire_at timestamptz,
|
||||||
|
used_at timestamptz,
|
||||||
|
created_at timestamptz default current_timestamp
|
||||||
|
);
|
||||||
|
create index idx_coupon_user_coupon_id on coupon_user (coupon_id);
|
||||||
|
create index idx_coupon_user_user_id on coupon_user (user_id);
|
||||||
|
|
||||||
|
-- coupon_user表字段注释
|
||||||
|
comment on table coupon_user is '优惠券发放表';
|
||||||
|
comment on column coupon_user.id is '记录ID';
|
||||||
|
comment on column coupon_user.coupon_id is '优惠券ID';
|
||||||
|
comment on column coupon_user.user_id is '用户ID';
|
||||||
|
comment on column coupon_user.status is '使用状态:0-未使用,1-已使用,2-已禁用';
|
||||||
|
comment on column coupon_user.expire_at is '过期时间';
|
||||||
|
comment on column coupon_user.used_at is '使用时间';
|
||||||
|
comment on column coupon_user.created_at is '创建时间';
|
||||||
|
|
||||||
-- endregion
|
-- endregion
|
||||||
|
|
||||||
-- ====================
|
-- ====================
|
||||||
@@ -1185,11 +1220,13 @@ alter table bill
|
|||||||
alter table bill
|
alter table bill
|
||||||
add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null;
|
add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null;
|
||||||
alter table bill
|
alter table bill
|
||||||
add constraint fk_bill_coupon_id foreign key (coupon_id) references coupon (id) on delete set null;
|
add constraint fk_bill_coupon_id foreign key (coupon_user_id) references coupon_user (id) on delete set null;
|
||||||
|
|
||||||
-- coupon表外键
|
-- coupon_user表外键
|
||||||
alter table coupon
|
alter table coupon_user
|
||||||
add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade;
|
add constraint fk_coupon_user_user_id foreign key (user_id) references "user" (id) on delete cascade;
|
||||||
|
alter table coupon_user
|
||||||
|
add constraint fk_coupon_user_coupon_id foreign key (coupon_id) references coupon (id) on delete cascade;
|
||||||
|
|
||||||
-- product_sku表外键
|
-- product_sku表外键
|
||||||
alter table product_sku
|
alter table product_sku
|
||||||
|
|||||||
81
scripts/sql/update.sql
Normal file
81
scripts/sql/update.sql
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
-- ====================
|
||||||
|
-- region 套餐更新
|
||||||
|
-- ====================
|
||||||
|
|
||||||
|
-- --------------------------
|
||||||
|
-- 短效动态
|
||||||
|
-- --------------------------
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 3 分钟', price = 10.00, price_min = 10.00, sort = 1 where code = 'mode=quota&live=3&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 5 分钟', price = 10.00, price_min = 10.00, sort = 2 where code = 'mode=quota&live=5&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 10 分钟', price = 10.00, price_min = 10.00, sort = 3 where code = 'mode=quota&live=10&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 15 分钟', price = 10.00, price_min = 10.00, sort = 4 where code = 'mode=quota&live=15&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包量 30 分钟', price = 10.00, price_min = 10.00, sort = 5 where code = 'mode=quota&live=30&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 7 天', price = 10.00, price_min = 10.00, sort = 6 where code = 'mode=time&live=3&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 7 天', price = 10.00, price_min = 10.00, sort = 7 where code = 'mode=time&live=5&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 7 天', price = 10.00, price_min = 10.00, sort = 8 where code = 'mode=time&live=10&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 7 天', price = 10.00, price_min = 10.00, sort = 9 where code = 'mode=time&live=15&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 7 天', price = 10.00, price_min = 10.00, sort = 10 where code = 'mode=time&live=30&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 15 天', price = 10.00, price_min = 10.00, sort = 11 where code = 'mode=time&live=3&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 15 天', price = 10.00, price_min = 10.00, sort = 12 where code = 'mode=time&live=5&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 15 天', price = 10.00, price_min = 10.00, sort = 13 where code = 'mode=time&live=10&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 15 天', price = 10.00, price_min = 10.00, sort = 14 where code = 'mode=time&live=15&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 15 天', price = 10.00, price_min = 10.00, sort = 15 where code = 'mode=time&live=30&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 30 天', price = 10.00, price_min = 10.00, sort = 16 where code = 'mode=time&live=3&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 30 天', price = 10.00, price_min = 10.00, sort = 17 where code = 'mode=time&live=5&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 30 天', price = 10.00, price_min = 10.00, sort = 18 where code = 'mode=time&live=10&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 30 天', price = 10.00, price_min = 10.00, sort = 19 where code = 'mode=time&live=15&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 30 天', price = 10.00, price_min = 10.00, sort = 20 where code = 'mode=time&live=30&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 90 天', price = 10.00, price_min = 10.00, sort = 21 where code = 'mode=time&live=3&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 90 天', price = 10.00, price_min = 10.00, sort = 22 where code = 'mode=time&live=5&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 90 天', price = 10.00, price_min = 10.00, sort = 23 where code = 'mode=time&live=10&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 90 天', price = 10.00, price_min = 10.00, sort = 24 where code = 'mode=time&live=15&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 90 天', price = 10.00, price_min = 10.00, sort = 25 where code = 'mode=time&live=30&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 180 天', price = 10.00, price_min = 10.00, sort = 26 where code = 'mode=time&live=3&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 180 天', price = 10.00, price_min = 10.00, sort = 27 where code = 'mode=time&live=5&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 180 天', price = 10.00, price_min = 10.00, sort = 28 where code = 'mode=time&live=10&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 180 天', price = 10.00, price_min = 10.00, sort = 29 where code = 'mode=time&live=15&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 180 天', price = 10.00, price_min = 10.00, sort = 30 where code = 'mode=time&live=30&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 3 分钟 365 天', price = 10.00, price_min = 10.00, sort = 31 where code = 'mode=time&live=3&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 5 分钟 365 天', price = 10.00, price_min = 10.00, sort = 32 where code = 'mode=time&live=5&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 10 分钟 365 天', price = 10.00, price_min = 10.00, sort = 33 where code = 'mode=time&live=10&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 15 分钟 365 天', price = 10.00, price_min = 10.00, sort = 34 where code = 'mode=time&live=15&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'short' and deleted_at is null), name = '短效动态包时 30 分钟 365 天', price = 10.00, price_min = 10.00, sort = 35 where code = 'mode=time&live=30&expire=365' and deleted_at is null;
|
||||||
|
|
||||||
|
-- --------------------------
|
||||||
|
-- 长效动态
|
||||||
|
-- --------------------------
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 1 小时', price = 10.00, price_min = 10.00, sort = 1 where code = 'mode=quota&live=60&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 4 小时', price = 10.00, price_min = 10.00, sort = 2 where code = 'mode=quota&live=240&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 8 小时', price = 10.00, price_min = 10.00, sort = 3 where code = 'mode=quota&live=480&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 12 小时', price = 10.00, price_min = 10.00, sort = 4 where code = 'mode=quota&live=720&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包量 24 小时', price = 10.00, price_min = 10.00, sort = 5 where code = 'mode=quota&live=1440&expire=0' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 7 天', price = 10.00, price_min = 10.00, sort = 6 where code = 'mode=time&live=60&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 7 天', price = 10.00, price_min = 10.00, sort = 7 where code = 'mode=time&live=240&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 7 天', price = 10.00, price_min = 10.00, sort = 8 where code = 'mode=time&live=480&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 7 天', price = 10.00, price_min = 10.00, sort = 9 where code = 'mode=time&live=720&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 7 天', price = 10.00, price_min = 10.00, sort = 10 where code = 'mode=time&live=1440&expire=7' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 15 天', price = 10.00, price_min = 10.00, sort = 11 where code = 'mode=time&live=60&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 15 天', price = 10.00, price_min = 10.00, sort = 12 where code = 'mode=time&live=240&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 15 天', price = 10.00, price_min = 10.00, sort = 13 where code = 'mode=time&live=480&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 15 天', price = 10.00, price_min = 10.00, sort = 14 where code = 'mode=time&live=720&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 15 天', price = 10.00, price_min = 10.00, sort = 15 where code = 'mode=time&live=1440&expire=15' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 30 天', price = 10.00, price_min = 10.00, sort = 16 where code = 'mode=time&live=60&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 30 天', price = 10.00, price_min = 10.00, sort = 17 where code = 'mode=time&live=240&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 30 天', price = 10.00, price_min = 10.00, sort = 18 where code = 'mode=time&live=480&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 30 天', price = 10.00, price_min = 10.00, sort = 19 where code = 'mode=time&live=720&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 30 天', price = 10.00, price_min = 10.00, sort = 20 where code = 'mode=time&live=1440&expire=30' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 90 天', price = 10.00, price_min = 10.00, sort = 21 where code = 'mode=time&live=60&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 90 天', price = 10.00, price_min = 10.00, sort = 22 where code = 'mode=time&live=240&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 90 天', price = 10.00, price_min = 10.00, sort = 23 where code = 'mode=time&live=480&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 90 天', price = 10.00, price_min = 10.00, sort = 24 where code = 'mode=time&live=720&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 90 天', price = 10.00, price_min = 10.00, sort = 25 where code = 'mode=time&live=1440&expire=90' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 180 天', price = 10.00, price_min = 10.00, sort = 26 where code = 'mode=time&live=60&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 180 天', price = 10.00, price_min = 10.00, sort = 27 where code = 'mode=time&live=240&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 180 天', price = 10.00, price_min = 10.00, sort = 28 where code = 'mode=time&live=480&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 180 天', price = 10.00, price_min = 10.00, sort = 29 where code = 'mode=time&live=720&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 180 天', price = 10.00, price_min = 10.00, sort = 30 where code = 'mode=time&live=1440&expire=180' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 1 小时 365 天', price = 10.00, price_min = 10.00, sort = 31 where code = 'mode=time&live=60&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 4 小时 365 天', price = 10.00, price_min = 10.00, sort = 32 where code = 'mode=time&live=240&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 8 小时 365 天', price = 10.00, price_min = 10.00, sort = 33 where code = 'mode=time&live=480&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 12 小时 365 天', price = 10.00, price_min = 10.00, sort = 34 where code = 'mode=time&live=720&expire=365' and deleted_at is null;
|
||||||
|
update product_sku set product_id = (select id from product where code = 'long' and deleted_at is null), name = '长效动态包时 24 小时 365 天', price = 10.00, price_min = 10.00, sort = 35 where code = 'mode=time&live=1440&expire=365' and deleted_at is null;
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"platform/pkg/u"
|
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
@@ -50,9 +49,8 @@ func authUser(loginType PwdLoginType, username, password string) (user *m.User,
|
|||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
user = &m.User{
|
user = &m.User{
|
||||||
Phone: username,
|
Phone: username,
|
||||||
Username: u.P(username),
|
Status: m.UserStatusEnabled,
|
||||||
Status: m.UserStatusEnabled,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case PwdLoginByEmail:
|
case PwdLoginByEmail:
|
||||||
@@ -79,7 +77,7 @@ func authUser(loginType PwdLoginType, username, password string) (user *m.User,
|
|||||||
|
|
||||||
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
|
func authUserBySms(tx *q.Query, username, code string) (*m.User, error) {
|
||||||
// 验证验证码
|
// 验证验证码
|
||||||
err := s.Verifier.VerifySms(context.Background(), username, code)
|
err := s.Verifier.VerifySms(context.Background(), username, code, s.VerifierSmsPurposeLogin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, core.NewBizErr("短信认证失败", err)
|
return nil, core.NewBizErr("短信认证失败", err)
|
||||||
}
|
}
|
||||||
@@ -169,6 +167,9 @@ func adminScopes(admin *m.Admin) ([]string, error) {
|
|||||||
|
|
||||||
scopeNames := make([]string, 0, len(scopes))
|
scopeNames := make([]string, 0, len(scopes))
|
||||||
for _, scope := range scopes {
|
for _, scope := range scopes {
|
||||||
|
if scope.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
scopeNames = append(scopeNames, scope.Name)
|
scopeNames = append(scopeNames, scope.Name)
|
||||||
}
|
}
|
||||||
return scopeNames, nil
|
return scopeNames, nil
|
||||||
|
|||||||
@@ -356,6 +356,11 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 非锁定管理员,不允许为空权限
|
||||||
|
if !admin.Lock && (len(scopes) == 0) {
|
||||||
|
return nil, ErrAuthorizeInvalidScope // 没有配置权限
|
||||||
|
}
|
||||||
|
|
||||||
// 更新管理员登录时间
|
// 更新管理员登录时间
|
||||||
admin.LastLogin = u.P(time.Now())
|
admin.LastLogin = u.P(time.Now())
|
||||||
admin.LastLoginIP = ip
|
admin.LastLoginIP = ip
|
||||||
@@ -532,10 +537,10 @@ func introspectUser(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
|||||||
|
|
||||||
// 掩码敏感信息
|
// 掩码敏感信息
|
||||||
if profile.Phone != "" {
|
if profile.Phone != "" {
|
||||||
profile.Phone = maskPhone(profile.Phone)
|
profile.Phone = u.MaskPhone(profile.Phone)
|
||||||
}
|
}
|
||||||
if profile.IDNo != nil && *profile.IDNo != "" {
|
if profile.IDNo != nil && *profile.IDNo != "" {
|
||||||
profile.IDNo = u.P(maskIdNo(*profile.IDNo))
|
profile.IDNo = u.P(u.MaskIdNo(*profile.IDNo))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.JSON(struct {
|
return ctx.JSON(struct {
|
||||||
@@ -574,20 +579,6 @@ func introspectAdmin(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
|||||||
}{profile, list})
|
}{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 {
|
type CodeContext struct {
|
||||||
UserID int32 `json:"user_id"`
|
UserID int32 `json:"user_id"`
|
||||||
ClientID int32 `json:"client_id"`
|
ClientID int32 `json:"client_id"`
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func FindSession(accessToken string, now time.Time) (*m.Session, error) {
|
|||||||
Preload(field.Associations).
|
Preload(field.Associations).
|
||||||
Where(
|
Where(
|
||||||
q.Session.AccessToken.Eq(accessToken),
|
q.Session.AccessToken.Eq(accessToken),
|
||||||
q.Session.AccessTokenExpires.Gt(now),
|
q.Session.AccessTokenExpires.Gt(now.UTC()),
|
||||||
).First()
|
).First()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ func FindSessionByRefresh(refreshToken string, now time.Time) (*m.Session, error
|
|||||||
Preload(field.Associations).
|
Preload(field.Associations).
|
||||||
Where(
|
Where(
|
||||||
q.Session.RefreshToken.Eq(refreshToken),
|
q.Session.RefreshToken.Eq(refreshToken),
|
||||||
q.Session.RefreshTokenExpires.Gt(now),
|
q.Session.RefreshTokenExpires.Gt(now.UTC()),
|
||||||
).First()
|
).First()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func Query(in any) url.Values {
|
|||||||
case int:
|
case int:
|
||||||
out.Add(name, strconv.Itoa(value))
|
out.Add(name, strconv.Itoa(value))
|
||||||
case bool:
|
case bool:
|
||||||
if tags[1] == "b2i" {
|
if len(tags) > 1 && tags[1] == "b2i" {
|
||||||
out.Add(name, u.Ternary(value, "1", "0"))
|
out.Add(name, u.Ternary(value, "1", "0"))
|
||||||
} else {
|
} else {
|
||||||
out.Add(name, strconv.FormatBool(value))
|
out.Add(name, strconv.FormatBool(value))
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ type IModel interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
ID int32 `json:"id" gorm:"column:id;primaryKey"`
|
ID int32 `json:"id,omitzero" gorm:"column:id;primaryKey"`
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
|
CreatedAt time.Time `json:"created_at,omitzero" gorm:"column:created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
|
UpdatedAt time.Time `json:"updated_at,omitzero" gorm:"column:updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"column:deleted_at"`
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"column:deleted_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ const (
|
|||||||
ScopeProductRead = string("product:read") // 读取产品列表
|
ScopeProductRead = string("product:read") // 读取产品列表
|
||||||
ScopeProductWrite = string("product:write") // 写入产品
|
ScopeProductWrite = string("product:write") // 写入产品
|
||||||
|
|
||||||
ScopeProductSku = string("product_sku") // 产品套餐
|
ScopeProductSku = string("product_sku") // 产品套餐
|
||||||
ScopeProductSkuRead = string("product_sku:read") // 读取产品套餐列表
|
ScopeProductSkuRead = string("product_sku:read") // 读取产品套餐列表
|
||||||
ScopeProductSkuWrite = string("product_sku:write") // 写入产品套餐
|
ScopeProductSkuWrite = string("product_sku:write") // 写入产品套餐
|
||||||
|
ScopeProductSkuWriteStatus = string("product_sku:write:status") // 更改产品套餐状态
|
||||||
|
|
||||||
ScopeDiscount = string("discount") // 折扣
|
ScopeDiscount = string("discount") // 折扣
|
||||||
ScopeDiscountRead = string("discount:read") // 读取折扣列表
|
ScopeDiscountRead = string("discount:read") // 读取折扣列表
|
||||||
@@ -29,30 +30,62 @@ const (
|
|||||||
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
|
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
|
||||||
ScopeResourceWrite = string("resource:write") // 写入用户套餐
|
ScopeResourceWrite = string("resource:write") // 写入用户套餐
|
||||||
|
|
||||||
ScopeUser = string("user") // 用户
|
ScopeResourceShort = string("resource:short") // 短效动态套餐
|
||||||
ScopeUserRead = string("user:read") // 读取用户列表
|
ScopeResourceShortRead = string("resource:short:read") // 读取用户短效动态套餐列表
|
||||||
ScopeUserReadOne = string("user:read:one") // 读取单个用户
|
ScopeResourceShortReadOfUser = string("resource:short:read:of_user") // 读取指定用户的短效动态套餐列表
|
||||||
ScopeUserWrite = string("user:write") // 写入用户
|
|
||||||
ScopeUserWriteBalance = string("user:write:balance") // 写入用户余额
|
|
||||||
ScopeUserWriteBind = string("user:write:bind") // 用户认领
|
|
||||||
|
|
||||||
ScopeCoupon = string("coupon") // 优惠券
|
ScopeResourceLong = string("resource:long") // 长效动态套餐
|
||||||
ScopeCouponRead = string("coupon:read") // 读取优惠券列表
|
ScopeResourceLongRead = string("resource:long:read") // 读取用户长效动态套餐列表
|
||||||
ScopeCouponWrite = string("coupon:write") // 写入优惠券
|
ScopeResourceLongReadOfUser = string("resource:long:read:of_user") // 读取指定用户的长效动态套餐列表
|
||||||
|
|
||||||
ScopeBatch = string("batch") // 批次
|
ScopeUser = string("user") // 用户
|
||||||
ScopeBatchRead = string("batch:read") // 读取批次列表
|
ScopeUserRead = string("user:read") // 读取用户列表
|
||||||
ScopeBatchWrite = string("batch:write") // 写入批次
|
ScopeUserReadOne = string("user:read:one") // 读取单个用户
|
||||||
|
ScopeUserReadNotBind = string("user:read:not_bind") // 读取未绑定管理员的用户列表
|
||||||
|
ScopeUserWrite = string("user:write") // 写入用户
|
||||||
|
ScopeUserWriteBalance = string("user:write:balance") // 写入用户余额
|
||||||
|
ScopeUserWriteBalanceInc = string("user:write:balance:inc") // 增加用户余额
|
||||||
|
ScopeUserWriteBalanceDec = string("user:write:balance:dec") // 减少用户余额
|
||||||
|
ScopeUserWriteBind = string("user:write:bind") // 用户认领
|
||||||
|
|
||||||
ScopeChannel = string("channel") // IP
|
ScopeCoupon = string("coupon") // 优惠券
|
||||||
ScopeChannelRead = string("channel:read") // 读取 IP 列表
|
ScopeCouponRead = string("coupon:read") // 读取优惠券列表
|
||||||
ScopeChannelWrite = string("channel:write") // 写入 IP
|
ScopeCouponWrite = string("coupon:write") // 写入优惠券
|
||||||
|
ScopeCouponWriteAssign = string("coupon:write:assign") // 发放优惠券
|
||||||
|
|
||||||
ScopeTrade = string("trade") // 交易
|
ScopeCouponUser = string("coupon_user") // 用户优惠券
|
||||||
ScopeTradeRead = string("trade:read") // 读取交易列表
|
ScopeCouponUserRead = string("coupon_user:read") // 读取用户优惠券列表
|
||||||
ScopeTradeWrite = string("trade:write") // 写入交易
|
ScopeCouponUserReadOfUser = string("coupon_user:read:of_user") // 读取指定用户的用户优惠券列表
|
||||||
|
ScopeCouponUserWrite = string("coupon_user:write") // 写入用户优惠券
|
||||||
|
|
||||||
ScopeBill = string("bill") // 账单
|
ScopeBatch = string("batch") // 批次
|
||||||
ScopeBillRead = string("bill:read") // 读取账单列表
|
ScopeBatchRead = string("batch:read") // 读取批次列表
|
||||||
ScopeBillWrite = string("bill:write") // 写入账单
|
ScopeBatchReadOfUser = string("batch:read:of_user") // 读取指定用户的批次列表
|
||||||
|
ScopeBatchWrite = string("batch:write") // 写入批次
|
||||||
|
|
||||||
|
ScopeChannel = string("channel") // IP
|
||||||
|
ScopeChannelRead = string("channel:read") // 读取 IP 列表
|
||||||
|
ScopeChannelReadOfUser = string("channel:read:of_user") // 读取指定用户的 IP 列表
|
||||||
|
ScopeChannelWrite = string("channel:write") // 写入 IP
|
||||||
|
ScopeChannelWriteClearExpired = string("channel:write:clear_expired") // 清理过期 IP
|
||||||
|
|
||||||
|
ScopeProxy = string("proxy") // 代理
|
||||||
|
ScopeProxyRead = string("proxy:read") // 读取代理列表
|
||||||
|
ScopeProxyWrite = string("proxy:write") // 写入代理
|
||||||
|
ScopeProxyWriteStatus = string("proxy:write:status") // 更改代理状态
|
||||||
|
|
||||||
|
ScopeTrade = string("trade") // 交易
|
||||||
|
ScopeTradeRead = string("trade:read") // 读取交易列表
|
||||||
|
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表
|
||||||
|
ScopeTradeWrite = string("trade:write") // 写入交易
|
||||||
|
ScopeTradeWriteComplete = string("trade:write:complete") // 完成交易
|
||||||
|
|
||||||
|
ScopeBill = string("bill") // 账单
|
||||||
|
ScopeBillRead = string("bill:read") // 读取账单列表
|
||||||
|
ScopeBillReadOfUser = string("bill:read:of_user") // 读取指定用户的账单列表
|
||||||
|
ScopeBillWrite = string("bill:write") // 写入账单
|
||||||
|
|
||||||
|
ScopeBalanceActivity = string("balance_activity") // 余额变动
|
||||||
|
ScopeBalanceActivityRead = string("balance_activity:read") // 读取余额变动列表
|
||||||
|
ScopeBalanceActivityReadOfUser = string("balance_activity:read:of_user") // 读取指定用户的余额变动列表
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
|
|||||||
slog.Warn("未处理的异常", slog.String("type", t.String()), slog.String("error", err.Error()))
|
slog.Warn("未处理的异常", slog.String("type", t.String()), slog.String("error", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Warn(message)
|
|
||||||
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
|
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
|
||||||
return c.Status(code).SendString(message)
|
return c.Status(code).SendString(message)
|
||||||
}
|
}
|
||||||
|
|||||||
9
web/events/edges.go
Normal file
9
web/events/edges.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import "github.com/hibiken/asynq"
|
||||||
|
|
||||||
|
const RefreshEdge = "edge:refresh"
|
||||||
|
|
||||||
|
func NewRefreshEdge() *asynq.Task {
|
||||||
|
return asynq.NewTask(RefreshEdge, nil)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package globals
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
@@ -17,11 +18,17 @@ import (
|
|||||||
var tp *trace.TracerProvider
|
var tp *trace.TracerProvider
|
||||||
|
|
||||||
func initOtel(ctx context.Context) error {
|
func initOtel(ctx context.Context) error {
|
||||||
|
addr := env.OtelHost + ":" + env.OtelPort
|
||||||
|
name := "lanhu-platform"
|
||||||
|
if env.OtelNameSuffix != "" {
|
||||||
|
name += "-" + env.OtelNameSuffix
|
||||||
|
}
|
||||||
|
slog.Info("初始化 otel", "endpoint", addr, "service_suffix", name)
|
||||||
|
|
||||||
if env.OtelHost == "" || env.OtelPort == "" {
|
if env.OtelHost == "" || env.OtelPort == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := env.OtelHost + ":" + env.OtelPort
|
|
||||||
exporter, err := otlptracegrpc.New(ctx,
|
exporter, err := otlptracegrpc.New(ctx,
|
||||||
otlptracegrpc.WithEndpoint(addr),
|
otlptracegrpc.WithEndpoint(addr),
|
||||||
otlptracegrpc.WithInsecure(),
|
otlptracegrpc.WithInsecure(),
|
||||||
@@ -36,7 +43,7 @@ func initOtel(ctx context.Context) error {
|
|||||||
trace.WithResource(
|
trace.WithResource(
|
||||||
resource.NewWithAttributes(
|
resource.NewWithAttributes(
|
||||||
semconv.SchemaURL,
|
semconv.SchemaURL,
|
||||||
semconv.ServiceNameKey.String("lanhu-platform"),
|
semconv.ServiceNameKey.String(name),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ func PageAdminByAdmin(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var req PageAdminsReq
|
var req core.PageReq
|
||||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, total, err := s.Admin.PageAdmins(req.PageReq)
|
list, total, err := s.Admin.Page(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -33,10 +33,6 @@ func PageAdminByAdmin(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageAdminsReq struct {
|
|
||||||
core.PageReq
|
|
||||||
}
|
|
||||||
|
|
||||||
func AllAdminByAdmin(c *fiber.Ctx) error {
|
func AllAdminByAdmin(c *fiber.Ctx) error {
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRead)
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeAdminRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,7 +58,7 @@ func CreateAdmin(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Admin.CreateAdmin(&req); err != nil {
|
if err := s.Admin.Create(&req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +76,7 @@ func UpdateAdmin(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Admin.UpdateAdmin(&req); err != nil {
|
if err := s.Admin.Update(&req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +94,7 @@ func RemoveAdmin(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Admin.RemoveAdmin(req.Id); err != nil {
|
if err := s.Admin.Remove(req.Id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
189
web/handlers/balance_activity.go
Normal file
189
web/handlers/balance_activity.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"platform/web/auth"
|
||||||
|
"platform/web/core"
|
||||||
|
g "platform/web/globals"
|
||||||
|
q "platform/web/queries"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageBalanceActivity 分页查询当前用户的余额变动记录
|
||||||
|
func PageBalanceActivity(c *fiber.Ctx) error {
|
||||||
|
// 获取当前用户ID
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageBalanceActivityByUserReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造查询条件
|
||||||
|
do := q.BalanceActivity.Where(q.BalanceActivity.UserID.Eq(authCtx.User.ID))
|
||||||
|
if req.BillNo != nil {
|
||||||
|
do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询余额变动列表
|
||||||
|
list, total, err := q.BalanceActivity.
|
||||||
|
Joins(q.BalanceActivity.Bill).
|
||||||
|
Select(
|
||||||
|
q.BalanceActivity.ALL,
|
||||||
|
q.Bill.As("Bill").ID.As("Bill__id"),
|
||||||
|
q.Bill.As("Bill").BillNo.As("Bill__bill_no"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.BalanceActivity.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageBalanceActivityByUserReq struct {
|
||||||
|
core.PageReq
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageBalanceActivityByAdmin 分页查询所有余额变动记录
|
||||||
|
func PageBalanceActivityByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBalanceActivityRead)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageBalanceActivityByAdminReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造查询条件
|
||||||
|
do := q.BalanceActivity.Where()
|
||||||
|
if req.UserPhone != nil {
|
||||||
|
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
|
||||||
|
}
|
||||||
|
if req.BillNo != nil {
|
||||||
|
do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询余额变动列表
|
||||||
|
list, total, err := q.BalanceActivity.
|
||||||
|
Joins(q.BalanceActivity.User, q.BalanceActivity.Admin, q.BalanceActivity.Bill).
|
||||||
|
Select(
|
||||||
|
q.BalanceActivity.ALL,
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
q.Admin.As("Admin").Name.As("Admin__name"),
|
||||||
|
q.Bill.As("Bill").BillNo.As("Bill__bill_no"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.BalanceActivity.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageBalanceActivityByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserPhone *string `json:"user_phone,omitempty"`
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageBalanceActivityOfUserByAdmin 分页查询指定用户的余额变动记录
|
||||||
|
func PageBalanceActivityOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBalanceActivityReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageBalanceActivityOfUserByAdminReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造查询条件
|
||||||
|
do := q.BalanceActivity.Where(q.BalanceActivity.UserID.Eq(req.UserID))
|
||||||
|
if req.BillNo != nil {
|
||||||
|
do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.BalanceActivity.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询余额变动列表
|
||||||
|
list, total, err := q.BalanceActivity.
|
||||||
|
Joins(q.BalanceActivity.Admin, q.BalanceActivity.Bill).
|
||||||
|
Select(
|
||||||
|
q.BalanceActivity.ALL,
|
||||||
|
q.Admin.As("Admin").Name.As("Admin__name"),
|
||||||
|
q.Bill.As("Bill").BillNo.As("Bill__bill_no"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.BalanceActivity.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageBalanceActivityOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"platform/pkg/u"
|
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
c "platform/web/core"
|
c "platform/web/core"
|
||||||
@@ -29,13 +28,18 @@ func PageBatch(ctx *fiber.Ctx) error {
|
|||||||
// 查询批次
|
// 查询批次
|
||||||
conds := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(authCtx.User.ID))
|
conds := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(authCtx.User.ID))
|
||||||
if req.TimeStart != nil {
|
if req.TimeStart != nil {
|
||||||
conds.Where(q.LogsUserUsage.Time.Gte(*req.TimeStart))
|
conds.Where(q.LogsUserUsage.Time.Gte(req.TimeStart.UTC()))
|
||||||
}
|
}
|
||||||
if req.TimeEnd != nil {
|
if req.TimeEnd != nil {
|
||||||
conds.Where(q.LogsUserUsage.Time.Lte(*req.TimeEnd))
|
conds.Where(q.LogsUserUsage.Time.Lte(req.TimeEnd.UTC()))
|
||||||
|
}
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
conds.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
|
||||||
}
|
}
|
||||||
|
|
||||||
list, total, err := q.LogsUserUsage.Where(conds).
|
list, total, err := q.LogsUserUsage.
|
||||||
|
Joins(q.LogsUserUsage.Resource).
|
||||||
|
Where(conds).
|
||||||
Order(q.LogsUserUsage.Time.Desc()).
|
Order(q.LogsUserUsage.Time.Desc()).
|
||||||
FindByPage(req.GetOffset(), req.GetLimit())
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -53,8 +57,9 @@ func PageBatch(ctx *fiber.Ctx) error {
|
|||||||
|
|
||||||
type PageResourceBatchReq struct {
|
type PageResourceBatchReq struct {
|
||||||
c.PageReq
|
c.PageReq
|
||||||
TimeStart *time.Time `json:"time_start"`
|
ResourceNo *string `json:"resource_no"`
|
||||||
TimeEnd *time.Time `json:"time_end"`
|
TimeStart *time.Time `json:"time_start"`
|
||||||
|
TimeEnd *time.Time `json:"time_end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageBatchByAdmin 分页查询所有提取记录
|
// PageBatchByAdmin 分页查询所有提取记录
|
||||||
@@ -71,10 +76,10 @@ func PageBatchByAdmin(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
do := q.LogsUserUsage.Where()
|
do := q.LogsUserUsage.Where()
|
||||||
if req.UserPhone != nil {
|
if req.UserPhone != nil {
|
||||||
do = do.Where(q.User.Phone.Eq(*req.UserPhone))
|
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
|
||||||
}
|
}
|
||||||
if req.ResourceNo != nil {
|
if req.ResourceNo != nil {
|
||||||
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
|
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
|
||||||
}
|
}
|
||||||
if req.BatchNo != nil {
|
if req.BatchNo != nil {
|
||||||
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
|
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
|
||||||
@@ -89,12 +94,10 @@ func PageBatchByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
|
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
|
||||||
}
|
}
|
||||||
if req.CreatedAtStart != nil {
|
if req.CreatedAtStart != nil {
|
||||||
time := u.DateHead(*req.CreatedAtStart)
|
do = do.Where(q.LogsUserUsage.Time.Gte(req.CreatedAtStart.UTC()))
|
||||||
do = do.Where(q.LogsUserUsage.Time.Gte(time))
|
|
||||||
}
|
}
|
||||||
if req.CreatedAtEnd != nil {
|
if req.CreatedAtEnd != nil {
|
||||||
time := u.DateTail(*req.CreatedAtEnd)
|
do = do.Where(q.LogsUserUsage.Time.Lte(req.CreatedAtEnd.UTC()))
|
||||||
do = do.Where(q.LogsUserUsage.Time.Lte(time))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list, total, err := q.LogsUserUsage.
|
list, total, err := q.LogsUserUsage.
|
||||||
@@ -104,6 +107,7 @@ func PageBatchByAdmin(c *fiber.Ctx) error {
|
|||||||
q.User.As("User").Phone.As("User__phone"),
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
q.User.As("User").Name.As("User__name"),
|
q.User.As("User").Name.As("User__name"),
|
||||||
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
||||||
|
q.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
).
|
).
|
||||||
Where(do).
|
Where(do).
|
||||||
Order(q.LogsUserUsage.Time.Desc()).
|
Order(q.LogsUserUsage.Time.Desc()).
|
||||||
@@ -128,3 +132,74 @@ type PageBatchByAdminReq struct {
|
|||||||
CreatedAtStart *time.Time `json:"created_at_start"`
|
CreatedAtStart *time.Time `json:"created_at_start"`
|
||||||
CreatedAtEnd *time.Time `json:"created_at_end"`
|
CreatedAtEnd *time.Time `json:"created_at_end"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageBatchOfUserByAdmin 分页查询指定用户的提取记录
|
||||||
|
func PageBatchOfUserByAdmin(ctx *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(ctx).PermitAdmin(core.ScopeBatchReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageBatchOfUserByAdminReq
|
||||||
|
if err = g.Validator.ParseBody(ctx, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
do := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(req.UserID))
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
|
||||||
|
}
|
||||||
|
if req.BatchNo != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
|
||||||
|
}
|
||||||
|
if req.Prov != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.Prov.Eq(*req.Prov))
|
||||||
|
}
|
||||||
|
if req.City != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.City.Eq(*req.City))
|
||||||
|
}
|
||||||
|
if req.Isp != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.Time.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.LogsUserUsage.Time.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
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"),
|
||||||
|
q.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.LogsUserUsage.Time.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return ctx.JSON(core.NewBizErr("获取数据失败", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(c.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageBatchOfUserByAdminReq struct {
|
||||||
|
c.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
ResourceNo *string `json:"resource_no"`
|
||||||
|
BatchNo *string `json:"batch_no"`
|
||||||
|
Prov *string `json:"prov"`
|
||||||
|
City *string `json:"city"`
|
||||||
|
Isp *string `json:"isp"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"platform/pkg/u"
|
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
@@ -40,12 +39,10 @@ func PageBillByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
||||||
}
|
}
|
||||||
if req.CreatedAtStart != nil {
|
if req.CreatedAtStart != nil {
|
||||||
time := u.DateHead(*req.CreatedAtStart)
|
do = do.Where(q.Bill.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
do = do.Where(q.Bill.CreatedAt.Gte(time))
|
|
||||||
}
|
}
|
||||||
if req.CreatedAtEnd != nil {
|
if req.CreatedAtEnd != nil {
|
||||||
time := u.DateHead(*req.CreatedAtEnd)
|
do = do.Where(q.Bill.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
do = do.Where(q.Bill.CreatedAt.Lte(time))
|
|
||||||
}
|
}
|
||||||
if req.ProductCode != nil {
|
if req.ProductCode != nil {
|
||||||
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
|
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
|
||||||
@@ -72,6 +69,7 @@ func PageBillByAdmin(c *fiber.Ctx) error {
|
|||||||
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
|
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
|
||||||
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
|
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
|
||||||
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
||||||
|
q.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
).
|
).
|
||||||
Where(do).
|
Where(do).
|
||||||
Order(q.Bill.CreatedAt.Desc()).
|
Order(q.Bill.CreatedAt.Desc()).
|
||||||
@@ -101,6 +99,89 @@ type PageBillByAdminReq struct {
|
|||||||
SkuCode *string `json:"sku_code,omitempty"`
|
SkuCode *string `json:"sku_code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageBillOfUserByAdmin 分页查询指定用户账单
|
||||||
|
func PageBillOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBillReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageBillOfUserByAdminReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造查询条件
|
||||||
|
do := q.Bill.Where(q.Bill.UserID.Eq(req.UserID))
|
||||||
|
if req.TradeInnerNo != nil {
|
||||||
|
do = do.Where(q.Trade.As("Trade").InnerNo.Eq(*req.TradeInnerNo))
|
||||||
|
}
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
|
||||||
|
}
|
||||||
|
if req.BillNo != nil {
|
||||||
|
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.Bill.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.Bill.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
if req.ProductCode != nil {
|
||||||
|
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
|
||||||
|
}
|
||||||
|
if req.SkuCode != nil {
|
||||||
|
do = do.Where(q.Bill.
|
||||||
|
Where(q.ResourceShort.As("Resource__Short").Code.Eq(*req.SkuCode)).
|
||||||
|
Or(q.ResourceLong.As("Resource__Long").Code.Eq(*req.SkuCode)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询账单列表
|
||||||
|
list, total, err := q.Bill.
|
||||||
|
Joins(
|
||||||
|
q.Bill.Resource,
|
||||||
|
q.Bill.Trade,
|
||||||
|
q.Bill.Resource.Short,
|
||||||
|
q.Bill.Resource.Long,
|
||||||
|
).
|
||||||
|
Select(
|
||||||
|
q.Bill.ALL,
|
||||||
|
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
|
||||||
|
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
|
||||||
|
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
||||||
|
q.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.Bill.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageBillOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
TradeInnerNo *string `json:"trade_inner_no,omitempty"`
|
||||||
|
ResourceNo *string `json:"resource_no,omitempty"`
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
ProductCode *string `json:"product_code,omitempty"`
|
||||||
|
SkuCode *string `json:"sku_code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListBill 获取账单列表
|
// ListBill 获取账单列表
|
||||||
func ListBill(c *fiber.Ctx) error {
|
func ListBill(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -123,10 +204,10 @@ func ListBill(c *fiber.Ctx) error {
|
|||||||
do.Where(q.Bill.Type.Eq(int(*req.Type)))
|
do.Where(q.Bill.Type.Eq(int(*req.Type)))
|
||||||
}
|
}
|
||||||
if req.CreateAfter != nil {
|
if req.CreateAfter != nil {
|
||||||
do.Where(q.Bill.CreatedAt.Gte(*req.CreateAfter))
|
do = do.Where(q.Bill.CreatedAt.Gte(req.CreateAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.CreateBefore != nil {
|
if req.CreateBefore != nil {
|
||||||
do.Where(q.Bill.CreatedAt.Lte(*req.CreateBefore))
|
do = do.Where(q.Bill.CreatedAt.Lte(req.CreateBefore.UTC()))
|
||||||
}
|
}
|
||||||
if req.BillNo != nil && *req.BillNo != "" {
|
if req.BillNo != nil && *req.BillNo != "" {
|
||||||
do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
do.Where(q.Bill.BillNo.Eq(*req.BillNo))
|
||||||
|
|||||||
@@ -15,90 +15,6 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PageChannelByAdmin 分页查询所有通道
|
|
||||||
func PageChannelByAdmin(c *fiber.Ctx) error {
|
|
||||||
// 检查权限
|
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelRead)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析请求参数
|
|
||||||
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(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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 分页查询当前用户通道
|
// ListChannel 分页查询当前用户通道
|
||||||
func ListChannel(c *fiber.Ctx) error {
|
func ListChannel(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -126,10 +42,10 @@ func ListChannel(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if req.ExpireAfter != nil {
|
if req.ExpireAfter != nil {
|
||||||
cond.Where(q.Channel.ExpiredAt.Gte(*req.ExpireAfter))
|
cond = cond.Where(q.Channel.ExpiredAt.Gte(req.ExpireAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
cond.Where(q.Channel.ExpiredAt.Lte(*req.ExpireBefore))
|
cond = cond.Where(q.Channel.ExpiredAt.Lte(req.ExpireBefore.UTC()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询数据
|
// 查询数据
|
||||||
@@ -172,12 +88,7 @@ type ListChannelsReq struct {
|
|||||||
|
|
||||||
// CreateChannel 创建新通道
|
// CreateChannel 创建新通道
|
||||||
func CreateChannel(c *fiber.Ctx) error {
|
func CreateChannel(c *fiber.Ctx) error {
|
||||||
|
// 不检查权限,允许 api 调用
|
||||||
// 检查权限
|
|
||||||
_, err := auth.GetAuthCtx(c).PermitUser()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析参数
|
// 解析参数
|
||||||
req := new(CreateChannelReq)
|
req := new(CreateChannelReq)
|
||||||
@@ -191,17 +102,21 @@ func CreateChannel(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建通道
|
// 创建通道
|
||||||
|
no, err := s.FindResourceNoById(req.ResourceId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var isp *m.EdgeISP
|
var isp *m.EdgeISP
|
||||||
if req.Isp != nil {
|
if req.Isp != nil {
|
||||||
isp = u.X(m.ToEdgeISP(*req.Isp))
|
isp = u.X(m.ToEdgeISP(*req.Isp))
|
||||||
}
|
}
|
||||||
result, err := s.Channel.CreateChannels(
|
result, err := s.Channel.CreateChannels(
|
||||||
ip,
|
ip, no,
|
||||||
req.ResourceId,
|
|
||||||
req.AuthType == s.ChannelAuthTypeIp,
|
req.AuthType == s.ChannelAuthTypeIp,
|
||||||
req.AuthType == s.ChannelAuthTypePass,
|
req.AuthType == s.ChannelAuthTypePass,
|
||||||
req.Count,
|
req.Count,
|
||||||
s.EdgeFilter{
|
&s.EdgeFilter{
|
||||||
Isp: isp,
|
Isp: isp,
|
||||||
Prov: req.Prov,
|
Prov: req.Prov,
|
||||||
City: req.City,
|
City: req.City,
|
||||||
@@ -217,6 +132,7 @@ func CreateChannel(c *fiber.Ctx) error {
|
|||||||
resp[i] = &CreateChannelRespItem{
|
resp[i] = &CreateChannelRespItem{
|
||||||
Proto: req.Protocol,
|
Proto: req.Protocol,
|
||||||
Host: channel.Host,
|
Host: channel.Host,
|
||||||
|
IP: channel.Proxy.IP.String(),
|
||||||
Port: channel.Port,
|
Port: channel.Port,
|
||||||
}
|
}
|
||||||
if req.AuthType == s.ChannelAuthTypePass {
|
if req.AuthType == s.ChannelAuthTypePass {
|
||||||
@@ -237,9 +153,73 @@ type CreateChannelReq struct {
|
|||||||
Isp *int `json:"isp"`
|
Isp *int `json:"isp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChannelV2 创建新通道 v2,使用 resource_no 替代 resource_id
|
||||||
|
func CreateChannelV2(c *fiber.Ctx) error {
|
||||||
|
// 不检查权限,允许 api 调用
|
||||||
|
|
||||||
|
// 解析参数
|
||||||
|
req := new(CreateChannelReqV2)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return core.NewBizErr("解析参数失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := netip.ParseAddr(c.IP())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取客户端地址失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建通道
|
||||||
|
var isp *m.EdgeISP
|
||||||
|
if req.Isp != nil {
|
||||||
|
isp = u.X(m.ToEdgeISP(*req.Isp))
|
||||||
|
}
|
||||||
|
result, err := s.Channel.CreateChannels(
|
||||||
|
ip,
|
||||||
|
req.ResourceNo,
|
||||||
|
req.AuthType == s.ChannelAuthTypeIp,
|
||||||
|
req.AuthType == s.ChannelAuthTypePass,
|
||||||
|
req.Count,
|
||||||
|
&s.EdgeFilter{
|
||||||
|
Isp: isp,
|
||||||
|
Prov: req.Prov,
|
||||||
|
City: req.City,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
var resp = make([]*CreateChannelRespItem, len(result))
|
||||||
|
for i, channel := range result {
|
||||||
|
resp[i] = &CreateChannelRespItem{
|
||||||
|
Proto: req.Protocol,
|
||||||
|
Host: channel.Host,
|
||||||
|
IP: channel.Proxy.IP.String(),
|
||||||
|
Port: channel.Port,
|
||||||
|
}
|
||||||
|
if req.AuthType == s.ChannelAuthTypePass {
|
||||||
|
resp[i].Username = channel.Username
|
||||||
|
resp[i].Password = channel.Password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.JSON(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateChannelReqV2 struct {
|
||||||
|
ResourceNo string `json:"resource_no" 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 {
|
type CreateChannelRespItem struct {
|
||||||
Proto int `json:"-"`
|
Proto int `json:"-"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
|
IP string `json:"ip"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
Username *string `json:"username,omitempty"`
|
Username *string `json:"username,omitempty"`
|
||||||
Password *string `json:"password,omitempty"`
|
Password *string `json:"password,omitempty"`
|
||||||
@@ -271,3 +251,195 @@ func RemoveChannels(c *fiber.Ctx) error {
|
|||||||
type RemoveChannelsReq struct {
|
type RemoveChannelsReq struct {
|
||||||
Batch string `json:"batch" validate:"required"`
|
Batch string `json:"batch" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageChannelByAdmin 分页查询所有通道
|
||||||
|
func PageChannelByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelRead)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
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 {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Gte(req.ExpiredAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.ExpiredAtEnd != nil {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Lte(req.ExpiredAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
if req.Expired != nil {
|
||||||
|
if *req.Expired {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Lte(time.Now().UTC()))
|
||||||
|
} else {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Gt(time.Now().UTC()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询通道列表
|
||||||
|
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.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.Channel.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type 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"`
|
||||||
|
Expired *bool `json:"expired"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageChannelOfUserByAdmin 分页查询指定用户的通道
|
||||||
|
func PageChannelOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
var req PageChannelOfUserByAdminReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
|
do := q.Channel.Where(q.Channel.UserID.Eq(req.UserID))
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
|
||||||
|
}
|
||||||
|
if req.BatchNo != nil {
|
||||||
|
do = do.Where(q.Channel.BatchNo.Eq(*req.BatchNo))
|
||||||
|
}
|
||||||
|
if req.ProxyHost != nil {
|
||||||
|
do = do.Where(q.Channel.Host.Eq(*req.ProxyHost))
|
||||||
|
}
|
||||||
|
if req.ProxyPort != nil {
|
||||||
|
do = do.Where(q.Channel.Port.Eq(*req.ProxyPort))
|
||||||
|
}
|
||||||
|
if req.ExpiredAtStart != nil {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Gte(req.ExpiredAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.ExpiredAtEnd != nil {
|
||||||
|
do = do.Where(q.Channel.ExpiredAt.Lte(req.ExpiredAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询通道列表
|
||||||
|
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.Resource.As("Resource").Type.As("Resource__type"),
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.Channel.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageChannelOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
ResourceNo *string `json:"resource_no"`
|
||||||
|
BatchNo *string `json:"batch_no"`
|
||||||
|
ProxyHost *string `json:"proxy_host"`
|
||||||
|
ProxyPort *uint16 `json:"proxy_port"`
|
||||||
|
ExpiredAtStart *time.Time `json:"expired_at_start"`
|
||||||
|
ExpiredAtEnd *time.Time `json:"expired_at_end"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncChannelClearExpiredByAdmin 清理过期通道
|
||||||
|
func SyncChannelClearExpiredByAdmin(c *fiber.Ctx) error {
|
||||||
|
if _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelWriteClearExpired); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req SyncChannelClearExpiredByAdminReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.Channel.ClearExpiredChannels(req.ProxyID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(SyncChannelClearExpiredByAdminResp{
|
||||||
|
Count: count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncChannelClearExpiredByAdminReq struct {
|
||||||
|
ProxyID int32 `json:"proxy_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncChannelClearExpiredByAdminResp struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -103,3 +103,27 @@ func DeleteCoupon(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AssignCoupon(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponWriteAssign)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req AssignCouponReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Coupon.Assign(req.CouponID, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssignCouponReq struct {
|
||||||
|
CouponID int32 `json:"coupon_id" validate:"required"`
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|||||||
329
web/handlers/coupon_user.go
Normal file
329
web/handlers/coupon_user.go
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"platform/web/auth"
|
||||||
|
"platform/web/core"
|
||||||
|
g "platform/web/globals"
|
||||||
|
m "platform/web/models"
|
||||||
|
q "platform/web/queries"
|
||||||
|
s "platform/web/services"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"gorm.io/gen"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageCouponUser 分页查询当前用户已发放优惠券
|
||||||
|
func PageCouponUser(c *fiber.Ctx) error {
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageCouponUserReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||||
|
conds = append(conds, q.CouponUser.UserID.Eq(authCtx.User.ID))
|
||||||
|
|
||||||
|
list, total, err := q.CouponUser.
|
||||||
|
Joins(q.CouponUser.Coupon).
|
||||||
|
Select(couponUserSelect(false)...).
|
||||||
|
Where(conds...).
|
||||||
|
Order(q.CouponUser.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageCouponUserReq struct {
|
||||||
|
core.PageReq
|
||||||
|
CouponUserPageFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCouponUser 获取当前用户已发放优惠券详情
|
||||||
|
func GetCouponUser(c *fiber.Ctx) error {
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req core.IdReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := q.CouponUser.
|
||||||
|
Joins(q.CouponUser.Coupon).
|
||||||
|
Select(couponUserSelect(false)...).
|
||||||
|
Where(
|
||||||
|
q.CouponUser.ID.Eq(req.Id),
|
||||||
|
q.CouponUser.UserID.Eq(authCtx.User.ID),
|
||||||
|
).
|
||||||
|
Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("已发放优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageCouponUserByAdmin 分页查询全部已发放优惠券
|
||||||
|
func PageCouponUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserRead)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageCouponUserByAdminReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||||
|
if req.UserID != nil {
|
||||||
|
conds = append(conds, q.CouponUser.UserID.Eq(*req.UserID))
|
||||||
|
}
|
||||||
|
if req.UserPhone != nil {
|
||||||
|
conds = append(conds, q.User.As("User").Phone.Eq(*req.UserPhone))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.CouponUser.
|
||||||
|
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||||
|
Select(couponUserSelect(true)...).
|
||||||
|
Where(conds...).
|
||||||
|
Order(q.CouponUser.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageCouponUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
CouponUserPageFilter
|
||||||
|
UserID *int32 `json:"user_id,omitempty"`
|
||||||
|
UserPhone *string `json:"user_phone,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageCouponUserOfUserByAdmin 分页查询指定用户已发放优惠券
|
||||||
|
func PageCouponUserOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageCouponUserOfUserByAdminReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conds := couponUserPageConditions(req.CouponUserPageFilter)
|
||||||
|
conds = append(conds, q.CouponUser.UserID.Eq(req.UserID))
|
||||||
|
|
||||||
|
list, total, err := q.CouponUser.
|
||||||
|
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||||
|
Select(couponUserSelect(true)...).
|
||||||
|
Where(conds...).
|
||||||
|
Order(q.CouponUser.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageCouponUserOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
CouponUserPageFilter
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCouponUserByAdmin 获取已发放优惠券详情
|
||||||
|
func GetCouponUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserRead)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req core.IdReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := q.CouponUser.
|
||||||
|
Joins(q.CouponUser.Coupon, q.CouponUser.User).
|
||||||
|
Select(couponUserSelect(true)...).
|
||||||
|
Where(q.CouponUser.ID.Eq(req.Id)).
|
||||||
|
Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("已发放优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("获取数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateCouponUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req s.CreateCouponUserData
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.CouponUser.Create(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCouponUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req s.UpdateCouponUserData
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.CouponUser.Update(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteCouponUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeCouponUserWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req core.IdReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.CouponUser.Delete(req.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CouponUserPageFilter struct {
|
||||||
|
CouponID *int32 `json:"coupon_id,omitempty"`
|
||||||
|
CouponName *string `json:"coupon_name,omitempty"`
|
||||||
|
Status *m.CouponUserStatus `json:"status,omitempty"`
|
||||||
|
Expired *bool `json:"expired,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
ExpireAtStart *time.Time `json:"expire_at_start,omitempty"`
|
||||||
|
ExpireAtEnd *time.Time `json:"expire_at_end,omitempty"`
|
||||||
|
UsedAtStart *time.Time `json:"used_at_start,omitempty"`
|
||||||
|
UsedAtEnd *time.Time `json:"used_at_end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func couponUserPageConditions(req CouponUserPageFilter) []gen.Condition {
|
||||||
|
conds := make([]gen.Condition, 0)
|
||||||
|
if req.CouponID != nil {
|
||||||
|
conds = append(conds, q.CouponUser.CouponID.Eq(*req.CouponID))
|
||||||
|
}
|
||||||
|
if req.CouponName != nil {
|
||||||
|
conds = append(conds, q.Coupon.As("Coupon").Name.Like("%"+*req.CouponName+"%"))
|
||||||
|
}
|
||||||
|
if req.Status != nil {
|
||||||
|
conds = append(conds, q.CouponUser.Status.Eq(int(*req.Status)))
|
||||||
|
}
|
||||||
|
if req.Expired != nil {
|
||||||
|
if *req.Expired {
|
||||||
|
conds = append(conds, q.CouponUser.ExpireAt.IsNotNull(), q.CouponUser.ExpireAt.Lte(time.Now().UTC()))
|
||||||
|
} else {
|
||||||
|
conds = append(conds, q.CouponUser.Where(q.CouponUser.ExpireAt.IsNull()).Or(q.CouponUser.ExpireAt.Gt(time.Now().UTC())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
conds = append(conds, q.CouponUser.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
conds = append(conds, q.CouponUser.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
if req.ExpireAtStart != nil {
|
||||||
|
conds = append(conds, q.CouponUser.ExpireAt.Gte(req.ExpireAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.ExpireAtEnd != nil {
|
||||||
|
conds = append(conds, q.CouponUser.ExpireAt.Lte(req.ExpireAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
if req.UsedAtStart != nil {
|
||||||
|
conds = append(conds, q.CouponUser.UsedAt.Gte(req.UsedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.UsedAtEnd != nil {
|
||||||
|
conds = append(conds, q.CouponUser.UsedAt.Lte(req.UsedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
return conds
|
||||||
|
}
|
||||||
|
|
||||||
|
func couponUserSelect(includeUser bool) []field.Expr {
|
||||||
|
cols := []field.Expr{
|
||||||
|
q.CouponUser.ALL,
|
||||||
|
q.Coupon.As("Coupon").ID.As("Coupon__id"),
|
||||||
|
q.Coupon.As("Coupon").Name.As("Coupon__name"),
|
||||||
|
q.Coupon.As("Coupon").Amount.As("Coupon__amount"),
|
||||||
|
q.Coupon.As("Coupon").MinAmount.As("Coupon__min_amount"),
|
||||||
|
q.Coupon.As("Coupon").Count_.As("Coupon__count"),
|
||||||
|
q.Coupon.As("Coupon").Status.As("Coupon__status"),
|
||||||
|
q.Coupon.As("Coupon").ExpireType.As("Coupon__expire_type"),
|
||||||
|
q.Coupon.As("Coupon").ExpireAt.As("Coupon__expire_at"),
|
||||||
|
q.Coupon.As("Coupon").ExpireIn.As("Coupon__expire_in"),
|
||||||
|
q.Coupon.As("Coupon").CreatedAt.As("Coupon__created_at"),
|
||||||
|
q.Coupon.As("Coupon").UpdatedAt.As("Coupon__updated_at"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if includeUser {
|
||||||
|
cols = append(cols,
|
||||||
|
q.User.As("User").ID.As("User__id"),
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cols
|
||||||
|
}
|
||||||
@@ -142,7 +142,7 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户实名认证状态
|
// 更新用户实名认证状态
|
||||||
_, err = q.User.
|
r, err := q.User.
|
||||||
Where(q.User.ID.Eq(info.Uid)).
|
Where(q.User.ID.Eq(info.Uid)).
|
||||||
UpdateSimple(
|
UpdateSimple(
|
||||||
q.User.IDType.Value(info.Type),
|
q.User.IDType.Value(info.Type),
|
||||||
@@ -153,6 +153,9 @@ func IdentifyCallbackNew(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return renderIdenResult(c, false, "保存实名认证信息失败,请联系客服处理")
|
return renderIdenResult(c, false, "保存实名认证信息失败,请联系客服处理")
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return renderIdenResult(c, false, "用户状态已失效")
|
||||||
|
}
|
||||||
|
|
||||||
// 返回结果页面
|
// 返回结果页面
|
||||||
return renderIdenResult(c, true, "实名认证成功,请在扫码页面点击按钮完成认证")
|
return renderIdenResult(c, true, "实名认证成功,请在扫码页面点击按钮完成认证")
|
||||||
@@ -172,7 +175,7 @@ func DebugIdentifyClear(c *fiber.Ctx) error {
|
|||||||
return core.NewServErr("需要提供手机号")
|
return core.NewServErr("需要提供手机号")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := q.User.
|
r, err := q.User.
|
||||||
Where(
|
Where(
|
||||||
q.User.Phone.Eq(phone),
|
q.User.Phone.Eq(phone),
|
||||||
).
|
).
|
||||||
@@ -184,6 +187,9 @@ func DebugIdentifyClear(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("清除实名认证失败")
|
return core.NewServErr("清除实名认证失败")
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("用户状态已失效")
|
||||||
|
}
|
||||||
|
|
||||||
return c.SendString("实名信息已清除")
|
return c.SendString("实名信息已清除")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,15 @@ func AllProductByAdmin(c *fiber.Ctx) error {
|
|||||||
type AllProductsByAdminReq struct {
|
type AllProductsByAdminReq struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AllProduct(c *fiber.Ctx) error {
|
||||||
|
infos, err := s.Product.AllProductSaleInfos()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(infos)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateProduct(c *fiber.Ctx) error {
|
func CreateProduct(c *fiber.Ctx) error {
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -181,6 +190,32 @@ func UpdateProductSku(c *fiber.Ctx) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateProductStatusSku(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
}
|
||||||
|
var req Params
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.ProductSku.Update(s.UpdateProductSkuData{
|
||||||
|
ID: req.ID,
|
||||||
|
Status: &req.Status,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func BatchUpdateProductSkuDiscount(c *fiber.Ctx) error {
|
func BatchUpdateProductSkuDiscount(c *fiber.Ctx) error {
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,63 +1,127 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
|
||||||
"platform/pkg/env"
|
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
"platform/web/globals"
|
g "platform/web/globals"
|
||||||
s "platform/web/services"
|
s "platform/web/services"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DebugRegisterProxyBaiYin(c *fiber.Ctx) error {
|
func PageProxyByAdmin(c *fiber.Ctx) error {
|
||||||
if env.RunMode != env.RunModeDev {
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyRead)
|
||||||
return fiber.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Proxy.RegisterBaiyin("1a:2b:3c:4d:5e:6f", netip.AddrFrom4([4]byte{127, 0, 0, 1}), "test", "test")
|
|
||||||
if err != nil {
|
|
||||||
return core.NewServErr("注册失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册白银代理网关
|
|
||||||
func ProxyRegisterBaiYin(c *fiber.Ctx) error {
|
|
||||||
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := new(RegisterProxyBaiyinReq)
|
var req core.PageReq
|
||||||
err = globals.Validator.ParseBody(c, req)
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := s.Proxy.Page(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := netip.ParseAddr(req.IP)
|
return c.JSON(core.PageResp{
|
||||||
if err != nil {
|
List: list,
|
||||||
return core.NewServErr("IP地址格式错误", err)
|
Total: int(total),
|
||||||
}
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
err = s.Proxy.RegisterBaiyin(req.Name, addr, req.Username, req.Password)
|
})
|
||||||
if err != nil {
|
|
||||||
return core.NewServErr("注册失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterProxyBaiyinReq struct {
|
func AllProxyByAdmin(c *fiber.Ctx) error {
|
||||||
Name string `json:"name" validate:"required"`
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyRead)
|
||||||
IP string `json:"ip" validate:"required"`
|
if err != nil {
|
||||||
Username string `json:"username" validate:"required"`
|
return err
|
||||||
Password string `json:"password" validate:"required"`
|
}
|
||||||
|
|
||||||
|
list, err := s.Proxy.All()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateProxy(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req s.CreateProxy
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Proxy.Create(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateProxy(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req s.UpdateProxy
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Proxy.Update(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateProxyStatus(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyWriteStatus)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req s.UpdateProxyStatus
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Proxy.UpdateStatus(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveProxy(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProxyWrite)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req core.IdReq
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Proxy.Remove(req.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
|
||||||
// region 报告上线
|
// region 报告上线
|
||||||
func ProxyReportOnline(c *fiber.Ctx) (err error) {
|
func ProxyReportOnline(c *fiber.Ctx) (err error) {
|
||||||
return c.JSON(map[string]any{
|
return c.JSON(map[string]any{
|
||||||
|
|||||||
@@ -44,26 +44,26 @@ func PageResourceShort(c *fiber.Ctx) error {
|
|||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(*req.Type))
|
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).Type.Eq(*req.Type))
|
||||||
}
|
}
|
||||||
if req.CreateAfter != nil {
|
if req.CreateAfter != nil {
|
||||||
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreateAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.CreateBefore != nil {
|
if req.CreateBefore != nil {
|
||||||
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreateBefore.UTC()))
|
||||||
}
|
}
|
||||||
if req.ExpireAfter != nil {
|
if req.ExpireAfter != nil {
|
||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Gte(*req.ExpireAfter))
|
do = do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Gte(req.ExpireAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
do = do.Where(q.ResourceShort.As(q.Resource.Short.Name()).ExpireAt.Lte(req.ExpireBefore.UTC()))
|
||||||
}
|
}
|
||||||
if req.Status != nil {
|
if req.Status != nil {
|
||||||
var short = q.ResourceShort.As(q.Resource.Short.Name())
|
var short = q.ResourceShort.As(q.Resource.Short.Name())
|
||||||
switch *req.Status {
|
switch *req.Status {
|
||||||
case 1:
|
case 1:
|
||||||
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Gte(time.Now()))
|
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Gte(time.Now().UTC()))
|
||||||
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.GtCol(short.Used))
|
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.GtCol(short.Used))
|
||||||
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
||||||
case 2:
|
case 2:
|
||||||
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Lte(time.Now()))
|
var timeCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeTime)), short.ExpireAt.Lte(time.Now().UTC()))
|
||||||
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.LteCol(short.Used))
|
var quotaCond = q.Resource.Where(short.Type.Eq(int(m.ResourceModeQuota)), short.Quota.LteCol(short.Used))
|
||||||
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
||||||
}
|
}
|
||||||
@@ -84,6 +84,7 @@ func PageResourceShort(c *fiber.Ctx) error {
|
|||||||
total = int64(len(resource) + req.GetOffset())
|
total = int64(len(resource) + req.GetOffset())
|
||||||
} else {
|
} else {
|
||||||
total, err = q.Resource.
|
total, err = q.Resource.
|
||||||
|
Joins(q.Resource.Short).
|
||||||
Where(do).
|
Where(do).
|
||||||
Count()
|
Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -140,26 +141,26 @@ func PageResourceLong(c *fiber.Ctx) error {
|
|||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int(*req.Type)))
|
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).Type.Eq(int(*req.Type)))
|
||||||
}
|
}
|
||||||
if req.CreateAfter != nil {
|
if req.CreateAfter != nil {
|
||||||
do.Where(q.Resource.CreatedAt.Gte(*req.CreateAfter))
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreateAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.CreateBefore != nil {
|
if req.CreateBefore != nil {
|
||||||
do.Where(q.Resource.CreatedAt.Lte(*req.CreateBefore))
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreateBefore.UTC()))
|
||||||
}
|
}
|
||||||
if req.ExpireAfter != nil {
|
if req.ExpireAfter != nil {
|
||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Gte(*req.ExpireAfter))
|
do = do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Gte(req.ExpireAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.ExpireBefore != nil {
|
if req.ExpireBefore != nil {
|
||||||
do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(*req.ExpireBefore))
|
do = do.Where(q.ResourceLong.As(q.Resource.Long.Name()).ExpireAt.Lte(req.ExpireBefore.UTC()))
|
||||||
}
|
}
|
||||||
if req.Status != nil {
|
if req.Status != nil {
|
||||||
var long = q.ResourceLong.As(q.Resource.Long.Name())
|
var long = q.ResourceLong.As(q.Resource.Long.Name())
|
||||||
switch *req.Status {
|
switch *req.Status {
|
||||||
case 1:
|
case 1:
|
||||||
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Gte(time.Now()))
|
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Gte(time.Now().UTC()))
|
||||||
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.GtCol(long.Used))
|
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.GtCol(long.Used))
|
||||||
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
||||||
case 2:
|
case 2:
|
||||||
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Lte(time.Now()))
|
var timeCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeTime)), long.ExpireAt.Lte(time.Now().UTC()))
|
||||||
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.LteCol(long.Used))
|
var quotaCond = q.Resource.Where(long.Type.Eq(int(m.ResourceModeQuota)), long.Quota.LteCol(long.Used))
|
||||||
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
do.Where(q.Resource.Where(timeCond).Or(quotaCond))
|
||||||
}
|
}
|
||||||
@@ -180,6 +181,7 @@ func PageResourceLong(c *fiber.Ctx) error {
|
|||||||
total = int64(len(resource) + req.GetOffset())
|
total = int64(len(resource) + req.GetOffset())
|
||||||
} else {
|
} else {
|
||||||
total, err = q.Resource.
|
total, err = q.Resource.
|
||||||
|
Joins(q.Resource.Long).
|
||||||
Where(do).
|
Where(do).
|
||||||
Count()
|
Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -209,7 +211,7 @@ type PageResourceLongReq struct {
|
|||||||
|
|
||||||
// PageResourceShortByAdmin 分页查询全部短效套餐
|
// PageResourceShortByAdmin 分页查询全部短效套餐
|
||||||
func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceRead)
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -233,18 +235,16 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
|
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
|
||||||
}
|
}
|
||||||
if req.CreatedAtStart != nil {
|
if req.CreatedAtStart != nil {
|
||||||
time := u.DateHead(*req.CreatedAtStart)
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
do = do.Where(q.Resource.CreatedAt.Gte(time))
|
|
||||||
}
|
}
|
||||||
if req.CreatedAtEnd != nil {
|
if req.CreatedAtEnd != nil {
|
||||||
time := u.DateTail(*req.CreatedAtEnd)
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
do = do.Where(q.Resource.CreatedAt.Lte(time))
|
|
||||||
}
|
}
|
||||||
if req.Expired != nil {
|
if req.Expired != nil {
|
||||||
if *req.Expired {
|
if *req.Expired {
|
||||||
do = do.Where(q.Resource.Where(
|
do = do.Where(q.Resource.Where(
|
||||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
||||||
q.ResourceShort.As("Short").ExpireAt.Lte(time.Now()),
|
q.ResourceShort.As("Short").ExpireAt.Lte(time.Now().UTC()),
|
||||||
).Or(
|
).Or(
|
||||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
||||||
q.ResourceShort.As("Short").Quota.LteCol(q.ResourceShort.As("Short").Used),
|
q.ResourceShort.As("Short").Quota.LteCol(q.ResourceShort.As("Short").Used),
|
||||||
@@ -252,7 +252,7 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
|||||||
} else {
|
} else {
|
||||||
do = do.Where(q.Resource.Where(
|
do = do.Where(q.Resource.Where(
|
||||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeTime)),
|
||||||
q.ResourceShort.As("Short").ExpireAt.Gt(time.Now()),
|
q.ResourceShort.As("Short").ExpireAt.Gt(time.Now().UTC()),
|
||||||
).Or(
|
).Or(
|
||||||
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
q.ResourceShort.As("Short").Type.Eq(int(m.ResourceModeQuota)),
|
||||||
q.ResourceShort.As("Short").Quota.GtCol(q.ResourceShort.As("Short").Used),
|
q.ResourceShort.As("Short").Quota.GtCol(q.ResourceShort.As("Short").Used),
|
||||||
@@ -303,7 +303,7 @@ type PageResourceShortByAdminReq struct {
|
|||||||
|
|
||||||
// PageResourceLongByAdmin 分页查询全部长效套餐
|
// PageResourceLongByAdmin 分页查询全部长效套餐
|
||||||
func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceRead)
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -327,16 +327,16 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
|
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
|
||||||
}
|
}
|
||||||
if req.CreatedAtStart != nil {
|
if req.CreatedAtStart != nil {
|
||||||
do = do.Where(q.Resource.CreatedAt.Gte(*req.CreatedAtStart))
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
}
|
}
|
||||||
if req.CreatedAtEnd != nil {
|
if req.CreatedAtEnd != nil {
|
||||||
do = do.Where(q.Resource.CreatedAt.Lte(*req.CreatedAtEnd))
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
}
|
}
|
||||||
if req.Expired != nil {
|
if req.Expired != nil {
|
||||||
if *req.Expired {
|
if *req.Expired {
|
||||||
do = do.Where(q.Resource.Where(
|
do = do.Where(q.Resource.Where(
|
||||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
||||||
q.ResourceLong.As("Long").ExpireAt.Lte(time.Now()),
|
q.ResourceLong.As("Long").ExpireAt.Lte(time.Now().UTC()),
|
||||||
).Or(
|
).Or(
|
||||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
||||||
q.ResourceLong.As("Long").Quota.LteCol(q.ResourceLong.As("Long").Used),
|
q.ResourceLong.As("Long").Quota.LteCol(q.ResourceLong.As("Long").Used),
|
||||||
@@ -344,7 +344,7 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
|||||||
} else {
|
} else {
|
||||||
do = do.Where(q.Resource.Where(
|
do = do.Where(q.Resource.Where(
|
||||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeTime)),
|
||||||
q.ResourceLong.As("Long").ExpireAt.Gt(time.Now()),
|
q.ResourceLong.As("Long").ExpireAt.Gt(time.Now().UTC()),
|
||||||
).Or(
|
).Or(
|
||||||
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
q.ResourceLong.As("Long").Type.Eq(int(m.ResourceModeQuota)),
|
||||||
q.ResourceLong.As("Long").Quota.GtCol(q.ResourceLong.As("Long").Used),
|
q.ResourceLong.As("Long").Quota.GtCol(q.ResourceLong.As("Long").Used),
|
||||||
@@ -393,6 +393,144 @@ type PageResourceLongByAdminReq struct {
|
|||||||
Expired *bool `json:"expired" form:"expired"`
|
Expired *bool `json:"expired" form:"expired"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageResourceShortOfUserByAdmin 分页查询指定用户的短效套餐
|
||||||
|
func PageResourceShortOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageResourceShortOfUserByAdminReq
|
||||||
|
if err = g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
|
||||||
|
}
|
||||||
|
if req.Active != nil {
|
||||||
|
do = do.Where(q.Resource.Active.Is(*req.Active))
|
||||||
|
}
|
||||||
|
if req.Mode != nil {
|
||||||
|
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.Resource.Debug().
|
||||||
|
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
|
||||||
|
Select(
|
||||||
|
q.Resource.ALL,
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
q.ResourceShort.As("Short").Type.As("Short__type"),
|
||||||
|
q.ResourceShort.As("Short").Live.As("Short__live"),
|
||||||
|
q.ResourceShort.As("Short").Quota.As("Short__quota"),
|
||||||
|
q.ResourceShort.As("Short").Used.As("Short__used"),
|
||||||
|
q.ResourceShort.As("Short").Daily.As("Short__daily"),
|
||||||
|
q.ResourceShort.As("Short").LastAt.As("Short__last_at"),
|
||||||
|
q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"),
|
||||||
|
q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"),
|
||||||
|
).
|
||||||
|
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do).
|
||||||
|
Order(q.Resource.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageResourceShortOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
ResourceNo *string `json:"resource_no"`
|
||||||
|
Active *bool `json:"active"`
|
||||||
|
Mode *int `json:"mode"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageResourceLongOfUserByAdmin 分页查询指定用户的长效套餐
|
||||||
|
func PageResourceLongOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req PageResourceLongOfUserByAdminReq
|
||||||
|
if err = g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
|
||||||
|
if req.ResourceNo != nil {
|
||||||
|
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
|
||||||
|
}
|
||||||
|
if req.Active != nil {
|
||||||
|
do = do.Where(q.Resource.Active.Is(*req.Active))
|
||||||
|
}
|
||||||
|
if req.Mode != nil {
|
||||||
|
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := q.Resource.
|
||||||
|
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
|
||||||
|
Select(
|
||||||
|
q.Resource.ALL,
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
q.ResourceLong.As("Long").Type.As("Long__type"),
|
||||||
|
q.ResourceLong.As("Long").Live.As("Long__live"),
|
||||||
|
q.ResourceLong.As("Long").Quota.As("Long__quota"),
|
||||||
|
q.ResourceLong.As("Long").Used.As("Long__used"),
|
||||||
|
q.ResourceLong.As("Long").Daily.As("Long__daily"),
|
||||||
|
q.ResourceLong.As("Long").LastAt.As("Long__last_at"),
|
||||||
|
q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"),
|
||||||
|
q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"),
|
||||||
|
).
|
||||||
|
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do).
|
||||||
|
Order(q.Resource.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageResourceLongOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
ResourceNo *string `json:"resource_no"`
|
||||||
|
Active *bool `json:"active"`
|
||||||
|
Mode *int `json:"mode"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end"`
|
||||||
|
}
|
||||||
|
|
||||||
// AllActiveResource 所有可用套餐
|
// AllActiveResource 所有可用套餐
|
||||||
func AllActiveResource(c *fiber.Ctx) error {
|
func AllActiveResource(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -410,6 +548,8 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
Joins(
|
Joins(
|
||||||
q.Resource.Short,
|
q.Resource.Short,
|
||||||
q.Resource.Long,
|
q.Resource.Long,
|
||||||
|
q.Resource.Short.Sku,
|
||||||
|
q.Resource.Long.Sku,
|
||||||
).
|
).
|
||||||
Where(
|
Where(
|
||||||
q.Resource.UserID.Eq(authCtx.User.ID),
|
q.Resource.UserID.Eq(authCtx.User.ID),
|
||||||
@@ -418,9 +558,9 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
|
q.Resource.Type.Eq(int(m.ResourceTypeShort)),
|
||||||
q.ResourceShort.As(q.Resource.Short.Name()).Where(
|
q.ResourceShort.As(q.Resource.Short.Name()).Where(
|
||||||
short.Type.Eq(int(m.ResourceModeTime)),
|
short.Type.Eq(int(m.ResourceModeTime)),
|
||||||
short.ExpireAt.Gte(now),
|
short.ExpireAt.Gte(now.UTC()),
|
||||||
q.ResourceShort.As(q.Resource.Short.Name()).
|
q.ResourceShort.As(q.Resource.Short.Name()).
|
||||||
Where(short.LastAt.Lt(u.Today())).
|
Where(short.LastAt.Lt(u.Today().UTC())).
|
||||||
Or(short.Quota.GtCol(short.Daily)),
|
Or(short.Quota.GtCol(short.Daily)),
|
||||||
).Or(
|
).Or(
|
||||||
short.Type.Eq(int(m.ResourceModeQuota)),
|
short.Type.Eq(int(m.ResourceModeQuota)),
|
||||||
@@ -430,9 +570,9 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
|
q.Resource.Type.Eq(int(m.ResourceTypeLong)),
|
||||||
q.ResourceLong.As(q.Resource.Long.Name()).Where(
|
q.ResourceLong.As(q.Resource.Long.Name()).Where(
|
||||||
long.Type.Eq(int(m.ResourceModeTime)),
|
long.Type.Eq(int(m.ResourceModeTime)),
|
||||||
long.ExpireAt.Gte(now),
|
long.ExpireAt.Gte(now.UTC()),
|
||||||
q.ResourceLong.As(q.Resource.Long.Name()).
|
q.ResourceLong.As(q.Resource.Long.Name()).
|
||||||
Where(long.LastAt.Lt(u.Today())).
|
Where(long.LastAt.Lt(u.Today().UTC())).
|
||||||
Or(long.Quota.GtCol(long.Daily)),
|
Or(long.Quota.GtCol(long.Daily)),
|
||||||
).Or(
|
).Or(
|
||||||
long.Type.Eq(int(m.ResourceModeQuota)),
|
long.Type.Eq(int(m.ResourceModeQuota)),
|
||||||
@@ -446,10 +586,16 @@ func AllActiveResource(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(resources)
|
for _, resource := range resources {
|
||||||
}
|
switch resource.Type {
|
||||||
|
case m.ResourceTypeShort:
|
||||||
|
resource.Short.Sku = &m.ProductSku{Name: resource.Short.Sku.Name}
|
||||||
|
case m.ResourceTypeLong:
|
||||||
|
resource.Long.Sku = &m.ProductSku{Name: resource.Long.Sku.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type AllResourceReq struct {
|
return c.JSON(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateResourceByAdmin(c *fiber.Ctx) error {
|
func UpdateResourceByAdmin(c *fiber.Ctx) error {
|
||||||
@@ -470,6 +616,30 @@ func UpdateResourceByAdmin(c *fiber.Ctx) error {
|
|||||||
return c.JSON(nil)
|
return c.JSON(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateResourceCheckIP(c *fiber.Ctx) error {
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req struct {
|
||||||
|
core.IdReq
|
||||||
|
CheckIP bool `json:"checkip"`
|
||||||
|
}
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Resource.Update(&s.UpdateResourceData{
|
||||||
|
IdReq: req.IdReq,
|
||||||
|
CheckIP: &req.CheckIP,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// StatisticResourceFree 统计每日可用
|
// StatisticResourceFree 统计每日可用
|
||||||
func StatisticResourceFree(c *fiber.Ctx) error {
|
func StatisticResourceFree(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -590,10 +760,10 @@ func StatisticResourceUsage(c *fiber.Ctx) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if req.TimeAfter != nil {
|
if req.TimeAfter != nil {
|
||||||
do.Where(q.LogsUserUsage.Time.Gte(*req.TimeAfter))
|
do = do.Where(q.LogsUserUsage.Time.Gte(req.TimeAfter.UTC()))
|
||||||
}
|
}
|
||||||
if req.TimeBefore != nil {
|
if req.TimeBefore != nil {
|
||||||
do.Where(q.LogsUserUsage.Time.Lte(*req.TimeBefore))
|
do = do.Where(q.LogsUserUsage.Time.Lte(req.TimeBefore.UTC()))
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = new(StatisticResourceUsageResp)
|
var data = new(StatisticResourceUsageResp)
|
||||||
@@ -658,10 +828,7 @@ type CreateResourceReq struct {
|
|||||||
// ResourcePrice 套餐价格
|
// ResourcePrice 套餐价格
|
||||||
func ResourcePrice(c *fiber.Ctx) error {
|
func ResourcePrice(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
_, err := auth.GetAuthCtx(c).PermitSecretClient()
|
ac := auth.GetAuthCtx(c)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
var req = new(CreateResourceReq)
|
var req = new(CreateResourceReq)
|
||||||
@@ -670,16 +837,7 @@ func ResourcePrice(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取套餐价格
|
// 获取套餐价格
|
||||||
// sku, err := s.Resource.GetSku(req.CreateResourceData.Code())
|
detail, err := req.TradeDetail(ac.User)
|
||||||
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -687,11 +845,13 @@ func ResourcePrice(c *fiber.Ctx) error {
|
|||||||
// 计算折扣
|
// 计算折扣
|
||||||
return c.JSON(ResourcePriceResp{
|
return c.JSON(ResourcePriceResp{
|
||||||
Price: detail.Amount.StringFixed(2),
|
Price: detail.Amount.StringFixed(2),
|
||||||
Discounted: detail.Actual.StringFixed(2),
|
Discounted: detail.Discounted.StringFixed(2),
|
||||||
|
Actual: detail.Actual.StringFixed(2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourcePriceResp struct {
|
type ResourcePriceResp struct {
|
||||||
Price string `json:"price"`
|
Price string `json:"price"`
|
||||||
Discounted string `json:"discounted_price"`
|
Discounted string `json:"discounted"`
|
||||||
|
Actual string `json:"actual"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
"platform/pkg/u"
|
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
@@ -53,12 +52,10 @@ func PageTradeByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.Trade.Status.Eq(*req.Status))
|
do = do.Where(q.Trade.Status.Eq(*req.Status))
|
||||||
}
|
}
|
||||||
if req.CreatedAtStart != nil {
|
if req.CreatedAtStart != nil {
|
||||||
time := u.DateHead(*req.CreatedAtStart)
|
do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
do = do.Where(q.Trade.CreatedAt.Gte(time))
|
|
||||||
}
|
}
|
||||||
if req.CreatedAtEnd != nil {
|
if req.CreatedAtEnd != nil {
|
||||||
time := u.DateTail(*req.CreatedAtEnd)
|
do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
do = do.Where(q.Trade.CreatedAt.Lte(time))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户列表
|
// 查询用户列表
|
||||||
@@ -97,6 +94,82 @@ type PageTradeByAdminReq struct {
|
|||||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageTradeOfUserByAdmin 分页查询指定用户的订单
|
||||||
|
func PageTradeOfUserByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeReadOfUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageTradeOfUserByAdminReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询语句
|
||||||
|
do := q.Trade.Where(q.Trade.UserID.Eq(req.UserID))
|
||||||
|
if req.InnerNo != nil {
|
||||||
|
do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo))
|
||||||
|
}
|
||||||
|
if req.OuterNo != nil {
|
||||||
|
do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo))
|
||||||
|
}
|
||||||
|
if req.Method != nil {
|
||||||
|
do = do.Where(q.Trade.Method.Eq(*req.Method))
|
||||||
|
}
|
||||||
|
if req.Platform != nil {
|
||||||
|
do = do.Where(q.Trade.Platform.Eq(*req.Platform))
|
||||||
|
}
|
||||||
|
if req.Status != nil {
|
||||||
|
do = do.Where(q.Trade.Status.Eq(*req.Status))
|
||||||
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.Trade.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.Trade.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询订单列表
|
||||||
|
list, total, err := q.Trade.
|
||||||
|
Joins(q.Trade.User).
|
||||||
|
Select(
|
||||||
|
q.Trade.ALL,
|
||||||
|
q.User.As("User").Phone.As("User__phone"),
|
||||||
|
q.User.As("User").Name.As("User__name"),
|
||||||
|
).
|
||||||
|
Where(do).
|
||||||
|
Order(q.Trade.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
List: list,
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageTradeOfUserByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
InnerNo *string `json:"inner_no,omitempty"`
|
||||||
|
OuterNo *string `json:"outer_no,omitempty"`
|
||||||
|
Method *int `json:"method,omitempty"`
|
||||||
|
Platform *int `json:"platform,omitempty"`
|
||||||
|
Status *int `json:"status,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// 创建订单
|
// 创建订单
|
||||||
func TradeCreate(c *fiber.Ctx) error {
|
func TradeCreate(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -104,6 +177,9 @@ func TradeCreate(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if authCtx.User.IDType == m.UserIDTypeUnverified {
|
||||||
|
return core.NewBizErr("请先实名认证后再购买")
|
||||||
|
}
|
||||||
|
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
req := new(TradeCreateReq)
|
req := new(TradeCreateReq)
|
||||||
@@ -143,6 +219,8 @@ type TradeCreateReq struct {
|
|||||||
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
|
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// 完成订单
|
// 完成订单
|
||||||
func TradeComplete(c *fiber.Ctx) error {
|
func TradeComplete(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -152,13 +230,13 @@ func TradeComplete(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析请求参数
|
// 解析请求参数
|
||||||
req := new(TradeCompleteReq)
|
var req s.TradeRef
|
||||||
if err := g.Validator.ParseBody(c, req); err != nil {
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查订单状态
|
// 检查订单状态
|
||||||
err = s.Trade.CompleteTrade(authCtx.User, &req.TradeRef)
|
err = s.Trade.CompleteTrade(authCtx.User, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -166,10 +244,40 @@ func TradeComplete(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradeCompleteReq struct {
|
// 管理员完成订单
|
||||||
s.TradeRef
|
func TradeCompleteByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWriteComplete)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
var req struct {
|
||||||
|
s.TradeRef
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
user, err := s.User.Get(q.Q, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成订单
|
||||||
|
err = s.Trade.CompleteTrade(user, &req.TradeRef)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// 取消订单
|
// 取消订单
|
||||||
func TradeCancel(c *fiber.Ctx) error {
|
func TradeCancel(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
@@ -198,6 +306,8 @@ type TradeCancelReq struct {
|
|||||||
s.TradeRef
|
s.TradeRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// 检查订单
|
// 检查订单
|
||||||
func TradeCheck(c *fiber.Ctx) error {
|
func TradeCheck(c *fiber.Ctx) error {
|
||||||
// 检查权限:sse 接口暂时不检查权限
|
// 检查权限:sse 接口暂时不检查权限
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
s "platform/web/services"
|
s "platform/web/services"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"gorm.io/gen/field"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,11 +66,17 @@ func PageUserByAdmin(c *fiber.Ctx) error {
|
|||||||
do = do.Where(q.User.AdminID.IsNull())
|
do = do.Where(q.User.AdminID.IsNull())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if req.CreatedAtStart != nil {
|
||||||
|
do = do.Where(q.User.CreatedAt.Gte(req.CreatedAtStart.UTC()))
|
||||||
|
}
|
||||||
|
if req.CreatedAtEnd != nil {
|
||||||
|
do = do.Where(q.User.CreatedAt.Lte(req.CreatedAtEnd.UTC()))
|
||||||
|
}
|
||||||
|
|
||||||
// 查询用户列表
|
// 查询用户列表
|
||||||
users, total, err := q.User.
|
users, total, err := q.User.
|
||||||
Preload(q.User.Admin, q.User.Discount).
|
Preload(q.User.Admin, q.User.Discount).
|
||||||
Omit(q.User.Password).
|
Omit(q.User.Password, q.Admin.Password).
|
||||||
Where(do).
|
Where(do).
|
||||||
Order(q.User.CreatedAt.Desc()).
|
Order(q.User.CreatedAt.Desc()).
|
||||||
FindByPage(req.GetOffset(), req.GetLimit())
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
@@ -76,6 +85,12 @@ func PageUserByAdmin(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
|
|
||||||
|
if user.IDNo != nil && len(*user.IDNo) == 18 {
|
||||||
|
var str = *user.IDNo
|
||||||
|
*user.IDNo = str[:6] + "****" + str[len(str)-2:]
|
||||||
|
}
|
||||||
|
|
||||||
if user.Admin != nil {
|
if user.Admin != nil {
|
||||||
user.Admin = &m.Admin{
|
user.Admin = &m.Admin{
|
||||||
Name: user.Admin.Name,
|
Name: user.Admin.Name,
|
||||||
@@ -94,11 +109,13 @@ func PageUserByAdmin(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
type PageUserByAdminReq struct {
|
type PageUserByAdminReq struct {
|
||||||
core.PageReq
|
core.PageReq
|
||||||
Account *string `json:"account,omitempty"`
|
Account *string `json:"account,omitempty"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Identified *bool `json:"identified,omitempty"`
|
Identified *bool `json:"identified,omitempty"`
|
||||||
Enabled *bool `json:"enabled,omitempty"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
Assigned *bool `json:"assigned,omitempty"`
|
Assigned *bool `json:"assigned,omitempty"`
|
||||||
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管理员获取单个用户
|
// 管理员获取单个用户
|
||||||
@@ -133,7 +150,7 @@ func GetUserByAdmin(c *fiber.Ctx) error {
|
|||||||
// 查询用户
|
// 查询用户
|
||||||
user, err := q.User.
|
user, err := q.User.
|
||||||
Preload(q.User.Admin, q.User.Discount).
|
Preload(q.User.Admin, q.User.Discount).
|
||||||
Omit(q.User.Password).
|
Omit(q.User.Password, q.Admin.Password).
|
||||||
Where(do).
|
Where(do).
|
||||||
Order(q.User.CreatedAt.Desc()).
|
Order(q.User.CreatedAt.Desc()).
|
||||||
First()
|
First()
|
||||||
@@ -252,7 +269,7 @@ type UpdateUserBalanceByAdminData struct {
|
|||||||
// 绑定管理员
|
// 绑定管理员
|
||||||
func BindAdmin(c *fiber.Ctx) error {
|
func BindAdmin(c *fiber.Ctx) error {
|
||||||
// 检查权限
|
// 检查权限
|
||||||
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
|
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -266,7 +283,7 @@ func BindAdmin(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
result, err := q.User.Where(
|
r, err := q.User.Where(
|
||||||
q.User.ID.Eq(int32(req.UserID)),
|
q.User.ID.Eq(int32(req.UserID)),
|
||||||
q.User.AdminID.IsNull(),
|
q.User.AdminID.IsNull(),
|
||||||
).UpdateColumnSimple(
|
).UpdateColumnSimple(
|
||||||
@@ -275,7 +292,7 @@ func BindAdmin(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if result.RowsAffected == 0 {
|
if r.RowsAffected == 0 {
|
||||||
return core.NewBizErr("用户已绑定管理员")
|
return core.NewBizErr("用户已绑定管理员")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,27 +315,45 @@ func UpdateUser(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
_, err = q.User.
|
do := make([]field.AssignExpr, 0)
|
||||||
|
if req.Username != nil && *req.Username != "" {
|
||||||
|
do = append(do, q.User.Username.Value(*req.Username))
|
||||||
|
}
|
||||||
|
if req.Email != nil {
|
||||||
|
if *req.Email == "" {
|
||||||
|
do = append(do, q.User.Email.Null())
|
||||||
|
} else {
|
||||||
|
do = append(do, q.User.Email.Value(*req.Email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.ContactQQ != nil {
|
||||||
|
do = append(do, q.User.ContactQQ.Value(*req.ContactQQ))
|
||||||
|
}
|
||||||
|
if req.ContactWechat != nil {
|
||||||
|
do = append(do, q.User.ContactWechat.Value(*req.ContactWechat))
|
||||||
|
}
|
||||||
|
r, err := q.User.
|
||||||
Where(q.User.ID.Eq(authCtx.User.ID)).
|
Where(q.User.ID.Eq(authCtx.User.ID)).
|
||||||
Updates(m.User{
|
UpdateSimple(do...)
|
||||||
Username: &req.Username,
|
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
Email: &req.Email,
|
return core.NewBizErr("用户名或邮箱已被占用")
|
||||||
ContactQQ: &req.ContactQQ,
|
}
|
||||||
ContactWechat: &req.ContactWechat,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserReq struct {
|
type UpdateUserReq struct {
|
||||||
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
Username *string `json:"username" validate:"omitempty,min=3,max=20"`
|
||||||
Email string `json:"email" validate:"omitempty,email"`
|
Email *string `json:"email" validate:"omitempty,email"`
|
||||||
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
|
ContactQQ *string `json:"contact_qq" validate:"omitempty,qq"`
|
||||||
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
|
ContactWechat *string `json:"contact_wechat" validate:"omitempty,wechat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新账号信息
|
// 更新账号信息
|
||||||
@@ -336,7 +371,7 @@ func UpdateAccount(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
_, err = q.User.
|
r, err := q.User.
|
||||||
Where(q.User.ID.Eq(authCtx.User.ID)).
|
Where(q.User.ID.Eq(authCtx.User.ID)).
|
||||||
Updates(m.User{
|
Updates(m.User{
|
||||||
Username: &req.Username,
|
Username: &req.Username,
|
||||||
@@ -345,6 +380,9 @@ func UpdateAccount(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
@@ -369,16 +407,14 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证手机号
|
|
||||||
if req.Phone != authCtx.User.Phone {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "手机号码不正确")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证手机令牌
|
// 验证手机令牌
|
||||||
if req.Code == "" {
|
if req.Code == "" {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "手机号码和验证码不能为空")
|
return fiber.NewError(fiber.StatusBadRequest, "验证码不能为空")
|
||||||
|
}
|
||||||
|
err = s.Verifier.VerifySms(c.Context(), authCtx.User.Phone, req.Code, s.VerifierSmsPurposePassword)
|
||||||
|
if errors.Is(err, s.ErrVerifierServiceInvalid) {
|
||||||
|
return core.NewBizErr(s.ErrVerifierServiceInvalid.Error())
|
||||||
}
|
}
|
||||||
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -389,19 +425,136 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = q.User.
|
r, err := q.User.
|
||||||
Where(q.User.ID.Eq(authCtx.User.ID)).
|
Where(q.User.ID.Eq(authCtx.User.ID)).
|
||||||
UpdateColumn(q.User.Password, newHash)
|
UpdateColumn(q.User.Password, newHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePasswordReq struct {
|
type UpdatePasswordReq struct {
|
||||||
Phone string `json:"phone"`
|
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PageUserNotBindByAdmin 分页获取未绑定管理员的用户
|
||||||
|
func PageUserNotBindByAdmin(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadNotBind)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(PageUserNotBindByAdminReq)
|
||||||
|
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询条件(强制过滤未绑定管理员的用户)
|
||||||
|
do := q.User.Where(q.User.AdminID.IsNull())
|
||||||
|
if req.Phone != nil {
|
||||||
|
do = do.Where(q.User.Phone.Eq(*req.Phone))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户列表
|
||||||
|
users, total, err := q.User.
|
||||||
|
Omit(q.User.Password, q.User.IDNo).
|
||||||
|
Where(do).
|
||||||
|
Order(q.User.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.JSON(core.PageResp{
|
||||||
|
Total: int(total),
|
||||||
|
Page: req.GetPage(),
|
||||||
|
Size: req.GetSize(),
|
||||||
|
List: users,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageUserNotBindByAdminReq struct {
|
||||||
|
core.PageReq
|
||||||
|
Phone *string `json:"phone,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserBalanceIncByAdmin 管理员增加用户余额
|
||||||
|
func UpdateUserBalanceIncByAdmin(c *fiber.Ctx) error {
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req UpdateUserBalanceChangeByAdminData
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
amount, err := decimal.NewFromString(req.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !amount.IsPositive() {
|
||||||
|
return core.NewBizErr("金额必须为正数")
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := s.User.Get(q.Q, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newBalance := user.Balance.Add(amount)
|
||||||
|
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserBalanceDecByAdmin 管理员减少用户余额
|
||||||
|
func UpdateUserBalanceDecByAdmin(c *fiber.Ctx) error {
|
||||||
|
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var req UpdateUserBalanceChangeByAdminData
|
||||||
|
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
amount, err := decimal.NewFromString(req.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !amount.IsPositive() {
|
||||||
|
return core.NewBizErr("金额必须为正数")
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := s.User.Get(q.Q, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newBalance := user.Balance.Sub(amount)
|
||||||
|
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserBalanceChangeByAdminData struct {
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
Amount string `json:"amount" validate:"required"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
"platform/web/services"
|
"platform/web/services"
|
||||||
|
s "platform/web/services"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -13,12 +14,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type VerifierReq struct {
|
type VerifierReq struct {
|
||||||
Purpose services.VerifierSmsPurpose `json:"purpose"`
|
Purpose s.VerifierSmsPurpose `json:"purpose"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SmsCode(c *fiber.Ctx) error {
|
func SendSmsCode(c *fiber.Ctx) error {
|
||||||
|
|
||||||
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
|
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -38,9 +38,9 @@ func SmsCode(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送身份验证码
|
// 发送身份验证码
|
||||||
err = services.Verifier.SendSms(c.Context(), req.Phone, req.Purpose)
|
err = s.Verifier.SendSms(c.Context(), req.Phone, req.Purpose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var sErr services.VerifierServiceSendLimitErr
|
var sErr s.VerifierServiceSendLimitErr
|
||||||
if errors.As(err, &sErr) {
|
if errors.As(err, &sErr) {
|
||||||
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
|
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,23 @@ func SmsCode(c *fiber.Ctx) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendSmsCodeForPassword(c *fiber.Ctx) error {
|
||||||
|
ac, err := auth.GetAuthCtx(c).PermitUser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Verifier.SendSms(c.Context(), ac.User.Phone, s.VerifierSmsPurposePassword); err != nil {
|
||||||
|
var sErr s.VerifierServiceSendLimitErr
|
||||||
|
if errors.As(err, &sErr) {
|
||||||
|
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func DebugGetSmsCode(c *fiber.Ctx) error {
|
func DebugGetSmsCode(c *fiber.Ctx) error {
|
||||||
if env.RunMode != env.RunModeDev {
|
if env.RunMode != env.RunModeDev {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "not allowed")
|
return fiber.NewError(fiber.StatusForbidden, "not allowed")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
@@ -92,17 +94,35 @@ func CreateWhitelist(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
ip, err := secureAddr(req.Host)
|
ip, err := secureAddr(req.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return core.NewBizErr("IP 地址无效", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建白名单
|
// 创建白名单
|
||||||
err = q.Whitelist.Create(&m.Whitelist{
|
uid := authCtx.User.ID
|
||||||
UserID: authCtx.User.ID,
|
err = g.Redsync.WithLock(whitelistKey(uid), func() error {
|
||||||
IP: u.Z(ip),
|
count, err := q.Whitelist.Where(
|
||||||
Remark: &req.Remark,
|
q.Whitelist.UserID.Eq(uid),
|
||||||
|
).Count()
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取白名单数量失败", err)
|
||||||
|
}
|
||||||
|
if count >= 5 {
|
||||||
|
return core.NewBizErr("白名单数量已达上限")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.Whitelist.Create(&m.Whitelist{
|
||||||
|
UserID: authCtx.User.ID,
|
||||||
|
IP: u.Z(ip),
|
||||||
|
Remark: &req.Remark,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("添加白名单失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("添加白名单失败", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -132,11 +152,11 @@ func UpdateWhitelist(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
ip, err := secureAddr(req.Host)
|
ip, err := secureAddr(req.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return core.NewBizErr("IP 地址无效", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新白名单
|
// 更新白名单
|
||||||
_, err = q.Whitelist.
|
r, err := q.Whitelist.
|
||||||
Where(
|
Where(
|
||||||
q.Whitelist.ID.Eq(req.ID),
|
q.Whitelist.ID.Eq(req.ID),
|
||||||
q.Whitelist.UserID.Eq(authCtx.User.ID),
|
q.Whitelist.UserID.Eq(authCtx.User.ID),
|
||||||
@@ -148,6 +168,9 @@ func UpdateWhitelist(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("白名单状态已过期")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +204,7 @@ func RemoveWhitelist(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除白名单
|
// 删除白名单
|
||||||
_, err = q.Whitelist.
|
r, err := q.Whitelist.
|
||||||
Where(
|
Where(
|
||||||
q.Whitelist.ID.In(ids...),
|
q.Whitelist.ID.In(ids...),
|
||||||
q.Whitelist.UserID.Eq(authCtx.User.ID),
|
q.Whitelist.UserID.Eq(authCtx.User.ID),
|
||||||
@@ -192,6 +215,9 @@ func RemoveWhitelist(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("白名单状态已过期")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +227,11 @@ func secureAddr(str string) (*orm.Inet, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
|
if !ip.IsGlobalUnicast() && env.RunMode != env.RunModeDev {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "IP 地址不可用")
|
return nil, errors.New("IP 地址不可用")
|
||||||
}
|
}
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func whitelistKey(userID int32) string {
|
||||||
|
return fmt.Sprintf("platform:whitelist:add:%d", userID)
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2/middleware/requestid"
|
"github.com/gofiber/fiber/v2/middleware/requestid"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jxskiss/base62"
|
"github.com/jxskiss/base62"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ApplyMiddlewares(app *fiber.App) {
|
func ApplyMiddlewares(app *fiber.App) {
|
||||||
@@ -20,13 +22,8 @@ func ApplyMiddlewares(app *fiber.App) {
|
|||||||
EnableStackTrace: true,
|
EnableStackTrace: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// cors
|
// metric
|
||||||
app.Use(cors.New(cors.Config{
|
app.Use(otelfiber.Middleware())
|
||||||
AllowCredentials: true,
|
|
||||||
AllowOriginsFunc: func(origin string) bool {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
app.Use(logger.New(logger.Config{
|
app.Use(logger.New(logger.Config{
|
||||||
@@ -35,8 +32,31 @@ func ApplyMiddlewares(app *fiber.App) {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// metric
|
// 补充 otel span attr
|
||||||
app.Use(otelfiber.Middleware())
|
app.Use(func(c *fiber.Ctx) error {
|
||||||
|
err := c.Next()
|
||||||
|
|
||||||
|
span := trace.SpanFromContext(c.UserContext())
|
||||||
|
if !span.IsRecording() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
str := ""
|
||||||
|
if err != nil {
|
||||||
|
str = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
span.SetAttributes(attribute.String("http.response.error", str))
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
// cors
|
||||||
|
app.Use(cors.New(cors.Config{
|
||||||
|
AllowCredentials: true,
|
||||||
|
AllowOriginsFunc: func(origin string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
// request id
|
// request id
|
||||||
app.Use(requestid.New(requestid.Config{
|
app.Use(requestid.New(requestid.Config{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
type Admin struct {
|
type Admin struct {
|
||||||
core.Model
|
core.Model
|
||||||
Username string `json:"username" gorm:"column:username"` // 用户名
|
Username string `json:"username" gorm:"column:username"` // 用户名
|
||||||
Password string `json:"password" gorm:"column:password"` // 密码
|
Password string `json:"-" gorm:"column:password"` // 密码
|
||||||
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
|
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
|
||||||
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
|
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
|
||||||
Phone *string `json:"phone,omitempty" gorm:"column:phone"` // 手机号码
|
Phone *string `json:"phone,omitempty" gorm:"column:phone"` // 手机号码
|
||||||
@@ -20,6 +20,7 @@ type Admin struct {
|
|||||||
LastLogin *time.Time `json:"last_login,omitempty" gorm:"column:last_login"` // 最后登录时间
|
LastLogin *time.Time `json:"last_login,omitempty" gorm:"column:last_login"` // 最后登录时间
|
||||||
LastLoginIP *orm.Inet `json:"last_login_ip,omitempty" gorm:"column:last_login_ip"` // 最后登录地址
|
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"` // 最后登录代理
|
LastLoginUA *string `json:"last_login_ua,omitempty" gorm:"column:last_login_ua"` // 最后登录代理
|
||||||
|
Lock bool `json:"lock" gorm:"column:lock"` // 是否锁定编辑
|
||||||
|
|
||||||
Roles []*AdminRole `json:"roles" gorm:"many2many:link_admin_role"`
|
Roles []*AdminRole `json:"roles" gorm:"many2many:link_admin_role"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type BalanceActivity struct {
|
|||||||
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
|
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
|
||||||
|
|
||||||
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||||
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
|
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
|
||||||
Admin *User `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
|
Admin *Admin `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,22 @@ import (
|
|||||||
// Bill 账单表
|
// Bill 账单表
|
||||||
type Bill struct {
|
type Bill struct {
|
||||||
core.Model
|
core.Model
|
||||||
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
|
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
|
||||||
TradeID *int32 `json:"trade_id,omitempty" gorm:"column:trade_id"` // 订单ID
|
TradeID *int32 `json:"trade_id,omitempty" gorm:"column:trade_id"` // 订单ID
|
||||||
ResourceID *int32 `json:"resource_id,omitempty" gorm:"column:resource_id"` // 套餐ID
|
ResourceID *int32 `json:"resource_id,omitempty" gorm:"column:resource_id"` // 套餐ID
|
||||||
RefundID *int32 `json:"refund_id,omitempty" gorm:"column:refund_id"` // 退款ID
|
RefundID *int32 `json:"refund_id,omitempty" gorm:"column:refund_id"` // 退款ID
|
||||||
CouponID *int32 `json:"coupon_id,omitempty" gorm:"column:coupon_id"` // 优惠券ID
|
CouponUserID *int32 `json:"coupon_user_id,omitempty" gorm:"column:coupon_user_id"` // 优惠券发放ID
|
||||||
BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号
|
BillNo string `json:"bill_no" gorm:"column:bill_no"` // 易读账单号
|
||||||
Info *string `json:"info,omitempty" gorm:"column:info"` // 产品可读信息
|
Info *string `json:"info,omitempty" gorm:"column:info"` // 产品可读信息
|
||||||
Type BillType `json:"type" gorm:"column:type"` // 账单类型:1-消费,2-退款,3-充值
|
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"` // 实付金额
|
Actual decimal.Decimal `json:"actual" gorm:"column:actual"` // 实付金额
|
||||||
|
|
||||||
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||||
Trade *Trade `json:"trade,omitempty" gorm:"foreignKey:TradeID"`
|
Trade *Trade `json:"trade,omitempty" gorm:"foreignKey:TradeID"`
|
||||||
Resource *Resource `json:"resource,omitempty" gorm:"foreignKey:ResourceID"`
|
Resource *Resource `json:"resource,omitempty" gorm:"foreignKey:ResourceID"`
|
||||||
Refund *Refund `json:"refund,omitempty" gorm:"foreignKey:RefundID"`
|
Refund *Refund `json:"refund,omitempty" gorm:"foreignKey:RefundID"`
|
||||||
|
CouponUser *CouponUser `json:"coupon,omitempty" gorm:"foreignKey:CouponUserID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BillType 账单类型枚举
|
// BillType 账单类型枚举
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
core.Model
|
core.Model
|
||||||
ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符
|
ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符
|
||||||
ClientSecret string `json:"client_secret" gorm:"column:client_secret"` // OAuth2客户端密钥
|
ClientSecret string `json:"-" gorm:"column:client_secret"` // OAuth2客户端密钥
|
||||||
RedirectURI *string `json:"redirect_uri,omitempty" gorm:"column:redirect_uri"` // OAuth2 重定向URI
|
RedirectURI *string `json:"redirect_uri,omitempty" gorm:"column:redirect_uri"` // OAuth2 重定向URI
|
||||||
Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范:1-native,2-browser,3-web,4-api
|
Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范:1-native,2-browser,3-web,4-api
|
||||||
Name string `json:"name" gorm:"column:name"` // 名称
|
Name string `json:"name" gorm:"column:name"` // 名称
|
||||||
|
|||||||
@@ -10,20 +10,29 @@ import (
|
|||||||
// Coupon 优惠券表
|
// Coupon 优惠券表
|
||||||
type Coupon struct {
|
type Coupon struct {
|
||||||
core.Model
|
core.Model
|
||||||
UserID *int32 `json:"user_id,omitempty" gorm:"column:user_id"` // 用户ID
|
Name string `json:"name" gorm:"column:name"` // 优惠券名称
|
||||||
Code string `json:"code" gorm:"column:code"` // 优惠券代码
|
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 优惠券金额
|
||||||
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 优惠券备注
|
MinAmount decimal.Decimal `json:"min_amount" gorm:"column:min_amount"` // 最低消费金额
|
||||||
Amount decimal.Decimal `json:"amount" gorm:"column:amount"` // 优惠券金额
|
Count int32 `json:"count" gorm:"column:count"` // 优惠券数量
|
||||||
MinAmount decimal.Decimal `json:"min_amount" gorm:"column:min_amount"` // 最低消费金额
|
Status CouponStatus `json:"status" gorm:"column:status"` // 优惠券状态:0-禁用,1-正常
|
||||||
Status CouponStatus `json:"status" gorm:"column:status"` // 优惠券状态:0-未使用,1-已使用,2-已过期
|
ExpireType CouponExpireType `json:"expire_type" gorm:"column:expire_type"` // 过期类型:0-不过期,1-固定日期,2-相对日期(从发放时间算起)
|
||||||
ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间
|
ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间,固定日期必填
|
||||||
|
ExpireIn *int `json:"expire_in,omitempty" gorm:"column:expire_in"` // 过期时长(天),相对日期必填
|
||||||
}
|
}
|
||||||
|
|
||||||
// CouponStatus 优惠券状态枚举
|
// CouponStatus 优惠券使用状态枚举
|
||||||
type CouponStatus int
|
type CouponStatus int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CouponStatusUnused CouponStatus = 0 // 未使用
|
CouponStatusDisabled CouponStatus = 0 // 禁用
|
||||||
CouponStatusUsed CouponStatus = 1 // 已使用
|
CouponStatusEnabled CouponStatus = 1 // 正常
|
||||||
CouponStatusExpired CouponStatus = 2 // 已过期
|
)
|
||||||
|
|
||||||
|
// CouponExpireType 优惠券过期类型枚举
|
||||||
|
type CouponExpireType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CouponExpireTypeNever CouponExpireType = 0 // 不过期
|
||||||
|
CouponExpireTypeFixed CouponExpireType = 1 // 固定日期
|
||||||
|
CouponExpireTypeRelative CouponExpireType = 2 // 相对日期
|
||||||
)
|
)
|
||||||
|
|||||||
26
web/models/coupon_user.go
Normal file
26
web/models/coupon_user.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// CouponUser 优惠券发放表
|
||||||
|
type CouponUser struct {
|
||||||
|
ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID
|
||||||
|
CouponID int32 `json:"coupon_id" gorm:"column:coupon_id"` // 优惠券ID
|
||||||
|
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
|
||||||
|
Status CouponUserStatus `json:"status" gorm:"column:status"` // 使用状态:0-未使用,1-已使用,2-已禁用
|
||||||
|
ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间
|
||||||
|
UsedAt *time.Time `json:"used_at,omitempty" gorm:"column:used_at"` // 使用时间
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
|
||||||
|
|
||||||
|
Coupon *Coupon `json:"coupon,omitempty" gorm:"foreignKey:CouponID"`
|
||||||
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CouponUserStatus 优惠券发放状态枚举
|
||||||
|
type CouponUserStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CouponUserStatusUnused CouponUserStatus = 0 // 未使用
|
||||||
|
CouponUserStatusUsed CouponUserStatus = 1 // 已使用
|
||||||
|
CouponUserStatusDisabled CouponUserStatus = 2 // 已禁用
|
||||||
|
)
|
||||||
@@ -12,6 +12,8 @@ type Product struct {
|
|||||||
Description *string `json:"description,omitempty" gorm:"column:description"` // 产品描述
|
Description *string `json:"description,omitempty" gorm:"column:description"` // 产品描述
|
||||||
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
|
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
|
||||||
Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态:0-禁用,1-正常
|
Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态:0-禁用,1-正常
|
||||||
|
|
||||||
|
Skus []*ProductSku `json:"skus,omitempty" gorm:"foreignKey:ProductID"` // 产品包含的SKU列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProductStatus 产品状态枚举
|
// ProductStatus 产品状态枚举
|
||||||
|
|||||||
@@ -14,7 +14,19 @@ type ProductSku struct {
|
|||||||
Code string `json:"code" gorm:"column:code"` // SSKU 代码:格式为 key=value,key=value,...,其中,key:value 是 SKU 的属性,多个属性用逗号分隔
|
Code string `json:"code" gorm:"column:code"` // SSKU 代码:格式为 key=value,key=value,...,其中,key:value 是 SKU 的属性,多个属性用逗号分隔
|
||||||
Name string `json:"name" gorm:"column:name"` // SKU 可读名称
|
Name string `json:"name" gorm:"column:name"` // SKU 可读名称
|
||||||
Price decimal.Decimal `json:"price" gorm:"column:price"` // 定价
|
Price decimal.Decimal `json:"price" gorm:"column:price"` // 定价
|
||||||
|
PriceMin decimal.Decimal `json:"price_min" gorm:"column:price_min"` // 最低价格
|
||||||
|
Status SkuStatus `json:"status" gorm:"column:status"` // SKU 状态:0-禁用,1-正常
|
||||||
|
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
|
||||||
|
CountMin int32 `json:"count_min" gorm:"column:count_min"` // 最小购买数量
|
||||||
|
|
||||||
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
|
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
|
||||||
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
|
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SkuStatus SKU 状态
|
||||||
|
type SkuStatus int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
SkuStatusDisabled SkuStatus = 0 // 禁用
|
||||||
|
SkuStatusEnabled SkuStatus = 1 // 正常
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type Resource struct {
|
|||||||
Active bool `json:"active" gorm:"column:active"` // 套餐状态
|
Active bool `json:"active" gorm:"column:active"` // 套餐状态
|
||||||
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型:1-短效动态,2-长效动态
|
Type ResourceType `json:"type" gorm:"column:type"` // 套餐类型:1-短效动态,2-长效动态
|
||||||
Code string `json:"code" gorm:"column:code"` // 产品编码
|
Code string `json:"code" gorm:"column:code"` // 产品编码
|
||||||
|
CheckIP bool `json:"checkip" gorm:"column:checkip"` // 是否检查IP
|
||||||
|
|
||||||
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||||
Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`
|
Short *ResourceShort `json:"short,omitempty" gorm:"foreignKey:ResourceID"`
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type User struct {
|
|||||||
Phone string `json:"phone" gorm:"column:phone"` // 手机号码
|
Phone string `json:"phone" gorm:"column:phone"` // 手机号码
|
||||||
Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
|
Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
|
||||||
Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
|
Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
|
||||||
Password *string `json:"password,omitempty" gorm:"column:password"` // 用户密码
|
Password *string `json:"-" gorm:"column:password"` // 用户密码
|
||||||
Source *UserSource `json:"source,omitempty" gorm:"column:source"` // 用户来源:0-官网注册,1-管理员添加,2-代理商注册,3-代理商添加
|
Source *UserSource `json:"source,omitempty" gorm:"column:source"` // 用户来源:0-官网注册,1-管理员添加,2-代理商注册,3-代理商添加
|
||||||
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
|
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
|
||||||
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
|
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
|
|||||||
_admin.LastLogin = field.NewTime(tableName, "last_login")
|
_admin.LastLogin = field.NewTime(tableName, "last_login")
|
||||||
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
|
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
|
||||||
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
|
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
|
||||||
|
_admin.Lock = field.NewBool(tableName, "lock")
|
||||||
_admin.Roles = adminManyToManyRoles{
|
_admin.Roles = adminManyToManyRoles{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@ type admin struct {
|
|||||||
LastLogin field.Time
|
LastLogin field.Time
|
||||||
LastLoginIP field.Field
|
LastLoginIP field.Field
|
||||||
LastLoginUA field.String
|
LastLoginUA field.String
|
||||||
|
Lock field.Bool
|
||||||
Roles adminManyToManyRoles
|
Roles adminManyToManyRoles
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
@@ -122,6 +124,7 @@ func (a *admin) updateTableName(table string) *admin {
|
|||||||
a.LastLogin = field.NewTime(table, "last_login")
|
a.LastLogin = field.NewTime(table, "last_login")
|
||||||
a.LastLoginIP = field.NewField(table, "last_login_ip")
|
a.LastLoginIP = field.NewField(table, "last_login_ip")
|
||||||
a.LastLoginUA = field.NewString(table, "last_login_ua")
|
a.LastLoginUA = field.NewString(table, "last_login_ua")
|
||||||
|
a.Lock = field.NewBool(table, "lock")
|
||||||
|
|
||||||
a.fillFieldMap()
|
a.fillFieldMap()
|
||||||
|
|
||||||
@@ -138,7 +141,7 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *admin) fillFieldMap() {
|
func (a *admin) fillFieldMap() {
|
||||||
a.fieldMap = make(map[string]field.Expr, 15)
|
a.fieldMap = make(map[string]field.Expr, 16)
|
||||||
a.fieldMap["id"] = a.ID
|
a.fieldMap["id"] = a.ID
|
||||||
a.fieldMap["created_at"] = a.CreatedAt
|
a.fieldMap["created_at"] = a.CreatedAt
|
||||||
a.fieldMap["updated_at"] = a.UpdatedAt
|
a.fieldMap["updated_at"] = a.UpdatedAt
|
||||||
@@ -153,6 +156,7 @@ func (a *admin) fillFieldMap() {
|
|||||||
a.fieldMap["last_login"] = a.LastLogin
|
a.fieldMap["last_login"] = a.LastLogin
|
||||||
a.fieldMap["last_login_ip"] = a.LastLoginIP
|
a.fieldMap["last_login_ip"] = a.LastLoginIP
|
||||||
a.fieldMap["last_login_ua"] = a.LastLoginUA
|
a.fieldMap["last_login_ua"] = a.LastLoginUA
|
||||||
|
a.fieldMap["lock"] = a.Lock
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
_balanceActivity.BalanceCurr = field.NewString(tableName, "balance_curr")
|
_balanceActivity.BalanceCurr = field.NewString(tableName, "balance_curr")
|
||||||
_balanceActivity.Remark = field.NewString(tableName, "remark")
|
_balanceActivity.Remark = field.NewString(tableName, "remark")
|
||||||
_balanceActivity.CreatedAt = field.NewTime(tableName, "created_at")
|
_balanceActivity.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_balanceActivity.Admin = balanceActivityHasOneAdmin{
|
_balanceActivity.User = balanceActivityBelongsToUser{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
RelationField: field.NewRelation("Admin", "models.User"),
|
RelationField: field.NewRelation("User", "models.User"),
|
||||||
Admin: struct {
|
Admin: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
Roles struct {
|
Roles struct {
|
||||||
@@ -55,7 +55,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Admin", "models.Admin"),
|
RelationField: field.NewRelation("User.Admin", "models.Admin"),
|
||||||
Roles: struct {
|
Roles: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
Permissions struct {
|
Permissions struct {
|
||||||
@@ -68,7 +68,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Admin.Roles", "models.AdminRole"),
|
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
|
||||||
Permissions: struct {
|
Permissions: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
Parent struct {
|
Parent struct {
|
||||||
@@ -78,16 +78,16 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
}
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions", "models.Permission"),
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
|
||||||
Parent: struct {
|
Parent: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Parent", "models.Permission"),
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
|
||||||
},
|
},
|
||||||
Children: struct {
|
Children: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Children", "models.Permission"),
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -95,7 +95,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Discount", "models.ProductDiscount"),
|
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
|
||||||
},
|
},
|
||||||
Roles: struct {
|
Roles: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -103,21 +103,15 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
}
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Roles", "models.UserRole"),
|
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
|
||||||
Permissions: struct {
|
Permissions: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Admin.Roles.Permissions", "models.Permission"),
|
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_balanceActivity.User = balanceActivityBelongsToUser{
|
|
||||||
db: db.Session(&gorm.Session{}),
|
|
||||||
|
|
||||||
RelationField: field.NewRelation("User", "models.User"),
|
|
||||||
}
|
|
||||||
|
|
||||||
_balanceActivity.Bill = balanceActivityBelongsToBill{
|
_balanceActivity.Bill = balanceActivityBelongsToBill{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
@@ -151,6 +145,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -179,6 +176,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -190,6 +190,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -198,8 +201,16 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -232,6 +243,33 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
|||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Bill.Refund", "models.Refund"),
|
RelationField: field.NewRelation("Bill.Refund", "models.Refund"),
|
||||||
},
|
},
|
||||||
|
CouponUser: struct {
|
||||||
|
field.RelationField
|
||||||
|
Coupon struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
User struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Bill.CouponUser", "models.CouponUser"),
|
||||||
|
Coupon: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Bill.CouponUser.Coupon", "models.Coupon"),
|
||||||
|
},
|
||||||
|
User: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Bill.CouponUser.User", "models.User"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_balanceActivity.Admin = balanceActivityBelongsToAdmin{
|
||||||
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
RelationField: field.NewRelation("Admin", "models.Admin"),
|
||||||
}
|
}
|
||||||
|
|
||||||
_balanceActivity.fillFieldMap()
|
_balanceActivity.fillFieldMap()
|
||||||
@@ -252,12 +290,12 @@ type balanceActivity struct {
|
|||||||
BalanceCurr field.String
|
BalanceCurr field.String
|
||||||
Remark field.String
|
Remark field.String
|
||||||
CreatedAt field.Time
|
CreatedAt field.Time
|
||||||
Admin balanceActivityHasOneAdmin
|
User balanceActivityBelongsToUser
|
||||||
|
|
||||||
User balanceActivityBelongsToUser
|
|
||||||
|
|
||||||
Bill balanceActivityBelongsToBill
|
Bill balanceActivityBelongsToBill
|
||||||
|
|
||||||
|
Admin balanceActivityBelongsToAdmin
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,24 +351,24 @@ func (b *balanceActivity) fillFieldMap() {
|
|||||||
|
|
||||||
func (b balanceActivity) clone(db *gorm.DB) balanceActivity {
|
func (b balanceActivity) clone(db *gorm.DB) balanceActivity {
|
||||||
b.balanceActivityDo.ReplaceConnPool(db.Statement.ConnPool)
|
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 = db.Session(&gorm.Session{Initialized: true})
|
||||||
b.User.db.Statement.ConnPool = db.Statement.ConnPool
|
b.User.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
b.Bill.db = db.Session(&gorm.Session{Initialized: true})
|
b.Bill.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
b.Bill.db.Statement.ConnPool = db.Statement.ConnPool
|
b.Bill.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
|
b.Admin.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
|
b.Admin.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
|
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
|
||||||
b.balanceActivityDo.ReplaceDB(db)
|
b.balanceActivityDo.ReplaceDB(db)
|
||||||
b.Admin.db = db.Session(&gorm.Session{})
|
|
||||||
b.User.db = db.Session(&gorm.Session{})
|
b.User.db = db.Session(&gorm.Session{})
|
||||||
b.Bill.db = db.Session(&gorm.Session{})
|
b.Bill.db = db.Session(&gorm.Session{})
|
||||||
|
b.Admin.db = db.Session(&gorm.Session{})
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
type balanceActivityHasOneAdmin struct {
|
type balanceActivityBelongsToUser struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -361,87 +399,6 @@ type balanceActivityHasOneAdmin struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func (a balanceActivityBelongsToUser) Where(conds ...field.Expr) *balanceActivityBelongsToUser {
|
||||||
if len(conds) == 0 {
|
if len(conds) == 0 {
|
||||||
return &a
|
return &a
|
||||||
@@ -542,6 +499,9 @@ type balanceActivityBelongsToBill struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -561,6 +521,15 @@ type balanceActivityBelongsToBill struct {
|
|||||||
Refund struct {
|
Refund struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
}
|
}
|
||||||
|
CouponUser struct {
|
||||||
|
field.RelationField
|
||||||
|
Coupon struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
User struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a balanceActivityBelongsToBill) Where(conds ...field.Expr) *balanceActivityBelongsToBill {
|
func (a balanceActivityBelongsToBill) Where(conds ...field.Expr) *balanceActivityBelongsToBill {
|
||||||
@@ -638,6 +607,87 @@ func (a balanceActivityBelongsToBillTx) Unscoped() *balanceActivityBelongsToBill
|
|||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type balanceActivityBelongsToAdmin struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdmin) Where(conds ...field.Expr) *balanceActivityBelongsToAdmin {
|
||||||
|
if len(conds) == 0 {
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
exprs := make([]clause.Expression, 0, len(conds))
|
||||||
|
for _, cond := range conds {
|
||||||
|
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||||
|
}
|
||||||
|
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdmin) WithContext(ctx context.Context) *balanceActivityBelongsToAdmin {
|
||||||
|
a.db = a.db.WithContext(ctx)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdmin) Session(session *gorm.Session) *balanceActivityBelongsToAdmin {
|
||||||
|
a.db = a.db.Session(session)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdmin) Model(m *models.BalanceActivity) *balanceActivityBelongsToAdminTx {
|
||||||
|
return &balanceActivityBelongsToAdminTx{a.db.Model(m).Association(a.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdmin) Unscoped() *balanceActivityBelongsToAdmin {
|
||||||
|
a.db = a.db.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type balanceActivityBelongsToAdminTx struct{ tx *gorm.Association }
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Find() (result *models.Admin, err error) {
|
||||||
|
return result, a.tx.Find(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Append(values ...*models.Admin) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Append(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Replace(values ...*models.Admin) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Replace(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Delete(values ...*models.Admin) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Delete(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Clear() error {
|
||||||
|
return a.tx.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Count() int64 {
|
||||||
|
return a.tx.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a balanceActivityBelongsToAdminTx) Unscoped() *balanceActivityBelongsToAdminTx {
|
||||||
|
a.tx = a.tx.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
type balanceActivityDo struct{ gen.DO }
|
type balanceActivityDo struct{ gen.DO }
|
||||||
|
|
||||||
func (b balanceActivityDo) Debug() *balanceActivityDo {
|
func (b balanceActivityDo) Debug() *balanceActivityDo {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
_bill.TradeID = field.NewInt32(tableName, "trade_id")
|
_bill.TradeID = field.NewInt32(tableName, "trade_id")
|
||||||
_bill.ResourceID = field.NewInt32(tableName, "resource_id")
|
_bill.ResourceID = field.NewInt32(tableName, "resource_id")
|
||||||
_bill.RefundID = field.NewInt32(tableName, "refund_id")
|
_bill.RefundID = field.NewInt32(tableName, "refund_id")
|
||||||
_bill.CouponID = field.NewInt32(tableName, "coupon_id")
|
_bill.CouponUserID = field.NewInt32(tableName, "coupon_user_id")
|
||||||
_bill.BillNo = field.NewString(tableName, "bill_no")
|
_bill.BillNo = field.NewString(tableName, "bill_no")
|
||||||
_bill.Info = field.NewString(tableName, "info")
|
_bill.Info = field.NewString(tableName, "info")
|
||||||
_bill.Type = field.NewInt(tableName, "type")
|
_bill.Type = field.NewInt(tableName, "type")
|
||||||
@@ -143,6 +143,9 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -154,6 +157,9 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -162,8 +168,16 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -198,6 +212,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
RelationField: field.NewRelation("Refund", "models.Refund"),
|
RelationField: field.NewRelation("Refund", "models.Refund"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_bill.CouponUser = billBelongsToCouponUser{
|
||||||
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
RelationField: field.NewRelation("CouponUser", "models.CouponUser"),
|
||||||
|
Coupon: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("CouponUser.Coupon", "models.Coupon"),
|
||||||
|
},
|
||||||
|
User: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("CouponUser.User", "models.User"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
_bill.fillFieldMap()
|
_bill.fillFieldMap()
|
||||||
|
|
||||||
return _bill
|
return _bill
|
||||||
@@ -206,22 +236,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
|
|||||||
type bill struct {
|
type bill struct {
|
||||||
billDo
|
billDo
|
||||||
|
|
||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32
|
ID field.Int32
|
||||||
CreatedAt field.Time
|
CreatedAt field.Time
|
||||||
UpdatedAt field.Time
|
UpdatedAt field.Time
|
||||||
DeletedAt field.Field
|
DeletedAt field.Field
|
||||||
UserID field.Int32
|
UserID field.Int32
|
||||||
TradeID field.Int32
|
TradeID field.Int32
|
||||||
ResourceID field.Int32
|
ResourceID field.Int32
|
||||||
RefundID field.Int32
|
RefundID field.Int32
|
||||||
CouponID field.Int32
|
CouponUserID field.Int32
|
||||||
BillNo field.String
|
BillNo field.String
|
||||||
Info field.String
|
Info field.String
|
||||||
Type field.Int
|
Type field.Int
|
||||||
Amount field.Field
|
Amount field.Field
|
||||||
Actual field.Field
|
Actual field.Field
|
||||||
User billBelongsToUser
|
User billBelongsToUser
|
||||||
|
|
||||||
Trade billBelongsToTrade
|
Trade billBelongsToTrade
|
||||||
|
|
||||||
@@ -229,6 +259,8 @@ type bill struct {
|
|||||||
|
|
||||||
Refund billBelongsToRefund
|
Refund billBelongsToRefund
|
||||||
|
|
||||||
|
CouponUser billBelongsToCouponUser
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +284,7 @@ func (b *bill) updateTableName(table string) *bill {
|
|||||||
b.TradeID = field.NewInt32(table, "trade_id")
|
b.TradeID = field.NewInt32(table, "trade_id")
|
||||||
b.ResourceID = field.NewInt32(table, "resource_id")
|
b.ResourceID = field.NewInt32(table, "resource_id")
|
||||||
b.RefundID = field.NewInt32(table, "refund_id")
|
b.RefundID = field.NewInt32(table, "refund_id")
|
||||||
b.CouponID = field.NewInt32(table, "coupon_id")
|
b.CouponUserID = field.NewInt32(table, "coupon_user_id")
|
||||||
b.BillNo = field.NewString(table, "bill_no")
|
b.BillNo = field.NewString(table, "bill_no")
|
||||||
b.Info = field.NewString(table, "info")
|
b.Info = field.NewString(table, "info")
|
||||||
b.Type = field.NewInt(table, "type")
|
b.Type = field.NewInt(table, "type")
|
||||||
@@ -274,7 +306,7 @@ func (b *bill) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *bill) fillFieldMap() {
|
func (b *bill) fillFieldMap() {
|
||||||
b.fieldMap = make(map[string]field.Expr, 18)
|
b.fieldMap = make(map[string]field.Expr, 19)
|
||||||
b.fieldMap["id"] = b.ID
|
b.fieldMap["id"] = b.ID
|
||||||
b.fieldMap["created_at"] = b.CreatedAt
|
b.fieldMap["created_at"] = b.CreatedAt
|
||||||
b.fieldMap["updated_at"] = b.UpdatedAt
|
b.fieldMap["updated_at"] = b.UpdatedAt
|
||||||
@@ -283,7 +315,7 @@ func (b *bill) fillFieldMap() {
|
|||||||
b.fieldMap["trade_id"] = b.TradeID
|
b.fieldMap["trade_id"] = b.TradeID
|
||||||
b.fieldMap["resource_id"] = b.ResourceID
|
b.fieldMap["resource_id"] = b.ResourceID
|
||||||
b.fieldMap["refund_id"] = b.RefundID
|
b.fieldMap["refund_id"] = b.RefundID
|
||||||
b.fieldMap["coupon_id"] = b.CouponID
|
b.fieldMap["coupon_user_id"] = b.CouponUserID
|
||||||
b.fieldMap["bill_no"] = b.BillNo
|
b.fieldMap["bill_no"] = b.BillNo
|
||||||
b.fieldMap["info"] = b.Info
|
b.fieldMap["info"] = b.Info
|
||||||
b.fieldMap["type"] = b.Type
|
b.fieldMap["type"] = b.Type
|
||||||
@@ -302,6 +334,8 @@ func (b bill) clone(db *gorm.DB) bill {
|
|||||||
b.Resource.db.Statement.ConnPool = db.Statement.ConnPool
|
b.Resource.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
b.Refund.db = db.Session(&gorm.Session{Initialized: true})
|
b.Refund.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
b.Refund.db.Statement.ConnPool = db.Statement.ConnPool
|
b.Refund.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
|
b.CouponUser.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
|
b.CouponUser.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +345,7 @@ func (b bill) replaceDB(db *gorm.DB) bill {
|
|||||||
b.Trade.db = db.Session(&gorm.Session{})
|
b.Trade.db = db.Session(&gorm.Session{})
|
||||||
b.Resource.db = db.Session(&gorm.Session{})
|
b.Resource.db = db.Session(&gorm.Session{})
|
||||||
b.Refund.db = db.Session(&gorm.Session{})
|
b.Refund.db = db.Session(&gorm.Session{})
|
||||||
|
b.CouponUser.db = db.Session(&gorm.Session{})
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,6 +554,9 @@ type billBelongsToResource struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -692,6 +730,94 @@ func (a billBelongsToRefundTx) Unscoped() *billBelongsToRefundTx {
|
|||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type billBelongsToCouponUser struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
field.RelationField
|
||||||
|
|
||||||
|
Coupon struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
User struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUser) Where(conds ...field.Expr) *billBelongsToCouponUser {
|
||||||
|
if len(conds) == 0 {
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
exprs := make([]clause.Expression, 0, len(conds))
|
||||||
|
for _, cond := range conds {
|
||||||
|
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||||
|
}
|
||||||
|
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUser) WithContext(ctx context.Context) *billBelongsToCouponUser {
|
||||||
|
a.db = a.db.WithContext(ctx)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUser) Session(session *gorm.Session) *billBelongsToCouponUser {
|
||||||
|
a.db = a.db.Session(session)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUser) Model(m *models.Bill) *billBelongsToCouponUserTx {
|
||||||
|
return &billBelongsToCouponUserTx{a.db.Model(m).Association(a.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUser) Unscoped() *billBelongsToCouponUser {
|
||||||
|
a.db = a.db.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type billBelongsToCouponUserTx struct{ tx *gorm.Association }
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Find() (result *models.CouponUser, err error) {
|
||||||
|
return result, a.tx.Find(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Append(values ...*models.CouponUser) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Append(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Replace(values ...*models.CouponUser) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Replace(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Delete(values ...*models.CouponUser) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Delete(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Clear() error {
|
||||||
|
return a.tx.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Count() int64 {
|
||||||
|
return a.tx.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a billBelongsToCouponUserTx) Unscoped() *billBelongsToCouponUserTx {
|
||||||
|
a.tx = a.tx.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
type billDo struct{ gen.DO }
|
type billDo struct{ gen.DO }
|
||||||
|
|
||||||
func (b billDo) Debug() *billDo {
|
func (b billDo) Debug() *billDo {
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -149,6 +152,9 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -157,8 +163,16 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
|
|||||||
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -490,6 +504,9 @@ type channelBelongsToResource struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ func newCoupon(db *gorm.DB, opts ...gen.DOOption) coupon {
|
|||||||
_coupon.CreatedAt = field.NewTime(tableName, "created_at")
|
_coupon.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_coupon.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_coupon.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_coupon.DeletedAt = field.NewField(tableName, "deleted_at")
|
_coupon.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||||
_coupon.UserID = field.NewInt32(tableName, "user_id")
|
_coupon.Name = field.NewString(tableName, "name")
|
||||||
_coupon.Code = field.NewString(tableName, "code")
|
|
||||||
_coupon.Remark = field.NewString(tableName, "remark")
|
|
||||||
_coupon.Amount = field.NewField(tableName, "amount")
|
_coupon.Amount = field.NewField(tableName, "amount")
|
||||||
_coupon.MinAmount = field.NewField(tableName, "min_amount")
|
_coupon.MinAmount = field.NewField(tableName, "min_amount")
|
||||||
|
_coupon.Count_ = field.NewInt32(tableName, "count")
|
||||||
_coupon.Status = field.NewInt(tableName, "status")
|
_coupon.Status = field.NewInt(tableName, "status")
|
||||||
|
_coupon.ExpireType = field.NewInt(tableName, "expire_type")
|
||||||
_coupon.ExpireAt = field.NewTime(tableName, "expire_at")
|
_coupon.ExpireAt = field.NewTime(tableName, "expire_at")
|
||||||
|
_coupon.ExpireIn = field.NewInt(tableName, "expire_in")
|
||||||
|
|
||||||
_coupon.fillFieldMap()
|
_coupon.fillFieldMap()
|
||||||
|
|
||||||
@@ -47,18 +48,19 @@ func newCoupon(db *gorm.DB, opts ...gen.DOOption) coupon {
|
|||||||
type coupon struct {
|
type coupon struct {
|
||||||
couponDo
|
couponDo
|
||||||
|
|
||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
ID field.Int32
|
ID field.Int32
|
||||||
CreatedAt field.Time
|
CreatedAt field.Time
|
||||||
UpdatedAt field.Time
|
UpdatedAt field.Time
|
||||||
DeletedAt field.Field
|
DeletedAt field.Field
|
||||||
UserID field.Int32
|
Name field.String
|
||||||
Code field.String
|
Amount field.Field
|
||||||
Remark field.String
|
MinAmount field.Field
|
||||||
Amount field.Field
|
Count_ field.Int32
|
||||||
MinAmount field.Field
|
Status field.Int
|
||||||
Status field.Int
|
ExpireType field.Int
|
||||||
ExpireAt field.Time
|
ExpireAt field.Time
|
||||||
|
ExpireIn field.Int
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@@ -79,13 +81,14 @@ func (c *coupon) updateTableName(table string) *coupon {
|
|||||||
c.CreatedAt = field.NewTime(table, "created_at")
|
c.CreatedAt = field.NewTime(table, "created_at")
|
||||||
c.UpdatedAt = field.NewTime(table, "updated_at")
|
c.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
c.DeletedAt = field.NewField(table, "deleted_at")
|
c.DeletedAt = field.NewField(table, "deleted_at")
|
||||||
c.UserID = field.NewInt32(table, "user_id")
|
c.Name = field.NewString(table, "name")
|
||||||
c.Code = field.NewString(table, "code")
|
|
||||||
c.Remark = field.NewString(table, "remark")
|
|
||||||
c.Amount = field.NewField(table, "amount")
|
c.Amount = field.NewField(table, "amount")
|
||||||
c.MinAmount = field.NewField(table, "min_amount")
|
c.MinAmount = field.NewField(table, "min_amount")
|
||||||
|
c.Count_ = field.NewInt32(table, "count")
|
||||||
c.Status = field.NewInt(table, "status")
|
c.Status = field.NewInt(table, "status")
|
||||||
|
c.ExpireType = field.NewInt(table, "expire_type")
|
||||||
c.ExpireAt = field.NewTime(table, "expire_at")
|
c.ExpireAt = field.NewTime(table, "expire_at")
|
||||||
|
c.ExpireIn = field.NewInt(table, "expire_in")
|
||||||
|
|
||||||
c.fillFieldMap()
|
c.fillFieldMap()
|
||||||
|
|
||||||
@@ -102,18 +105,19 @@ func (c *coupon) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *coupon) fillFieldMap() {
|
func (c *coupon) fillFieldMap() {
|
||||||
c.fieldMap = make(map[string]field.Expr, 11)
|
c.fieldMap = make(map[string]field.Expr, 12)
|
||||||
c.fieldMap["id"] = c.ID
|
c.fieldMap["id"] = c.ID
|
||||||
c.fieldMap["created_at"] = c.CreatedAt
|
c.fieldMap["created_at"] = c.CreatedAt
|
||||||
c.fieldMap["updated_at"] = c.UpdatedAt
|
c.fieldMap["updated_at"] = c.UpdatedAt
|
||||||
c.fieldMap["deleted_at"] = c.DeletedAt
|
c.fieldMap["deleted_at"] = c.DeletedAt
|
||||||
c.fieldMap["user_id"] = c.UserID
|
c.fieldMap["name"] = c.Name
|
||||||
c.fieldMap["code"] = c.Code
|
|
||||||
c.fieldMap["remark"] = c.Remark
|
|
||||||
c.fieldMap["amount"] = c.Amount
|
c.fieldMap["amount"] = c.Amount
|
||||||
c.fieldMap["min_amount"] = c.MinAmount
|
c.fieldMap["min_amount"] = c.MinAmount
|
||||||
|
c.fieldMap["count"] = c.Count_
|
||||||
c.fieldMap["status"] = c.Status
|
c.fieldMap["status"] = c.Status
|
||||||
|
c.fieldMap["expire_type"] = c.ExpireType
|
||||||
c.fieldMap["expire_at"] = c.ExpireAt
|
c.fieldMap["expire_at"] = c.ExpireAt
|
||||||
|
c.fieldMap["expire_in"] = c.ExpireIn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c coupon) clone(db *gorm.DB) coupon {
|
func (c coupon) clone(db *gorm.DB) coupon {
|
||||||
|
|||||||
621
web/queries/coupon_user.gen.go
Normal file
621
web/queries/coupon_user.gen.go
Normal file
@@ -0,0 +1,621 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
|
|
||||||
|
"gorm.io/gen"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
|
||||||
|
"gorm.io/plugin/dbresolver"
|
||||||
|
|
||||||
|
"platform/web/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCouponUser(db *gorm.DB, opts ...gen.DOOption) couponUser {
|
||||||
|
_couponUser := couponUser{}
|
||||||
|
|
||||||
|
_couponUser.couponUserDo.UseDB(db, opts...)
|
||||||
|
_couponUser.couponUserDo.UseModel(&models.CouponUser{})
|
||||||
|
|
||||||
|
tableName := _couponUser.couponUserDo.TableName()
|
||||||
|
_couponUser.ALL = field.NewAsterisk(tableName)
|
||||||
|
_couponUser.ID = field.NewInt32(tableName, "id")
|
||||||
|
_couponUser.CouponID = field.NewInt32(tableName, "coupon_id")
|
||||||
|
_couponUser.UserID = field.NewInt32(tableName, "user_id")
|
||||||
|
_couponUser.Status = field.NewInt(tableName, "status")
|
||||||
|
_couponUser.ExpireAt = field.NewTime(tableName, "expire_at")
|
||||||
|
_couponUser.UsedAt = field.NewTime(tableName, "used_at")
|
||||||
|
_couponUser.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
|
_couponUser.Coupon = couponUserBelongsToCoupon{
|
||||||
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
RelationField: field.NewRelation("Coupon", "models.Coupon"),
|
||||||
|
}
|
||||||
|
|
||||||
|
_couponUser.User = couponUserBelongsToUser{
|
||||||
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
RelationField: field.NewRelation("User", "models.User"),
|
||||||
|
Admin: struct {
|
||||||
|
field.RelationField
|
||||||
|
Roles struct {
|
||||||
|
field.RelationField
|
||||||
|
Permissions struct {
|
||||||
|
field.RelationField
|
||||||
|
Parent struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Children struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Admin", "models.Admin"),
|
||||||
|
Roles: struct {
|
||||||
|
field.RelationField
|
||||||
|
Permissions struct {
|
||||||
|
field.RelationField
|
||||||
|
Parent struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Children struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
|
||||||
|
Permissions: struct {
|
||||||
|
field.RelationField
|
||||||
|
Parent struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Children struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
|
||||||
|
Parent: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
|
||||||
|
},
|
||||||
|
Children: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Discount: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
|
||||||
|
},
|
||||||
|
Roles: struct {
|
||||||
|
field.RelationField
|
||||||
|
Permissions struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
|
||||||
|
Permissions: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_couponUser.fillFieldMap()
|
||||||
|
|
||||||
|
return _couponUser
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUser struct {
|
||||||
|
couponUserDo
|
||||||
|
|
||||||
|
ALL field.Asterisk
|
||||||
|
ID field.Int32
|
||||||
|
CouponID field.Int32
|
||||||
|
UserID field.Int32
|
||||||
|
Status field.Int
|
||||||
|
ExpireAt field.Time
|
||||||
|
UsedAt field.Time
|
||||||
|
CreatedAt field.Time
|
||||||
|
Coupon couponUserBelongsToCoupon
|
||||||
|
|
||||||
|
User couponUserBelongsToUser
|
||||||
|
|
||||||
|
fieldMap map[string]field.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUser) Table(newTableName string) *couponUser {
|
||||||
|
c.couponUserDo.UseTable(newTableName)
|
||||||
|
return c.updateTableName(newTableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUser) As(alias string) *couponUser {
|
||||||
|
c.couponUserDo.DO = *(c.couponUserDo.As(alias).(*gen.DO))
|
||||||
|
return c.updateTableName(alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *couponUser) updateTableName(table string) *couponUser {
|
||||||
|
c.ALL = field.NewAsterisk(table)
|
||||||
|
c.ID = field.NewInt32(table, "id")
|
||||||
|
c.CouponID = field.NewInt32(table, "coupon_id")
|
||||||
|
c.UserID = field.NewInt32(table, "user_id")
|
||||||
|
c.Status = field.NewInt(table, "status")
|
||||||
|
c.ExpireAt = field.NewTime(table, "expire_at")
|
||||||
|
c.UsedAt = field.NewTime(table, "used_at")
|
||||||
|
c.CreatedAt = field.NewTime(table, "created_at")
|
||||||
|
|
||||||
|
c.fillFieldMap()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *couponUser) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||||
|
_f, ok := c.fieldMap[fieldName]
|
||||||
|
if !ok || _f == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
_oe, ok := _f.(field.OrderExpr)
|
||||||
|
return _oe, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *couponUser) fillFieldMap() {
|
||||||
|
c.fieldMap = make(map[string]field.Expr, 9)
|
||||||
|
c.fieldMap["id"] = c.ID
|
||||||
|
c.fieldMap["coupon_id"] = c.CouponID
|
||||||
|
c.fieldMap["user_id"] = c.UserID
|
||||||
|
c.fieldMap["status"] = c.Status
|
||||||
|
c.fieldMap["expire_at"] = c.ExpireAt
|
||||||
|
c.fieldMap["used_at"] = c.UsedAt
|
||||||
|
c.fieldMap["created_at"] = c.CreatedAt
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUser) clone(db *gorm.DB) couponUser {
|
||||||
|
c.couponUserDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||||
|
c.Coupon.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
|
c.Coupon.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
|
c.User.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
|
c.User.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUser) replaceDB(db *gorm.DB) couponUser {
|
||||||
|
c.couponUserDo.ReplaceDB(db)
|
||||||
|
c.Coupon.db = db.Session(&gorm.Session{})
|
||||||
|
c.User.db = db.Session(&gorm.Session{})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUserBelongsToCoupon struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCoupon) Where(conds ...field.Expr) *couponUserBelongsToCoupon {
|
||||||
|
if len(conds) == 0 {
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
exprs := make([]clause.Expression, 0, len(conds))
|
||||||
|
for _, cond := range conds {
|
||||||
|
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||||
|
}
|
||||||
|
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCoupon) WithContext(ctx context.Context) *couponUserBelongsToCoupon {
|
||||||
|
a.db = a.db.WithContext(ctx)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCoupon) Session(session *gorm.Session) *couponUserBelongsToCoupon {
|
||||||
|
a.db = a.db.Session(session)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCoupon) Model(m *models.CouponUser) *couponUserBelongsToCouponTx {
|
||||||
|
return &couponUserBelongsToCouponTx{a.db.Model(m).Association(a.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCoupon) Unscoped() *couponUserBelongsToCoupon {
|
||||||
|
a.db = a.db.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUserBelongsToCouponTx struct{ tx *gorm.Association }
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Find() (result *models.Coupon, err error) {
|
||||||
|
return result, a.tx.Find(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Append(values ...*models.Coupon) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Append(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Replace(values ...*models.Coupon) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Replace(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Delete(values ...*models.Coupon) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Delete(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Clear() error {
|
||||||
|
return a.tx.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Count() int64 {
|
||||||
|
return a.tx.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToCouponTx) Unscoped() *couponUserBelongsToCouponTx {
|
||||||
|
a.tx = a.tx.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUserBelongsToUser struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
field.RelationField
|
||||||
|
|
||||||
|
Admin struct {
|
||||||
|
field.RelationField
|
||||||
|
Roles struct {
|
||||||
|
field.RelationField
|
||||||
|
Permissions struct {
|
||||||
|
field.RelationField
|
||||||
|
Parent struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Children struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Discount struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Roles struct {
|
||||||
|
field.RelationField
|
||||||
|
Permissions struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUser) Where(conds ...field.Expr) *couponUserBelongsToUser {
|
||||||
|
if len(conds) == 0 {
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
exprs := make([]clause.Expression, 0, len(conds))
|
||||||
|
for _, cond := range conds {
|
||||||
|
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||||
|
}
|
||||||
|
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUser) WithContext(ctx context.Context) *couponUserBelongsToUser {
|
||||||
|
a.db = a.db.WithContext(ctx)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUser) Session(session *gorm.Session) *couponUserBelongsToUser {
|
||||||
|
a.db = a.db.Session(session)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUser) Model(m *models.CouponUser) *couponUserBelongsToUserTx {
|
||||||
|
return &couponUserBelongsToUserTx{a.db.Model(m).Association(a.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUser) Unscoped() *couponUserBelongsToUser {
|
||||||
|
a.db = a.db.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUserBelongsToUserTx struct{ tx *gorm.Association }
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Find() (result *models.User, err error) {
|
||||||
|
return result, a.tx.Find(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Append(values ...*models.User) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Append(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Replace(values ...*models.User) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Replace(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Delete(values ...*models.User) (err error) {
|
||||||
|
targetValues := make([]interface{}, len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
targetValues[i] = v
|
||||||
|
}
|
||||||
|
return a.tx.Delete(targetValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Clear() error {
|
||||||
|
return a.tx.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Count() int64 {
|
||||||
|
return a.tx.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a couponUserBelongsToUserTx) Unscoped() *couponUserBelongsToUserTx {
|
||||||
|
a.tx = a.tx.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type couponUserDo struct{ gen.DO }
|
||||||
|
|
||||||
|
func (c couponUserDo) Debug() *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Debug())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) WithContext(ctx context.Context) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) ReadDB() *couponUserDo {
|
||||||
|
return c.Clauses(dbresolver.Read)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) WriteDB() *couponUserDo {
|
||||||
|
return c.Clauses(dbresolver.Write)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Session(config *gorm.Session) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Session(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Clauses(conds ...clause.Expression) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Clauses(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Returning(value interface{}, columns ...string) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Returning(value, columns...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Not(conds ...gen.Condition) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Not(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Or(conds ...gen.Condition) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Or(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Select(conds ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Select(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Where(conds ...gen.Condition) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Where(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Order(conds ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Order(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Distinct(cols ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Distinct(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Omit(cols ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Omit(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Join(table schema.Tabler, on ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Join(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) LeftJoin(table schema.Tabler, on ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.LeftJoin(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) RightJoin(table schema.Tabler, on ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.RightJoin(table, on...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Group(cols ...field.Expr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Group(cols...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Having(conds ...gen.Condition) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Having(conds...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Limit(limit int) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Limit(limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Offset(offset int) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Offset(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Scopes(funcs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Unscoped() *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Unscoped())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Create(values ...*models.CouponUser) error {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.DO.Create(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) CreateInBatches(values []*models.CouponUser, batchSize int) error {
|
||||||
|
return c.DO.CreateInBatches(values, batchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save : !!! underlying implementation is different with GORM
|
||||||
|
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||||
|
func (c couponUserDo) Save(values ...*models.CouponUser) error {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.DO.Save(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) First() (*models.CouponUser, error) {
|
||||||
|
if result, err := c.DO.First(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*models.CouponUser), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Take() (*models.CouponUser, error) {
|
||||||
|
if result, err := c.DO.Take(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*models.CouponUser), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Last() (*models.CouponUser, error) {
|
||||||
|
if result, err := c.DO.Last(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*models.CouponUser), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Find() ([]*models.CouponUser, error) {
|
||||||
|
result, err := c.DO.Find()
|
||||||
|
return result.([]*models.CouponUser), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.CouponUser, err error) {
|
||||||
|
buf := make([]*models.CouponUser, 0, batchSize)
|
||||||
|
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||||
|
defer func() { results = append(results, buf...) }()
|
||||||
|
return fc(tx, batch)
|
||||||
|
})
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) FindInBatches(result *[]*models.CouponUser, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||||
|
return c.DO.FindInBatches(result, batchSize, fc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Attrs(attrs ...field.AssignExpr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Attrs(attrs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Assign(attrs ...field.AssignExpr) *couponUserDo {
|
||||||
|
return c.withDO(c.DO.Assign(attrs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Joins(fields ...field.RelationField) *couponUserDo {
|
||||||
|
for _, _f := range fields {
|
||||||
|
c = *c.withDO(c.DO.Joins(_f))
|
||||||
|
}
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Preload(fields ...field.RelationField) *couponUserDo {
|
||||||
|
for _, _f := range fields {
|
||||||
|
c = *c.withDO(c.DO.Preload(_f))
|
||||||
|
}
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) FirstOrInit() (*models.CouponUser, error) {
|
||||||
|
if result, err := c.DO.FirstOrInit(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*models.CouponUser), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) FirstOrCreate() (*models.CouponUser, error) {
|
||||||
|
if result, err := c.DO.FirstOrCreate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return result.(*models.CouponUser), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) FindByPage(offset int, limit int) (result []*models.CouponUser, count int64, err error) {
|
||||||
|
result, err = c.Offset(offset).Limit(limit).Find()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||||
|
count = int64(size + offset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err = c.Offset(-1).Limit(-1).Count()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||||
|
count, err = c.Count()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Offset(offset).Limit(limit).Scan(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Scan(result interface{}) (err error) {
|
||||||
|
return c.DO.Scan(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c couponUserDo) Delete(models ...*models.CouponUser) (result gen.ResultInfo, err error) {
|
||||||
|
return c.DO.Delete(models)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *couponUserDo) withDO(do gen.Dao) *couponUserDo {
|
||||||
|
c.DO = *do.(*gen.DO)
|
||||||
|
return c
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ var (
|
|||||||
Channel *channel
|
Channel *channel
|
||||||
Client *client
|
Client *client
|
||||||
Coupon *coupon
|
Coupon *coupon
|
||||||
|
CouponUser *couponUser
|
||||||
Edge *edge
|
Edge *edge
|
||||||
Inquiry *inquiry
|
Inquiry *inquiry
|
||||||
LinkAdminRole *linkAdminRole
|
LinkAdminRole *linkAdminRole
|
||||||
@@ -63,6 +64,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
|||||||
Channel = &Q.Channel
|
Channel = &Q.Channel
|
||||||
Client = &Q.Client
|
Client = &Q.Client
|
||||||
Coupon = &Q.Coupon
|
Coupon = &Q.Coupon
|
||||||
|
CouponUser = &Q.CouponUser
|
||||||
Edge = &Q.Edge
|
Edge = &Q.Edge
|
||||||
Inquiry = &Q.Inquiry
|
Inquiry = &Q.Inquiry
|
||||||
LinkAdminRole = &Q.LinkAdminRole
|
LinkAdminRole = &Q.LinkAdminRole
|
||||||
@@ -102,6 +104,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
|||||||
Channel: newChannel(db, opts...),
|
Channel: newChannel(db, opts...),
|
||||||
Client: newClient(db, opts...),
|
Client: newClient(db, opts...),
|
||||||
Coupon: newCoupon(db, opts...),
|
Coupon: newCoupon(db, opts...),
|
||||||
|
CouponUser: newCouponUser(db, opts...),
|
||||||
Edge: newEdge(db, opts...),
|
Edge: newEdge(db, opts...),
|
||||||
Inquiry: newInquiry(db, opts...),
|
Inquiry: newInquiry(db, opts...),
|
||||||
LinkAdminRole: newLinkAdminRole(db, opts...),
|
LinkAdminRole: newLinkAdminRole(db, opts...),
|
||||||
@@ -142,6 +145,7 @@ type Query struct {
|
|||||||
Channel channel
|
Channel channel
|
||||||
Client client
|
Client client
|
||||||
Coupon coupon
|
Coupon coupon
|
||||||
|
CouponUser couponUser
|
||||||
Edge edge
|
Edge edge
|
||||||
Inquiry inquiry
|
Inquiry inquiry
|
||||||
LinkAdminRole linkAdminRole
|
LinkAdminRole linkAdminRole
|
||||||
@@ -183,6 +187,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
|||||||
Channel: q.Channel.clone(db),
|
Channel: q.Channel.clone(db),
|
||||||
Client: q.Client.clone(db),
|
Client: q.Client.clone(db),
|
||||||
Coupon: q.Coupon.clone(db),
|
Coupon: q.Coupon.clone(db),
|
||||||
|
CouponUser: q.CouponUser.clone(db),
|
||||||
Edge: q.Edge.clone(db),
|
Edge: q.Edge.clone(db),
|
||||||
Inquiry: q.Inquiry.clone(db),
|
Inquiry: q.Inquiry.clone(db),
|
||||||
LinkAdminRole: q.LinkAdminRole.clone(db),
|
LinkAdminRole: q.LinkAdminRole.clone(db),
|
||||||
@@ -231,6 +236,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
|||||||
Channel: q.Channel.replaceDB(db),
|
Channel: q.Channel.replaceDB(db),
|
||||||
Client: q.Client.replaceDB(db),
|
Client: q.Client.replaceDB(db),
|
||||||
Coupon: q.Coupon.replaceDB(db),
|
Coupon: q.Coupon.replaceDB(db),
|
||||||
|
CouponUser: q.CouponUser.replaceDB(db),
|
||||||
Edge: q.Edge.replaceDB(db),
|
Edge: q.Edge.replaceDB(db),
|
||||||
Inquiry: q.Inquiry.replaceDB(db),
|
Inquiry: q.Inquiry.replaceDB(db),
|
||||||
LinkAdminRole: q.LinkAdminRole.replaceDB(db),
|
LinkAdminRole: q.LinkAdminRole.replaceDB(db),
|
||||||
@@ -269,6 +275,7 @@ type queryCtx struct {
|
|||||||
Channel *channelDo
|
Channel *channelDo
|
||||||
Client *clientDo
|
Client *clientDo
|
||||||
Coupon *couponDo
|
Coupon *couponDo
|
||||||
|
CouponUser *couponUserDo
|
||||||
Edge *edgeDo
|
Edge *edgeDo
|
||||||
Inquiry *inquiryDo
|
Inquiry *inquiryDo
|
||||||
LinkAdminRole *linkAdminRoleDo
|
LinkAdminRole *linkAdminRoleDo
|
||||||
@@ -307,6 +314,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
|||||||
Channel: q.Channel.WithContext(ctx),
|
Channel: q.Channel.WithContext(ctx),
|
||||||
Client: q.Client.WithContext(ctx),
|
Client: q.Client.WithContext(ctx),
|
||||||
Coupon: q.Coupon.WithContext(ctx),
|
Coupon: q.Coupon.WithContext(ctx),
|
||||||
|
CouponUser: q.CouponUser.WithContext(ctx),
|
||||||
Edge: q.Edge.WithContext(ctx),
|
Edge: q.Edge.WithContext(ctx),
|
||||||
Inquiry: q.Inquiry.WithContext(ctx),
|
Inquiry: q.Inquiry.WithContext(ctx),
|
||||||
LinkAdminRole: q.LinkAdminRole.WithContext(ctx),
|
LinkAdminRole: q.LinkAdminRole.WithContext(ctx),
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -139,6 +142,9 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -147,8 +153,16 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
|
|||||||
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -391,6 +405,9 @@ type logsUserUsageBelongsToResource struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -36,6 +36,29 @@ func newProduct(db *gorm.DB, opts ...gen.DOOption) product {
|
|||||||
_product.Description = field.NewString(tableName, "description")
|
_product.Description = field.NewString(tableName, "description")
|
||||||
_product.Sort = field.NewInt32(tableName, "sort")
|
_product.Sort = field.NewInt32(tableName, "sort")
|
||||||
_product.Status = field.NewInt(tableName, "status")
|
_product.Status = field.NewInt(tableName, "status")
|
||||||
|
_product.Skus = productHasManySkus{
|
||||||
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
|
RelationField: field.NewRelation("Skus", "models.ProductSku"),
|
||||||
|
Product: struct {
|
||||||
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Skus.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Skus.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Discount: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Skus.Discount", "models.ProductDiscount"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
_product.fillFieldMap()
|
_product.fillFieldMap()
|
||||||
|
|
||||||
@@ -55,6 +78,7 @@ type product struct {
|
|||||||
Description field.String
|
Description field.String
|
||||||
Sort field.Int32
|
Sort field.Int32
|
||||||
Status field.Int
|
Status field.Int
|
||||||
|
Skus productHasManySkus
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@@ -96,7 +120,7 @@ func (p *product) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *product) fillFieldMap() {
|
func (p *product) fillFieldMap() {
|
||||||
p.fieldMap = make(map[string]field.Expr, 9)
|
p.fieldMap = make(map[string]field.Expr, 10)
|
||||||
p.fieldMap["id"] = p.ID
|
p.fieldMap["id"] = p.ID
|
||||||
p.fieldMap["created_at"] = p.CreatedAt
|
p.fieldMap["created_at"] = p.CreatedAt
|
||||||
p.fieldMap["updated_at"] = p.UpdatedAt
|
p.fieldMap["updated_at"] = p.UpdatedAt
|
||||||
@@ -106,18 +130,113 @@ func (p *product) fillFieldMap() {
|
|||||||
p.fieldMap["description"] = p.Description
|
p.fieldMap["description"] = p.Description
|
||||||
p.fieldMap["sort"] = p.Sort
|
p.fieldMap["sort"] = p.Sort
|
||||||
p.fieldMap["status"] = p.Status
|
p.fieldMap["status"] = p.Status
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p product) clone(db *gorm.DB) product {
|
func (p product) clone(db *gorm.DB) product {
|
||||||
p.productDo.ReplaceConnPool(db.Statement.ConnPool)
|
p.productDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||||
|
p.Skus.db = db.Session(&gorm.Session{Initialized: true})
|
||||||
|
p.Skus.db.Statement.ConnPool = db.Statement.ConnPool
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p product) replaceDB(db *gorm.DB) product {
|
func (p product) replaceDB(db *gorm.DB) product {
|
||||||
p.productDo.ReplaceDB(db)
|
p.productDo.ReplaceDB(db)
|
||||||
|
p.Skus.db = db.Session(&gorm.Session{})
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type productHasManySkus struct {
|
||||||
|
db *gorm.DB
|
||||||
|
|
||||||
|
field.RelationField
|
||||||
|
|
||||||
|
Product struct {
|
||||||
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Discount struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkus) Where(conds ...field.Expr) *productHasManySkus {
|
||||||
|
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 productHasManySkus) WithContext(ctx context.Context) *productHasManySkus {
|
||||||
|
a.db = a.db.WithContext(ctx)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkus) Session(session *gorm.Session) *productHasManySkus {
|
||||||
|
a.db = a.db.Session(session)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkus) Model(m *models.Product) *productHasManySkusTx {
|
||||||
|
return &productHasManySkusTx{a.db.Model(m).Association(a.Name())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkus) Unscoped() *productHasManySkus {
|
||||||
|
a.db = a.db.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type productHasManySkusTx struct{ tx *gorm.Association }
|
||||||
|
|
||||||
|
func (a productHasManySkusTx) Find() (result []*models.ProductSku, err error) {
|
||||||
|
return result, a.tx.Find(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkusTx) 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 productHasManySkusTx) 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 productHasManySkusTx) 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 productHasManySkusTx) Clear() error {
|
||||||
|
return a.tx.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkusTx) Count() int64 {
|
||||||
|
return a.tx.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a productHasManySkusTx) Unscoped() *productHasManySkusTx {
|
||||||
|
a.tx = a.tx.Unscoped()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
type productDo struct{ gen.DO }
|
type productDo struct{ gen.DO }
|
||||||
|
|
||||||
func (p productDo) Debug() *productDo {
|
func (p productDo) Debug() *productDo {
|
||||||
|
|||||||
@@ -36,10 +36,35 @@ func newProductSku(db *gorm.DB, opts ...gen.DOOption) productSku {
|
|||||||
_productSku.Code = field.NewString(tableName, "code")
|
_productSku.Code = field.NewString(tableName, "code")
|
||||||
_productSku.Name = field.NewString(tableName, "name")
|
_productSku.Name = field.NewString(tableName, "name")
|
||||||
_productSku.Price = field.NewField(tableName, "price")
|
_productSku.Price = field.NewField(tableName, "price")
|
||||||
|
_productSku.PriceMin = field.NewField(tableName, "price_min")
|
||||||
|
_productSku.Status = field.NewInt32(tableName, "status")
|
||||||
|
_productSku.Sort = field.NewInt32(tableName, "sort")
|
||||||
|
_productSku.CountMin = field.NewInt32(tableName, "count_min")
|
||||||
_productSku.Product = productSkuBelongsToProduct{
|
_productSku.Product = productSkuBelongsToProduct{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
RelationField: field.NewRelation("Product", "models.Product"),
|
RelationField: field.NewRelation("Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
Product struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Discount struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Product.Skus", "models.ProductSku"),
|
||||||
|
Product: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Product.Skus.Product", "models.Product"),
|
||||||
|
},
|
||||||
|
Discount: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Product.Skus.Discount", "models.ProductDiscount"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_productSku.Discount = productSkuBelongsToDiscount{
|
_productSku.Discount = productSkuBelongsToDiscount{
|
||||||
@@ -66,6 +91,10 @@ type productSku struct {
|
|||||||
Code field.String
|
Code field.String
|
||||||
Name field.String
|
Name field.String
|
||||||
Price field.Field
|
Price field.Field
|
||||||
|
PriceMin field.Field
|
||||||
|
Status field.Int32
|
||||||
|
Sort field.Int32
|
||||||
|
CountMin field.Int32
|
||||||
Product productSkuBelongsToProduct
|
Product productSkuBelongsToProduct
|
||||||
|
|
||||||
Discount productSkuBelongsToDiscount
|
Discount productSkuBelongsToDiscount
|
||||||
@@ -94,6 +123,10 @@ func (p *productSku) updateTableName(table string) *productSku {
|
|||||||
p.Code = field.NewString(table, "code")
|
p.Code = field.NewString(table, "code")
|
||||||
p.Name = field.NewString(table, "name")
|
p.Name = field.NewString(table, "name")
|
||||||
p.Price = field.NewField(table, "price")
|
p.Price = field.NewField(table, "price")
|
||||||
|
p.PriceMin = field.NewField(table, "price_min")
|
||||||
|
p.Status = field.NewInt32(table, "status")
|
||||||
|
p.Sort = field.NewInt32(table, "sort")
|
||||||
|
p.CountMin = field.NewInt32(table, "count_min")
|
||||||
|
|
||||||
p.fillFieldMap()
|
p.fillFieldMap()
|
||||||
|
|
||||||
@@ -110,7 +143,7 @@ func (p *productSku) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *productSku) fillFieldMap() {
|
func (p *productSku) fillFieldMap() {
|
||||||
p.fieldMap = make(map[string]field.Expr, 11)
|
p.fieldMap = make(map[string]field.Expr, 15)
|
||||||
p.fieldMap["id"] = p.ID
|
p.fieldMap["id"] = p.ID
|
||||||
p.fieldMap["created_at"] = p.CreatedAt
|
p.fieldMap["created_at"] = p.CreatedAt
|
||||||
p.fieldMap["updated_at"] = p.UpdatedAt
|
p.fieldMap["updated_at"] = p.UpdatedAt
|
||||||
@@ -120,6 +153,10 @@ func (p *productSku) fillFieldMap() {
|
|||||||
p.fieldMap["code"] = p.Code
|
p.fieldMap["code"] = p.Code
|
||||||
p.fieldMap["name"] = p.Name
|
p.fieldMap["name"] = p.Name
|
||||||
p.fieldMap["price"] = p.Price
|
p.fieldMap["price"] = p.Price
|
||||||
|
p.fieldMap["price_min"] = p.PriceMin
|
||||||
|
p.fieldMap["status"] = p.Status
|
||||||
|
p.fieldMap["sort"] = p.Sort
|
||||||
|
p.fieldMap["count_min"] = p.CountMin
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +180,16 @@ type productSkuBelongsToProduct struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
Product struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
Discount struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a productSkuBelongsToProduct) Where(conds ...field.Expr) *productSkuBelongsToProduct {
|
func (a productSkuBelongsToProduct) Where(conds ...field.Expr) *productSkuBelongsToProduct {
|
||||||
|
|||||||
@@ -115,8 +115,16 @@ func newProductSkuUser(db *gorm.DB, opts ...gen.DOOption) productSkuUser {
|
|||||||
RelationField: field.NewRelation("ProductSku", "models.ProductSku"),
|
RelationField: field.NewRelation("ProductSku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("ProductSku.Product", "models.Product"),
|
RelationField: field.NewRelation("ProductSku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("ProductSku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -331,6 +339,9 @@ type productSkuUserBelongsToProductSku struct {
|
|||||||
|
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -181,6 +184,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -192,6 +198,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -200,8 +209,16 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
|
|||||||
RelationField: field.NewRelation("Channels.Resource.Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Channels.Resource.Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Channels.Resource.Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Channels.Resource.Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Channels.Resource.Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -387,6 +404,9 @@ type proxyHasManyChannels struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
|
|||||||
_resource.Active = field.NewBool(tableName, "active")
|
_resource.Active = field.NewBool(tableName, "active")
|
||||||
_resource.Type = field.NewInt(tableName, "type")
|
_resource.Type = field.NewInt(tableName, "type")
|
||||||
_resource.Code = field.NewString(tableName, "code")
|
_resource.Code = field.NewString(tableName, "code")
|
||||||
|
_resource.CheckIP = field.NewBool(tableName, "checkip")
|
||||||
_resource.Short = resourceHasOneShort{
|
_resource.Short = resourceHasOneShort{
|
||||||
db: db.Session(&gorm.Session{}),
|
db: db.Session(&gorm.Session{}),
|
||||||
|
|
||||||
@@ -44,6 +45,9 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -52,8 +56,16 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
|
|||||||
RelationField: field.NewRelation("Short.Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Short.Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Short.Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Short.Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Short.Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -174,6 +186,7 @@ type resource struct {
|
|||||||
Active field.Bool
|
Active field.Bool
|
||||||
Type field.Int
|
Type field.Int
|
||||||
Code field.String
|
Code field.String
|
||||||
|
CheckIP field.Bool
|
||||||
Short resourceHasOneShort
|
Short resourceHasOneShort
|
||||||
|
|
||||||
Long resourceHasOneLong
|
Long resourceHasOneLong
|
||||||
@@ -206,6 +219,7 @@ func (r *resource) updateTableName(table string) *resource {
|
|||||||
r.Active = field.NewBool(table, "active")
|
r.Active = field.NewBool(table, "active")
|
||||||
r.Type = field.NewInt(table, "type")
|
r.Type = field.NewInt(table, "type")
|
||||||
r.Code = field.NewString(table, "code")
|
r.Code = field.NewString(table, "code")
|
||||||
|
r.CheckIP = field.NewBool(table, "checkip")
|
||||||
|
|
||||||
r.fillFieldMap()
|
r.fillFieldMap()
|
||||||
|
|
||||||
@@ -222,7 +236,7 @@ func (r *resource) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resource) fillFieldMap() {
|
func (r *resource) fillFieldMap() {
|
||||||
r.fieldMap = make(map[string]field.Expr, 13)
|
r.fieldMap = make(map[string]field.Expr, 14)
|
||||||
r.fieldMap["id"] = r.ID
|
r.fieldMap["id"] = r.ID
|
||||||
r.fieldMap["created_at"] = r.CreatedAt
|
r.fieldMap["created_at"] = r.CreatedAt
|
||||||
r.fieldMap["updated_at"] = r.UpdatedAt
|
r.fieldMap["updated_at"] = r.UpdatedAt
|
||||||
@@ -232,6 +246,7 @@ func (r *resource) fillFieldMap() {
|
|||||||
r.fieldMap["active"] = r.Active
|
r.fieldMap["active"] = r.Active
|
||||||
r.fieldMap["type"] = r.Type
|
r.fieldMap["type"] = r.Type
|
||||||
r.fieldMap["code"] = r.Code
|
r.fieldMap["code"] = r.Code
|
||||||
|
r.fieldMap["checkip"] = r.CheckIP
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +281,9 @@ type resourceHasOneShort struct {
|
|||||||
field.RelationField
|
field.RelationField
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -43,8 +43,16 @@ func newResourceLong(db *gorm.DB, opts ...gen.DOOption) resourceLong {
|
|||||||
RelationField: field.NewRelation("Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -149,6 +157,9 @@ type resourceLongHasOneSku struct {
|
|||||||
|
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -43,8 +43,16 @@ func newResourceShort(db *gorm.DB, opts ...gen.DOOption) resourceShort {
|
|||||||
RelationField: field.NewRelation("Sku", "models.ProductSku"),
|
RelationField: field.NewRelation("Sku", "models.ProductSku"),
|
||||||
Product: struct {
|
Product: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}{
|
}{
|
||||||
RelationField: field.NewRelation("Sku.Product", "models.Product"),
|
RelationField: field.NewRelation("Sku.Product", "models.Product"),
|
||||||
|
Skus: struct {
|
||||||
|
field.RelationField
|
||||||
|
}{
|
||||||
|
RelationField: field.NewRelation("Sku.Product.Skus", "models.ProductSku"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Discount: struct {
|
Discount: struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
@@ -149,6 +157,9 @@ type resourceShortHasOneSku struct {
|
|||||||
|
|
||||||
Product struct {
|
Product struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
Skus struct {
|
||||||
|
field.RelationField
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Discount struct {
|
Discount struct {
|
||||||
field.RelationField
|
field.RelationField
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package web
|
|||||||
import (
|
import (
|
||||||
"platform/pkg/env"
|
"platform/pkg/env"
|
||||||
auth2 "platform/web/auth"
|
auth2 "platform/web/auth"
|
||||||
|
"platform/web/core"
|
||||||
|
"platform/web/globals"
|
||||||
"platform/web/handlers"
|
"platform/web/handlers"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -25,15 +27,28 @@ func ApplyRouters(app *fiber.App) {
|
|||||||
if env.RunMode == env.RunModeDev {
|
if env.RunMode == env.RunModeDev {
|
||||||
debug := app.Group("/debug")
|
debug := app.Group("/debug")
|
||||||
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
|
debug.Get("/sms/:phone", handlers.DebugGetSmsCode)
|
||||||
debug.Get("/proxy/register", handlers.DebugRegisterProxyBaiYin)
|
|
||||||
debug.Get("/iden/clear/:phone", handlers.DebugIdentifyClear)
|
debug.Get("/iden/clear/:phone", handlers.DebugIdentifyClear)
|
||||||
debug.Get("/session/now", func(ctx *fiber.Ctx) error {
|
debug.Get("/session/now", func(ctx *fiber.Ctx) error {
|
||||||
rs, err := q.Session.Where(q.Session.AccessTokenExpires.Gt(time.Now())).Find()
|
rs, err := q.Session.Where(q.Session.AccessTokenExpires.Gt(time.Now().UTC())).Find()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ctx.JSON(rs)
|
return ctx.JSON(rs)
|
||||||
})
|
})
|
||||||
|
debug.Get("/test/err", func(ctx *fiber.Ctx) error {
|
||||||
|
return core.NewBizErr("测试错误")
|
||||||
|
})
|
||||||
|
|
||||||
|
debug.Get("/trade/status/:trade_no", func(ctx *fiber.Ctx) error {
|
||||||
|
tradeNo := ctx.Params("trade_no")
|
||||||
|
resp, err := globals.SFTPay.QueryTrade(&globals.QueryTradeReq{
|
||||||
|
MchOrderNo: &tradeNo,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ctx.JSON(resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +82,7 @@ func userRouter(api fiber.Router) {
|
|||||||
resource.Post("/list/short", handlers.PageResourceShort)
|
resource.Post("/list/short", handlers.PageResourceShort)
|
||||||
resource.Post("/list/long", handlers.PageResourceLong)
|
resource.Post("/list/long", handlers.PageResourceLong)
|
||||||
resource.Post("/create", handlers.CreateResource)
|
resource.Post("/create", handlers.CreateResource)
|
||||||
|
resource.Post("/update/checkip", handlers.UpdateResourceCheckIP)
|
||||||
|
|
||||||
resource.Post("/statistics/free", handlers.StatisticResourceFree)
|
resource.Post("/statistics/free", handlers.StatisticResourceFree)
|
||||||
resource.Post("/statistics/usage", handlers.StatisticResourceUsage)
|
resource.Post("/statistics/usage", handlers.StatisticResourceUsage)
|
||||||
@@ -79,6 +95,7 @@ func userRouter(api fiber.Router) {
|
|||||||
channel := api.Group("/channel")
|
channel := api.Group("/channel")
|
||||||
channel.Post("/list", handlers.ListChannel)
|
channel.Post("/list", handlers.ListChannel)
|
||||||
channel.Post("/create", handlers.CreateChannel)
|
channel.Post("/create", handlers.CreateChannel)
|
||||||
|
channel.Post("/create/v2", handlers.CreateChannelV2)
|
||||||
|
|
||||||
// 交易
|
// 交易
|
||||||
trade := api.Group("/trade")
|
trade := api.Group("/trade")
|
||||||
@@ -91,6 +108,15 @@ func userRouter(api fiber.Router) {
|
|||||||
bill := api.Group("/bill")
|
bill := api.Group("/bill")
|
||||||
bill.Post("/list", handlers.ListBill)
|
bill.Post("/list", handlers.ListBill)
|
||||||
|
|
||||||
|
// 余额变动
|
||||||
|
balance := api.Group("/balance")
|
||||||
|
balance.Post("/page", handlers.PageBalanceActivity)
|
||||||
|
|
||||||
|
// 已发放优惠券
|
||||||
|
couponUser := api.Group("/coupon-user")
|
||||||
|
couponUser.Post("/page", handlers.PageCouponUser)
|
||||||
|
couponUser.Post("/get", handlers.GetCouponUser)
|
||||||
|
|
||||||
// 公告
|
// 公告
|
||||||
announcement := api.Group("/announcement")
|
announcement := api.Group("/announcement")
|
||||||
announcement.Post("/list", handlers.ListAnnouncements)
|
announcement.Post("/list", handlers.ListAnnouncements)
|
||||||
@@ -109,6 +135,14 @@ func userRouter(api fiber.Router) {
|
|||||||
// 前台
|
// 前台
|
||||||
inquiry := api.Group("/inquiry")
|
inquiry := api.Group("/inquiry")
|
||||||
inquiry.Post("/create", handlers.CreateInquiry)
|
inquiry.Post("/create", handlers.CreateInquiry)
|
||||||
|
|
||||||
|
// 产品
|
||||||
|
product := api.Group("/product")
|
||||||
|
product.Post("/list", handlers.AllProduct)
|
||||||
|
|
||||||
|
// 认证
|
||||||
|
verify := api.Group("/verify")
|
||||||
|
verify.Post("/sms/password", handlers.SendSmsCodeForPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 客户端接口路由
|
// 客户端接口路由
|
||||||
@@ -116,7 +150,7 @@ func clientRouter(api fiber.Router) {
|
|||||||
client := api
|
client := api
|
||||||
|
|
||||||
// 验证短信令牌
|
// 验证短信令牌
|
||||||
client.Post("/verify/sms", handlers.SmsCode)
|
client.Post("/verify/sms", handlers.SendSmsCode)
|
||||||
|
|
||||||
// 套餐定价查询
|
// 套餐定价查询
|
||||||
resource := client.Group("/resource")
|
resource := client.Group("/resource")
|
||||||
@@ -126,9 +160,6 @@ func clientRouter(api fiber.Router) {
|
|||||||
channel := client.Group("/channel")
|
channel := client.Group("/channel")
|
||||||
channel.Post("/remove", handlers.RemoveChannels)
|
channel.Post("/remove", handlers.RemoveChannels)
|
||||||
|
|
||||||
// 代理网关注册
|
|
||||||
proxy := client.Group("/proxy")
|
|
||||||
proxy.Post("/register/baidyin", handlers.ProxyRegisterBaiYin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管理员接口路由
|
// 管理员接口路由
|
||||||
@@ -159,35 +190,60 @@ func adminRouter(api fiber.Router) {
|
|||||||
// user 用户
|
// user 用户
|
||||||
var user = api.Group("/user")
|
var user = api.Group("/user")
|
||||||
user.Post("/page", handlers.PageUserByAdmin)
|
user.Post("/page", handlers.PageUserByAdmin)
|
||||||
|
user.Post("/page/not-bind", handlers.PageUserNotBindByAdmin)
|
||||||
user.Post("/get", handlers.GetUserByAdmin)
|
user.Post("/get", handlers.GetUserByAdmin)
|
||||||
user.Post("/create", handlers.CreateUserByAdmin)
|
user.Post("/create", handlers.CreateUserByAdmin)
|
||||||
user.Post("/update", handlers.UpdateUserByAdmin)
|
user.Post("/update", handlers.UpdateUserByAdmin)
|
||||||
user.Post("/remove", handlers.RemoveUserByAdmin)
|
user.Post("/remove", handlers.RemoveUserByAdmin)
|
||||||
|
|
||||||
user.Post("/bind", handlers.BindAdmin)
|
user.Post("/update/bind", handlers.BindAdmin)
|
||||||
user.Post("/update/balance", handlers.UpdateUserBalanceByAdmin)
|
user.Post("/update/balance", handlers.UpdateUserBalanceByAdmin)
|
||||||
|
user.Post("/update/balance-inc", handlers.UpdateUserBalanceIncByAdmin)
|
||||||
|
user.Post("/update/balance-dec", handlers.UpdateUserBalanceDecByAdmin)
|
||||||
|
|
||||||
// resource 套餐
|
// resource 套餐
|
||||||
var resource = api.Group("/resource")
|
var resource = api.Group("/resource")
|
||||||
resource.Post("/short/page", handlers.PageResourceShortByAdmin)
|
resource.Post("/short/page", handlers.PageResourceShortByAdmin)
|
||||||
|
resource.Post("/short/page/of-user", handlers.PageResourceShortOfUserByAdmin)
|
||||||
resource.Post("/long/page", handlers.PageResourceLongByAdmin)
|
resource.Post("/long/page", handlers.PageResourceLongByAdmin)
|
||||||
|
resource.Post("/long/page/of-user", handlers.PageResourceLongOfUserByAdmin)
|
||||||
resource.Post("/update", handlers.UpdateResourceByAdmin)
|
resource.Post("/update", handlers.UpdateResourceByAdmin)
|
||||||
|
|
||||||
// batch 批次
|
// batch 批次
|
||||||
var batch = api.Group("/batch")
|
var batch = api.Group("/batch")
|
||||||
batch.Post("/page", handlers.PageBatchByAdmin)
|
batch.Post("/page", handlers.PageBatchByAdmin)
|
||||||
|
batch.Post("/page/of-user", handlers.PageBatchOfUserByAdmin)
|
||||||
|
|
||||||
// channel 通道
|
// channel 通道
|
||||||
var channel = api.Group("/channel")
|
var channel = api.Group("/channel")
|
||||||
channel.Post("/page", handlers.PageChannelByAdmin)
|
channel.Post("/page", handlers.PageChannelByAdmin)
|
||||||
|
channel.Post("/page/of-user", handlers.PageChannelOfUserByAdmin)
|
||||||
|
channel.Post("/sync/clear-expired", handlers.SyncChannelClearExpiredByAdmin)
|
||||||
|
|
||||||
|
// proxy 代理
|
||||||
|
var proxy = api.Group("/proxy")
|
||||||
|
proxy.Post("/all", handlers.AllProxyByAdmin)
|
||||||
|
proxy.Post("/page", handlers.PageProxyByAdmin)
|
||||||
|
proxy.Post("/create", handlers.CreateProxy)
|
||||||
|
proxy.Post("/update", handlers.UpdateProxy)
|
||||||
|
proxy.Post("/update/status", handlers.UpdateProxyStatus)
|
||||||
|
proxy.Post("/remove", handlers.RemoveProxy)
|
||||||
|
|
||||||
// trade 交易
|
// trade 交易
|
||||||
var trade = api.Group("/trade")
|
var trade = api.Group("/trade")
|
||||||
trade.Post("/page", handlers.PageTradeByAdmin)
|
trade.Post("/page", handlers.PageTradeByAdmin)
|
||||||
|
trade.Post("/page/of-user", handlers.PageTradeOfUserByAdmin)
|
||||||
|
trade.Post("/complete", handlers.TradeCompleteByAdmin)
|
||||||
|
|
||||||
// bill 账单
|
// bill 账单
|
||||||
var bill = api.Group("/bill")
|
var bill = api.Group("/bill")
|
||||||
bill.Post("/page", handlers.PageBillByAdmin)
|
bill.Post("/page", handlers.PageBillByAdmin)
|
||||||
|
bill.Post("/page/of-user", handlers.PageBillOfUserByAdmin)
|
||||||
|
|
||||||
|
// balance-activity 余额变动
|
||||||
|
var balanceActivity = api.Group("/balance-activity")
|
||||||
|
balanceActivity.Post("/page", handlers.PageBalanceActivityByAdmin)
|
||||||
|
balanceActivity.Post("/page/of-user", handlers.PageBalanceActivityOfUserByAdmin)
|
||||||
|
|
||||||
// product 产品
|
// product 产品
|
||||||
var product = api.Group("/product")
|
var product = api.Group("/product")
|
||||||
@@ -200,6 +256,7 @@ func adminRouter(api fiber.Router) {
|
|||||||
product.Post("/sku/page", handlers.PageProductSkuByAdmin)
|
product.Post("/sku/page", handlers.PageProductSkuByAdmin)
|
||||||
product.Post("/sku/create", handlers.CreateProductSku)
|
product.Post("/sku/create", handlers.CreateProductSku)
|
||||||
product.Post("/sku/update", handlers.UpdateProductSku)
|
product.Post("/sku/update", handlers.UpdateProductSku)
|
||||||
|
product.Post("/sku/update/status", handlers.UpdateProductStatusSku)
|
||||||
product.Post("/sku/remove", handlers.DeleteProductSku)
|
product.Post("/sku/remove", handlers.DeleteProductSku)
|
||||||
|
|
||||||
product.Post("/sku/update/discount/batch", handlers.BatchUpdateProductSkuDiscount)
|
product.Post("/sku/update/discount/batch", handlers.BatchUpdateProductSkuDiscount)
|
||||||
@@ -219,4 +276,14 @@ func adminRouter(api fiber.Router) {
|
|||||||
coupon.Post("/create", handlers.CreateCoupon)
|
coupon.Post("/create", handlers.CreateCoupon)
|
||||||
coupon.Post("/update", handlers.UpdateCoupon)
|
coupon.Post("/update", handlers.UpdateCoupon)
|
||||||
coupon.Post("/remove", handlers.DeleteCoupon)
|
coupon.Post("/remove", handlers.DeleteCoupon)
|
||||||
|
coupon.Post("/update/assign", handlers.AssignCoupon)
|
||||||
|
|
||||||
|
// coupon-user 已发放优惠券
|
||||||
|
var couponUser = api.Group("/coupon-user")
|
||||||
|
couponUser.Post("/page", handlers.PageCouponUserByAdmin)
|
||||||
|
couponUser.Post("/page/of-user", handlers.PageCouponUserOfUserByAdmin)
|
||||||
|
couponUser.Post("/get", handlers.GetCouponUserByAdmin)
|
||||||
|
couponUser.Post("/create", handlers.CreateCouponUserByAdmin)
|
||||||
|
couponUser.Post("/update", handlers.UpdateCouponUserByAdmin)
|
||||||
|
couponUser.Post("/remove", handlers.DeleteCouponUserByAdmin)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var Admin = &adminService{}
|
|||||||
|
|
||||||
type adminService struct{}
|
type adminService struct{}
|
||||||
|
|
||||||
func (s *adminService) PageAdmins(req core.PageReq) (result []*m.Admin, count int64, err error) {
|
func (s *adminService) Page(req core.PageReq) (result []*m.Admin, count int64, err error) {
|
||||||
return q.Admin.
|
return q.Admin.
|
||||||
Preload(q.Admin.Roles).
|
Preload(q.Admin.Roles).
|
||||||
Omit(q.Admin.Password).
|
Omit(q.Admin.Password).
|
||||||
@@ -30,25 +30,14 @@ func (s *adminService) All() (result []*m.Admin, err error) {
|
|||||||
Find()
|
Find()
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateAdmin struct {
|
func (s *adminService) Create(create *CreateAdmin) error {
|
||||||
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)
|
hash, err := bcrypt.GenerateFromPassword([]byte(create.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("密码加密失败", err)
|
return core.NewServErr("密码加密失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Q.Transaction(func(tx *q.Query) error {
|
return q.Q.Transaction(func(q *q.Query) error {
|
||||||
// 创建管理员
|
// 创建管理员
|
||||||
admin := &m.Admin{
|
admin := &m.Admin{
|
||||||
Username: create.Username,
|
Username: create.Username,
|
||||||
@@ -59,7 +48,7 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
|
|||||||
Email: create.Email,
|
Email: create.Email,
|
||||||
Status: u.Else(create.Status, m.AdminStatusEnabled),
|
Status: u.Else(create.Status, m.AdminStatusEnabled),
|
||||||
}
|
}
|
||||||
if err := tx.Admin.Create(admin); err != nil {
|
if err := q.Admin.Create(admin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +61,7 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
|
|||||||
RoleID: roleID,
|
RoleID: roleID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
|
if err := q.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,18 +70,18 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateAdmin struct {
|
type CreateAdmin struct {
|
||||||
Id int32 `json:"id" validate:"required"`
|
Username string `json:"username" validate:"required,min=3,max=50"`
|
||||||
Password *string `json:"password"`
|
Password string `json:"password" validate:"required,min=6,max=50"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Avatar *string `json:"avatar"`
|
Avatar *string `json:"avatar"`
|
||||||
Phone *string `json:"phone"`
|
Phone *string `json:"phone"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Status *m.AdminStatus `json:"status"`
|
Status *m.AdminStatus `json:"status"`
|
||||||
Roles *[]int32 `json:"roles"`
|
Roles []int32 `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
func (s *adminService) Update(update *UpdateAdmin) error {
|
||||||
simples := make([]field.AssignExpr, 0)
|
simples := make([]field.AssignExpr, 0)
|
||||||
|
|
||||||
if update.Password != nil {
|
if update.Password != nil {
|
||||||
@@ -118,21 +107,27 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
|||||||
simples = append(simples, q.Admin.Status.Value(int(*update.Status)))
|
simples = append(simples, q.Admin.Status.Value(int(*update.Status)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Q.Transaction(func(tx *q.Query) error {
|
return q.Q.Transaction(func(q *q.Query) error {
|
||||||
// 更新管理员基本信息
|
// 更新管理员基本信息
|
||||||
if len(simples) > 0 {
|
if len(simples) > 0 {
|
||||||
_, err := tx.Admin.
|
r, err := q.Admin.
|
||||||
Where(tx.Admin.ID.Eq(update.Id), tx.Admin.Username.Neq("admin")).
|
Where(
|
||||||
|
q.Admin.ID.Eq(update.Id),
|
||||||
|
q.Admin.Lock.Is(false),
|
||||||
|
).
|
||||||
UpdateSimple(simples...)
|
UpdateSimple(simples...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("管理员状态已过期")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新角色关联
|
// 更新角色关联
|
||||||
if update.Roles != nil {
|
if update.Roles != nil {
|
||||||
roles := *update.Roles
|
roles := *update.Roles
|
||||||
if _, err := tx.LinkAdminRole.Where(tx.LinkAdminRole.AdminID.Eq(update.Id)).Delete(); err != nil {
|
if _, err := q.LinkAdminRole.Where(q.LinkAdminRole.AdminID.Eq(update.Id)).Delete(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(roles) > 0 {
|
if len(roles) > 0 {
|
||||||
@@ -143,7 +138,7 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
|||||||
RoleID: roleID,
|
RoleID: roleID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
|
if err := q.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +148,29 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *adminService) RemoveAdmin(id int32) error {
|
type UpdateAdmin struct {
|
||||||
_, err := q.Admin.Where(q.Admin.ID.Eq(id), q.Admin.Username.Neq("admin")).UpdateColumn(q.Admin.DeletedAt, time.Now())
|
Id int32 `json:"id" validate:"required"`
|
||||||
return err
|
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) Remove(id int32) error {
|
||||||
|
r, err := q.Admin.
|
||||||
|
Where(
|
||||||
|
q.Admin.ID.Eq(id),
|
||||||
|
q.Admin.Lock.Is(false),
|
||||||
|
).
|
||||||
|
UpdateColumn(q.Admin.DeletedAt, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("管理员状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,8 +137,14 @@ type UpdateAdminRole struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *adminRoleService) RemoveAdminRole(id int32) error {
|
func (r *adminRoleService) RemoveAdminRole(id int32) error {
|
||||||
_, err := q.AdminRole.Where(q.AdminRole.ID.Eq(id)).UpdateColumn(q.AdminRole.DeletedAt, time.Now())
|
rs, err := q.AdminRole.Where(q.AdminRole.ID.Eq(id)).UpdateColumn(q.AdminRole.DeletedAt, time.Now())
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rs.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("管理员角色状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var AdminRoleModifyLock = "platform:admin_role_permissions:modify"
|
var AdminRoleModifyLock = "platform:admin_role_permissions:modify"
|
||||||
|
|||||||
@@ -9,28 +9,42 @@ var Bill = &billService{}
|
|||||||
|
|
||||||
type billService struct{}
|
type billService struct{}
|
||||||
|
|
||||||
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) error {
|
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) (*m.Bill, error) {
|
||||||
return q.Bill.Create(&m.Bill{
|
bill := &m.Bill{
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
BillNo: ID.GenReadable("bil"),
|
BillNo: ID.GenReadable("bil"),
|
||||||
TradeID: &tradeId,
|
TradeID: &tradeId,
|
||||||
Type: m.BillTypeRecharge,
|
Type: m.BillTypeRecharge,
|
||||||
Info: &detail.Subject,
|
Info: &detail.Subject,
|
||||||
Amount: detail.Amount,
|
Amount: detail.Discounted,
|
||||||
Actual: detail.Actual,
|
Actual: detail.Actual,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
err := q.Bill.Create(bill)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bill, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) error {
|
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) (*m.Bill, error) {
|
||||||
return q.Bill.Create(&m.Bill{
|
bill := &m.Bill{
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
BillNo: ID.GenReadable("bil"),
|
BillNo: ID.GenReadable("bil"),
|
||||||
ResourceID: &resourceId,
|
ResourceID: &resourceId,
|
||||||
TradeID: tradeId,
|
TradeID: tradeId,
|
||||||
CouponID: detail.CouponId,
|
CouponUserID: detail.CouponUserId,
|
||||||
Type: m.BillTypeConsume,
|
Type: m.BillTypeConsume,
|
||||||
Info: &detail.Subject,
|
Info: &detail.Subject,
|
||||||
Amount: detail.Amount,
|
Amount: detail.Discounted,
|
||||||
Actual: detail.Actual,
|
Actual: detail.Actual,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
err := q.Bill.Create(bill)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bill, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"platform/pkg/env"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
g "platform/web/globals"
|
g "platform/web/globals"
|
||||||
@@ -24,22 +25,69 @@ var Channel = &channelServer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChannelServiceProvider interface {
|
type ChannelServiceProvider interface {
|
||||||
CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error)
|
CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error)
|
||||||
RemoveChannels(batch string) error
|
RemoveChannels(batch string) error
|
||||||
|
ClearExpiredChannels(proxyId int32) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type channelServer struct {
|
type channelServer struct {
|
||||||
provider ChannelServiceProvider
|
provider ChannelServiceProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelServer) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error) {
|
func (s *channelServer) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) {
|
||||||
return s.provider.CreateChannels(source, resourceId, authWhitelist, authPassword, count, edgeFilter...)
|
return s.provider.CreateChannels(source, resourceNo, authWhitelist, authPassword, count, edgeFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelServer) RemoveChannels(batch string) error {
|
func (s *channelServer) RemoveChannels(batch string) error {
|
||||||
return s.provider.RemoveChannels(batch)
|
return s.provider.RemoveChannels(batch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *channelServer) ClearExpiredChannels(proxyId int32) (int, error) {
|
||||||
|
return s.provider.ClearExpiredChannels(proxyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *channelServer) RefreshEdges() error {
|
||||||
|
if env.RunMode != env.RunModeProd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到所有网关
|
||||||
|
proxies, err := q.Proxy.Where(
|
||||||
|
q.Proxy.Status.Eq(int(m.ProxyStatusOnline)),
|
||||||
|
).Find()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("查询网关失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, proxy := range proxies {
|
||||||
|
gateway, err := proxyGateway(proxy)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("创建代理网关失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选取随机节点
|
||||||
|
edges, err := gateway.GatewayEdge(&g.GatewayEdgeReq{
|
||||||
|
Assigned: u.P(false),
|
||||||
|
Limit: u.P(1000),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取边缘节点失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交断开配置
|
||||||
|
edgeIds := make([]string, 0, len(edges))
|
||||||
|
for id, _ := range edges {
|
||||||
|
edgeIds = append(edgeIds, id)
|
||||||
|
}
|
||||||
|
g.Cloud.CloudDisconnect(&g.CloudDisconnectReq{
|
||||||
|
Uuid: proxy.Mac,
|
||||||
|
Edge: &edgeIds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 授权方式
|
// 授权方式
|
||||||
type ChannelAuthType int
|
type ChannelAuthType int
|
||||||
|
|
||||||
@@ -67,12 +115,23 @@ func genPassPair() (string, string) {
|
|||||||
return string(username), string(password)
|
return string(username), string(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindResourceNoById(resourceId int32) (string, error) {
|
||||||
|
resource, err := q.Resource.
|
||||||
|
Select(q.Resource.ResourceNo).
|
||||||
|
Where(q.Resource.ID.Eq(resourceId)).
|
||||||
|
Take()
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrResourceNotExist
|
||||||
|
}
|
||||||
|
return u.Z(resource.ResourceNo), nil
|
||||||
|
}
|
||||||
|
|
||||||
// 查找资源
|
// 查找资源
|
||||||
func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
func findResourceViewByNo(resourceNo string, now time.Time) (*ResourceView, error) {
|
||||||
resource, err := q.Resource.
|
resource, err := q.Resource.
|
||||||
Preload(field.Associations).
|
Preload(field.Associations).
|
||||||
Where(
|
Where(
|
||||||
q.Resource.ID.Eq(resourceId),
|
q.Resource.ResourceNo.Eq(resourceNo),
|
||||||
q.Resource.Active.Is(true),
|
q.Resource.Active.Is(true),
|
||||||
).
|
).
|
||||||
Take()
|
Take()
|
||||||
@@ -83,10 +142,11 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
return nil, ErrResourceNotExist
|
return nil, ErrResourceNotExist
|
||||||
}
|
}
|
||||||
var info = &ResourceView{
|
var info = &ResourceView{
|
||||||
Id: resource.ID,
|
ID: resource.ID,
|
||||||
User: *resource.User,
|
User: *resource.User,
|
||||||
Active: resource.Active,
|
Active: resource.Active,
|
||||||
Type: resource.Type,
|
Type: resource.Type,
|
||||||
|
CheckIP: resource.CheckIP,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch resource.Type {
|
switch resource.Type {
|
||||||
@@ -94,7 +154,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
var sub = resource.Short
|
var sub = resource.Short
|
||||||
info.ShortId = &sub.ID
|
info.ShortId = &sub.ID
|
||||||
info.ExpireAt = sub.ExpireAt
|
info.ExpireAt = sub.ExpireAt
|
||||||
info.Live = time.Duration(sub.Live) * time.Second
|
info.Live = time.Duration(sub.Live) * time.Minute
|
||||||
info.Mode = sub.Type
|
info.Mode = sub.Type
|
||||||
info.Quota = sub.Quota
|
info.Quota = sub.Quota
|
||||||
info.Used = sub.Used
|
info.Used = sub.Used
|
||||||
@@ -108,7 +168,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
var sub = resource.Long
|
var sub = resource.Long
|
||||||
info.LongId = &sub.ID
|
info.LongId = &sub.ID
|
||||||
info.ExpireAt = sub.ExpireAt
|
info.ExpireAt = sub.ExpireAt
|
||||||
info.Live = time.Duration(sub.Live) * time.Hour
|
info.Live = time.Duration(sub.Live) * time.Minute
|
||||||
info.Mode = sub.Type
|
info.Mode = sub.Type
|
||||||
info.Quota = sub.Quota
|
info.Quota = sub.Quota
|
||||||
info.Used = sub.Used
|
info.Used = sub.Used
|
||||||
@@ -128,7 +188,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
|
|||||||
|
|
||||||
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
// ResourceView 套餐数据的简化视图,便于直接获取主要数据
|
||||||
type ResourceView struct {
|
type ResourceView struct {
|
||||||
Id int32
|
ID int32
|
||||||
User m.User
|
User m.User
|
||||||
Active bool
|
Active bool
|
||||||
Type m.ResourceType
|
Type m.ResourceType
|
||||||
@@ -142,16 +202,17 @@ type ResourceView struct {
|
|||||||
Daily int32
|
Daily int32
|
||||||
LastAt *time.Time
|
LastAt *time.Time
|
||||||
Today int // 今日用量
|
Today int // 今日用量
|
||||||
|
CheckIP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否可提取
|
// 检查用户是否可提取
|
||||||
func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*ResourceView, []string, error) {
|
func ensure(now time.Time, source netip.Addr, resourceNo string, authWhitelist bool, count int) (*ResourceView, []string, error) {
|
||||||
if count > 400 {
|
if count > 400 {
|
||||||
return nil, nil, core.NewBizErr("单次最多提取 400 个")
|
return nil, nil, core.NewBizErr("单次最多提取 400 个")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户套餐
|
// 获取用户套餐
|
||||||
resource, err := findResource(resourceId, now)
|
resource, err := findResourceViewByNo(resourceNo, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -170,6 +231,10 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authWhitelist && len(whitelists) == 0 {
|
||||||
|
return nil, nil, core.NewBizErr("当前白名单为空,请先添加白名单")
|
||||||
|
}
|
||||||
|
|
||||||
ips := make([]string, len(whitelists))
|
ips := make([]string, len(whitelists))
|
||||||
pass := false
|
pass := false
|
||||||
for i, item := range whitelists {
|
for i, item := range whitelists {
|
||||||
@@ -178,7 +243,7 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
|
|||||||
pass = true
|
pass = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !pass {
|
if resource.CheckIP && !pass {
|
||||||
return nil, nil, core.NewBizErr(fmt.Sprintf("IP 地址 %s 不在白名单内", source.String()))
|
return nil, nil, core.NewBizErr(fmt.Sprintf("IP 地址 %s 不在白名单内", source.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,10 +274,13 @@ func ensure(now time.Time, source netip.Addr, resourceId int32, count int) (*Res
|
|||||||
return resource, ips, nil
|
return resource, ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func freeChansKey(proxy int32) string {
|
||||||
freeChansKey = "channel:free"
|
return "channel:free:" + strconv.Itoa(int(proxy))
|
||||||
usedChansKey = "channel:used"
|
}
|
||||||
)
|
|
||||||
|
func usedChansKey(proxy int32, batch string) string {
|
||||||
|
return "channel:used:" + strconv.Itoa(int(proxy)) + ":" + batch
|
||||||
|
}
|
||||||
|
|
||||||
// 扩容通道
|
// 扩容通道
|
||||||
func regChans(proxy int32, chans []netip.AddrPort) error {
|
func regChans(proxy int32, chans []netip.AddrPort) error {
|
||||||
@@ -221,7 +289,7 @@ func regChans(proxy int32, chans []netip.AddrPort) error {
|
|||||||
strs[i] = ch.String()
|
strs[i] = ch.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
key := freeChansKey + ":" + strconv.Itoa(int(proxy))
|
key := freeChansKey(proxy)
|
||||||
err := g.Redis.SAdd(context.Background(), key, strs...).Err()
|
err := g.Redis.SAdd(context.Background(), key, strs...).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("扩容通道失败: %w", err)
|
return fmt.Errorf("扩容通道失败: %w", err)
|
||||||
@@ -231,8 +299,8 @@ func regChans(proxy int32, chans []netip.AddrPort) error {
|
|||||||
|
|
||||||
// 缩容通道
|
// 缩容通道
|
||||||
func remChans(proxy int32) error {
|
func remChans(proxy int32) error {
|
||||||
key := freeChansKey + ":" + strconv.Itoa(int(proxy))
|
key := freeChansKey(proxy)
|
||||||
err := g.Redis.SRem(context.Background(), key).Err()
|
err := g.Redis.Del(context.Background(), key).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("缩容通道失败: %w", err)
|
return fmt.Errorf("缩容通道失败: %w", err)
|
||||||
}
|
}
|
||||||
@@ -241,13 +309,12 @@ func remChans(proxy int32) error {
|
|||||||
|
|
||||||
// 取用通道
|
// 取用通道
|
||||||
func lockChans(proxy int32, batch string, count int) ([]netip.AddrPort, error) {
|
func lockChans(proxy int32, batch string, count int) ([]netip.AddrPort, error) {
|
||||||
pid := strconv.Itoa(int(proxy))
|
|
||||||
chans, err := RedisScriptLockChans.Run(
|
chans, err := RedisScriptLockChans.Run(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
g.Redis,
|
g.Redis,
|
||||||
[]string{
|
[]string{
|
||||||
freeChansKey + ":" + pid,
|
freeChansKey(proxy),
|
||||||
usedChansKey + ":" + pid + ":" + batch,
|
usedChansKey(proxy, batch),
|
||||||
},
|
},
|
||||||
count,
|
count,
|
||||||
).StringSlice()
|
).StringSlice()
|
||||||
@@ -268,11 +335,12 @@ func lockChans(proxy int32, batch string, count int) ([]netip.AddrPort, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var RedisScriptLockChans = redis.NewScript(`
|
var RedisScriptLockChans = redis.NewScript(`
|
||||||
local free_key = KEYS[1]
|
local free_key = KEYS[1]
|
||||||
local batch_key = KEYS[2]
|
local batch_key = KEYS[2]
|
||||||
local count = tonumber(ARGV[1])
|
local count = tonumber(ARGV[1])
|
||||||
|
|
||||||
if redis.call("SCARD", free_key) < count then
|
local free_count = redis.call("SCARD", free_key)
|
||||||
|
if count <= 0 or free_count < count then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,13 +352,12 @@ return ports
|
|||||||
|
|
||||||
// 归还通道
|
// 归还通道
|
||||||
func freeChans(proxy int32, batch string) error {
|
func freeChans(proxy int32, batch string) error {
|
||||||
pid := strconv.Itoa(int(proxy))
|
|
||||||
err := RedisScriptFreeChans.Run(
|
err := RedisScriptFreeChans.Run(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
g.Redis,
|
g.Redis,
|
||||||
[]string{
|
[]string{
|
||||||
freeChansKey + ":" + pid,
|
freeChansKey(proxy),
|
||||||
usedChansKey + ":" + pid + ":" + batch,
|
usedChansKey(proxy, batch),
|
||||||
},
|
},
|
||||||
).Err()
|
).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -301,16 +368,17 @@ func freeChans(proxy int32, batch string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var RedisScriptFreeChans = redis.NewScript(`
|
var RedisScriptFreeChans = redis.NewScript(`
|
||||||
local free_key = KEYS[1]
|
local free_key = KEYS[1]
|
||||||
local batch_key = KEYS[2]
|
local batch_key = KEYS[2]
|
||||||
|
|
||||||
local chans = redis.call("LRANGE", batch_key, 0, -1)
|
local chans = redis.call("LRANGE", batch_key, 0, -1)
|
||||||
redis.call("DEL", batch_key)
|
if #chans == 0 then
|
||||||
|
return 1
|
||||||
if redis.call("EXISTS", free_key) == 1 then
|
|
||||||
redis.call("SADD", free_key, unpack(chans))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
redis.call("SADD", free_key, unpack(chans))
|
||||||
|
redis.call("DEL", batch_key)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
@@ -23,201 +24,180 @@ import (
|
|||||||
|
|
||||||
type channelBaiyinProvider struct{}
|
type channelBaiyinProvider struct{}
|
||||||
|
|
||||||
func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter ...EdgeFilter) ([]*m.Channel, error) {
|
func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, filter *EdgeFilter) ([]*m.Channel, error) {
|
||||||
var filter *EdgeFilter = nil
|
if filter == nil {
|
||||||
if len(edgeFilter) > 0 {
|
return nil, core.NewBizErr("缺少节点过滤条件")
|
||||||
filter = &edgeFilter[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
batch := ID.GenReadable("bat")
|
batchNo := ID.GenReadable("bat")
|
||||||
|
|
||||||
// 检查并获取套餐与白名单
|
|
||||||
resource, whitelists, err := ensure(now, source, resourceId, count)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
user := resource.User
|
|
||||||
expire := now.Add(resource.Live)
|
|
||||||
|
|
||||||
// 选择代理
|
|
||||||
proxyResult := struct {
|
|
||||||
m.Proxy
|
|
||||||
Count int
|
|
||||||
}{}
|
|
||||||
err = q.Proxy.
|
|
||||||
LeftJoin(q.Channel, q.Channel.ProxyID.EqCol(q.Proxy.ID), q.Channel.ExpiredAt.Gt(now)).
|
|
||||||
Select(q.Proxy.ALL, field.NewUnsafeFieldRaw("10000 - count(*)").As("count")).
|
|
||||||
Where(
|
|
||||||
q.Proxy.Type.Eq(int(m.ProxyTypeBaiYin)),
|
|
||||||
q.Proxy.Status.Eq(int(m.ProxyStatusOnline)),
|
|
||||||
).
|
|
||||||
Group(q.Proxy.ID).
|
|
||||||
Order(field.NewField("", "count")).
|
|
||||||
Limit(1).Scan(&proxyResult)
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewBizErr("获取可用代理失败", err)
|
|
||||||
}
|
|
||||||
if proxyResult.Count < count {
|
|
||||||
return nil, core.NewBizErr("无可用主机,请稍后再试")
|
|
||||||
}
|
|
||||||
proxy := proxyResult.Proxy
|
|
||||||
|
|
||||||
// 获取可用通道
|
|
||||||
chans, err := lockChans(proxy.ID, batch, count)
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewBizErr("无可用通道,请稍后再试", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取可用节点
|
|
||||||
edgesResp, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{
|
|
||||||
Province: filter.Prov,
|
|
||||||
City: filter.City,
|
|
||||||
Isp: u.X(filter.Isp.String()),
|
|
||||||
Limit: &count,
|
|
||||||
NoRepeat: u.P(true),
|
|
||||||
NoDayRepeat: u.P(true),
|
|
||||||
ActiveTime: u.P(3600),
|
|
||||||
IpUnchangedTime: u.P(3600),
|
|
||||||
Sort: u.P("ip_unchanged_time_asc"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewBizErr("获取可用节点失败", err)
|
|
||||||
}
|
|
||||||
if edgesResp.Total != count && len(edgesResp.Edges) != count {
|
|
||||||
return nil, core.NewBizErr("地区可用节点数量不足 [%s, %s] [%s]")
|
|
||||||
}
|
|
||||||
edges := edgesResp.Edges
|
|
||||||
|
|
||||||
// 准备通道数据
|
|
||||||
channels := make([]*m.Channel, count)
|
channels := make([]*m.Channel, count)
|
||||||
chanConfigs := make([]*g.PortConfigsReq, count)
|
|
||||||
edgeConfigs := make([]string, count)
|
|
||||||
for i := range count {
|
|
||||||
ch := chans[i]
|
|
||||||
edge := edges[i]
|
|
||||||
|
|
||||||
|
// 资源锁,防止并发扣减失败导致的端口悬空问题
|
||||||
|
err := g.Redsync.WithLock(lockChannelCreateKey(resourceNo), func() error {
|
||||||
|
// 检查并获取套餐与白名单
|
||||||
|
resource, whitelists, err := ensure(now, source, resourceNo, authWhitelist, count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, core.NewBizErr("解析通道地址失败", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通道数据
|
user := resource.User
|
||||||
channels[i] = &m.Channel{
|
expire := now.Add(resource.Live)
|
||||||
UserID: user.ID,
|
|
||||||
ResourceID: resourceId,
|
|
||||||
BatchNo: batch,
|
|
||||||
ProxyID: proxy.ID,
|
|
||||||
Host: u.Else(proxy.Host, proxy.IP.String()),
|
|
||||||
Port: ch.Port(),
|
|
||||||
EdgeRef: u.P(edge.EdgeID),
|
|
||||||
FilterISP: filter.Isp,
|
|
||||||
FilterProv: filter.Prov,
|
|
||||||
FilterCity: filter.City,
|
|
||||||
ExpiredAt: expire,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通道配置数据
|
// 选择代理
|
||||||
chanConfigs[i] = &g.PortConfigsReq{
|
proxy, gateway, err := selectProxy(count)
|
||||||
Port: int(ch.Port()),
|
|
||||||
Status: true,
|
|
||||||
Edge: &[]string{edge.EdgeID},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 白名单模式
|
|
||||||
if authWhitelist {
|
|
||||||
channels[i].Whitelists = u.P(strings.Join(whitelists, ","))
|
|
||||||
chanConfigs[i].Whitelist = &whitelists
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码模式
|
|
||||||
if authPassword {
|
|
||||||
username, password := genPassPair()
|
|
||||||
channels[i].Username = &username
|
|
||||||
channels[i].Password = &password
|
|
||||||
chanConfigs[i].Userpass = u.P(username + ":" + password)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 连接配置数据
|
|
||||||
edgeConfigs[i] = edge.EdgeID
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交异步任务关闭通道
|
|
||||||
_, err = g.Asynq.Enqueue(
|
|
||||||
e.NewRemoveChannel(batch),
|
|
||||||
asynq.ProcessAt(expire),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewServErr("提交关闭通道任务失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存数据
|
|
||||||
err = q.Q.Transaction(func(q *q.Query) error {
|
|
||||||
var rs gen.ResultInfo
|
|
||||||
|
|
||||||
// 根据套餐类型和模式更新使用记录
|
|
||||||
isShortType := resource.Type == m.ResourceTypeShort
|
|
||||||
isLongType := resource.Type == m.ResourceTypeLong
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case isShortType:
|
|
||||||
rs, err = q.ResourceShort.
|
|
||||||
Where(
|
|
||||||
q.ResourceShort.ID.Eq(*resource.ShortId),
|
|
||||||
q.ResourceShort.Used.Eq(resource.Used),
|
|
||||||
q.ResourceShort.Daily.Eq(resource.Daily),
|
|
||||||
).
|
|
||||||
UpdateSimple(
|
|
||||||
q.ResourceShort.Used.Add(int32(count)),
|
|
||||||
q.ResourceShort.Daily.Value(int32(resource.Today+count)),
|
|
||||||
q.ResourceShort.LastAt.Value(now),
|
|
||||||
)
|
|
||||||
|
|
||||||
case isLongType:
|
|
||||||
rs, err = q.ResourceLong.
|
|
||||||
Where(
|
|
||||||
q.ResourceLong.ID.Eq(*resource.LongId),
|
|
||||||
q.ResourceLong.Used.Eq(resource.Used),
|
|
||||||
q.ResourceLong.Daily.Eq(resource.Daily),
|
|
||||||
).
|
|
||||||
UpdateSimple(
|
|
||||||
q.ResourceLong.Used.Add(int32(count)),
|
|
||||||
q.ResourceLong.Daily.Value(int32(resource.Today+count)),
|
|
||||||
q.ResourceLong.LastAt.Value(now),
|
|
||||||
)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return core.NewServErr("套餐类型不正确,无法更新", nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新套餐使用记录失败", err)
|
return err
|
||||||
}
|
|
||||||
if rs.RowsAffected == 0 {
|
|
||||||
return core.NewServErr("套餐使用记录不存在")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存通道
|
// 取用端口
|
||||||
err = q.Channel.
|
chans, err := selectPorts(proxy.ID, batchNo, count, expire)
|
||||||
Omit(field.AssociationFields).
|
|
||||||
Create(channels...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("保存通道失败", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存提取记录
|
// 绑定节点端口
|
||||||
err = q.LogsUserUsage.Create(&m.LogsUserUsage{
|
chanConfigs := make([]*g.PortConfigsReq, count)
|
||||||
UserID: user.ID,
|
edgeConfigs := make([]string, 0, count)
|
||||||
ResourceID: resourceId,
|
for i := range count {
|
||||||
BatchNo: batch,
|
ch := chans[i]
|
||||||
Count: int32(count),
|
|
||||||
ISP: u.P(filter.Isp.String()),
|
// 通道数据
|
||||||
Prov: filter.Prov,
|
channels[i] = &m.Channel{
|
||||||
City: filter.City,
|
UserID: user.ID,
|
||||||
IP: orm.Inet{Addr: source},
|
ResourceID: resource.ID,
|
||||||
Time: now,
|
BatchNo: batchNo,
|
||||||
|
ProxyID: proxy.ID,
|
||||||
|
Host: u.Else(proxy.Host, proxy.IP.String()),
|
||||||
|
Port: ch.Port(),
|
||||||
|
FilterISP: filter.Isp,
|
||||||
|
FilterProv: filter.Prov,
|
||||||
|
FilterCity: filter.City,
|
||||||
|
ExpiredAt: expire,
|
||||||
|
Proxy: proxy,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通道配置数据
|
||||||
|
chanConfigs[i] = &g.PortConfigsReq{
|
||||||
|
Port: int(ch.Port()),
|
||||||
|
Status: true,
|
||||||
|
AutoEdgeConfig: &g.AutoEdgeConfig{
|
||||||
|
Province: u.Z(filter.Prov),
|
||||||
|
City: u.Z(filter.City),
|
||||||
|
Isp: filter.Isp.String(),
|
||||||
|
Count: u.P(1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 白名单模式
|
||||||
|
if authWhitelist {
|
||||||
|
channels[i].Whitelists = u.P(strings.Join(whitelists, ","))
|
||||||
|
chanConfigs[i].Whitelist = &whitelists
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码模式
|
||||||
|
if authPassword {
|
||||||
|
username, password := genPassPair()
|
||||||
|
channels[i].Username = &username
|
||||||
|
channels[i].Password = &password
|
||||||
|
chanConfigs[i].Userpass = u.P(username + ":" + password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交配置
|
||||||
|
slog.Debug("提交代理端口配置", "proxy", proxy.IP.String(), "total_count", len(chanConfigs), "remote_count", len(edgeConfigs))
|
||||||
|
if env.RunMode == env.RunModeProd {
|
||||||
|
|
||||||
|
// 从云端补足节点
|
||||||
|
err := ensureEdges(proxy, gateway, filter, count)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("ensureEdges 失败", "err", err) // 不阻止通道创建,继续走后续流程
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用网关代理通道
|
||||||
|
if len(chanConfigs) > 0 {
|
||||||
|
if err := gateway.GatewayPortConfigs(chanConfigs); err != nil {
|
||||||
|
slog.Warn("提交代理端口配置失败", "error", err.Error())
|
||||||
|
return core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", proxy.IP.String()), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, item := range chanConfigs {
|
||||||
|
str, _ := json.Marshal(item)
|
||||||
|
fmt.Println(string(str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存数据
|
||||||
|
err = q.Q.Transaction(func(q *q.Query) error {
|
||||||
|
// 更新使用记录
|
||||||
|
var result gen.ResultInfo
|
||||||
|
var err error
|
||||||
|
switch resource.Type {
|
||||||
|
case m.ResourceTypeShort:
|
||||||
|
result, err = q.ResourceShort.
|
||||||
|
Where(
|
||||||
|
q.ResourceShort.ID.Eq(*resource.ShortId),
|
||||||
|
q.ResourceShort.Used.Eq(resource.Used),
|
||||||
|
q.ResourceShort.Daily.Eq(resource.Daily),
|
||||||
|
).
|
||||||
|
UpdateSimple(
|
||||||
|
q.ResourceShort.Used.Add(int32(count)),
|
||||||
|
q.ResourceShort.Daily.Value(int32(resource.Today+count)),
|
||||||
|
q.ResourceShort.LastAt.Value(now),
|
||||||
|
)
|
||||||
|
|
||||||
|
case m.ResourceTypeLong:
|
||||||
|
result, err = q.ResourceLong.
|
||||||
|
Where(
|
||||||
|
q.ResourceLong.ID.Eq(*resource.LongId),
|
||||||
|
q.ResourceLong.Used.Eq(resource.Used),
|
||||||
|
q.ResourceLong.Daily.Eq(resource.Daily),
|
||||||
|
).
|
||||||
|
UpdateSimple(
|
||||||
|
q.ResourceLong.Used.Add(int32(count)),
|
||||||
|
q.ResourceLong.Daily.Value(int32(resource.Today+count)),
|
||||||
|
q.ResourceLong.LastAt.Value(now),
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return core.NewBizErr("套餐类型不正确,无法更新")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("更新套餐使用记录失败", err)
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("套餐状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存通道
|
||||||
|
err = q.Channel.
|
||||||
|
Omit(field.AssociationFields).
|
||||||
|
Create(channels...)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("保存通道失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存提取记录
|
||||||
|
err = q.LogsUserUsage.Create(&m.LogsUserUsage{
|
||||||
|
UserID: user.ID,
|
||||||
|
ResourceID: resource.ID,
|
||||||
|
BatchNo: batchNo,
|
||||||
|
Count: int32(count),
|
||||||
|
ISP: u.X(filter.Isp.String()),
|
||||||
|
Prov: filter.Prov,
|
||||||
|
City: filter.City,
|
||||||
|
IP: orm.Inet{Addr: source},
|
||||||
|
Time: now,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("保存用户使用记录失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("保存用户使用记录失败", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -226,100 +206,269 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交配置
|
|
||||||
secret := strings.Split(u.Z(proxy.Secret), ":")
|
|
||||||
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
|
|
||||||
if env.DebugExternalChange {
|
|
||||||
|
|
||||||
// 连接节点到网关
|
|
||||||
err = g.Cloud.CloudConnect(&g.CloudConnectReq{
|
|
||||||
Uuid: proxy.Mac,
|
|
||||||
Edge: &edgeConfigs,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewServErr("连接云平台失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启用网关代理通道
|
|
||||||
err = gateway.GatewayPortConfigs(chanConfigs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewServErr(fmt.Sprintf("配置代理 %s 端口失败", proxy.IP.String()), err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slog.Debug("提交代理端口配置", "proxy", proxy.IP.String())
|
|
||||||
for _, item := range chanConfigs {
|
|
||||||
str, _ := json.Marshal(item)
|
|
||||||
fmt.Println(string(str))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return channels, nil
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelBaiyinProvider) RemoveChannels(batch string) error {
|
func (s *channelBaiyinProvider) RemoveChannels(batchNo string) error {
|
||||||
start := time.Now()
|
return g.Redsync.WithLock(lockChannelRemoveKey(batchNo), func() error {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
// 获取连接数据
|
// 获取连接数据
|
||||||
channels, err := q.Channel.Where(q.Channel.BatchNo.Eq(batch)).Find()
|
channels, err := q.Channel.Where(q.Channel.BatchNo.Eq(batchNo)).Find()
|
||||||
if err != nil {
|
|
||||||
return core.NewServErr(fmt.Sprintf("获取通道数据失败,batch:%s", batch), err)
|
|
||||||
}
|
|
||||||
if len(channels) == 0 {
|
|
||||||
slog.Warn(fmt.Sprintf("未找到通道数据,batch:%s", batch))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(channels[0].ProxyID)).Take()
|
|
||||||
if err != nil {
|
|
||||||
return core.NewServErr(fmt.Sprintf("获取代理数据失败,batch:%s", batch), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 准备配置数据
|
|
||||||
edgeConfigs := make([]string, len(channels))
|
|
||||||
configs := make([]*g.PortConfigsReq, len(channels))
|
|
||||||
for i, channel := range channels {
|
|
||||||
if channel.EdgeRef != nil {
|
|
||||||
edgeConfigs[i] = *channel.EdgeRef
|
|
||||||
} else {
|
|
||||||
slog.Warn(fmt.Sprintf("通道 %d 没有保存节点引用", channel.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
configs[i] = &g.PortConfigsReq{
|
|
||||||
Status: false,
|
|
||||||
Port: int(channel.Port),
|
|
||||||
Edge: &[]string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交配置
|
|
||||||
if env.DebugExternalChange {
|
|
||||||
// 断开节点连接
|
|
||||||
g.Cloud.CloudDisconnect(&g.CloudDisconnectReq{
|
|
||||||
Uuid: proxy.Mac,
|
|
||||||
Edge: &edgeConfigs,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 清空通道配置
|
|
||||||
secret := strings.Split(*proxy.Secret, ":")
|
|
||||||
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
|
|
||||||
err := gateway.GatewayPortConfigs(configs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr(fmt.Sprintf("清空代理 %s 端口配置失败", proxy.IP.String()), err)
|
return core.NewServErr(fmt.Sprintf("获取通道数据失败,batch:%s", batchNo), err)
|
||||||
}
|
}
|
||||||
} else {
|
if len(channels) == 0 {
|
||||||
slog.Debug("清除代理端口配置", "proxy", proxy.IP)
|
slog.Warn(fmt.Sprintf("未找到通道数据,batch:%s", batchNo))
|
||||||
for _, item := range configs {
|
return nil
|
||||||
str, _ := json.Marshal(item)
|
|
||||||
fmt.Println(string(str))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 释放端口
|
proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(channels[0].ProxyID)).Take()
|
||||||
err = freeChans(proxy.ID, batch)
|
if err != nil {
|
||||||
|
return core.NewServErr(fmt.Sprintf("获取代理数据失败,batch:%s", batchNo), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查通道是否存在
|
||||||
|
chans, err := g.Redis.LRange(context.Background(), usedChansKey(proxy.ID, batchNo), 0, -1).Result()
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("查询使用中通道失败", err)
|
||||||
|
}
|
||||||
|
if len(chans) == 0 {
|
||||||
|
slog.Debug("通道为空,跳过清理", "key", usedChansKey(proxy.ID, batchNo))
|
||||||
|
return nil // 没有使用中通道,已经被清理过了
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备配置数据
|
||||||
|
configs := make([]*g.PortConfigsReq, len(chans))
|
||||||
|
for i, ch := range chans {
|
||||||
|
ap, err := netip.ParseAddrPort(ch)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr(fmt.Sprintf("解析通道数据失败: %s", ch), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configs[i] = &g.PortConfigsReq{
|
||||||
|
Port: int(ap.Port()),
|
||||||
|
Edge: &[]string{},
|
||||||
|
AutoEdgeConfig: &g.AutoEdgeConfig{Count: u.P(0)},
|
||||||
|
Status: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交配置
|
||||||
|
if env.RunMode == env.RunModeProd {
|
||||||
|
gateway, err := proxyGateway(proxy)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("创建代理网关失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = gateway.GatewayPortConfigs(configs); err != nil {
|
||||||
|
return core.NewServErr(fmt.Sprintf("清空代理 %s 端口配置失败", proxy.IP.String()), err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, item := range configs {
|
||||||
|
str, _ := json.Marshal(item)
|
||||||
|
fmt.Println(string(str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放端口
|
||||||
|
err = freeChans(proxy.ID, batchNo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("清除代理端口配置", "proxy", proxy.ID, "batch", batchNo, "duration", time.Since(start).String())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearExpiredChannels 清理指定代理的过期通道,并返回清理数量(现在理论上不会有需要手动批量清理的通道,未来可以废弃)
|
||||||
|
func (s *channelBaiyinProvider) ClearExpiredChannels(proxyId int32) (int, error) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// 获取未清理通道
|
||||||
|
keys, err := g.Redis.Keys(context.Background(), usedChansKey(proxyId, "*")).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0, core.NewServErr("查询使用中通道失败", err)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
batchList := make([]string, len(keys))
|
||||||
|
batchSet := make(map[string]struct{}, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
parts := strings.Split(key, ":")
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return 0, core.NewServErr(fmt.Sprintf("使用中通道键格式错误: %s", key), nil)
|
||||||
|
}
|
||||||
|
batchList[i] = parts[3]
|
||||||
|
batchSet[parts[3]] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除未过期通道
|
||||||
|
var batchQueried []struct{ BatchNo string }
|
||||||
|
err = q.Channel.
|
||||||
|
Select(q.Channel.BatchNo).
|
||||||
|
Where(
|
||||||
|
q.Channel.BatchNo.In(batchList...),
|
||||||
|
q.Channel.ExpiredAt.Gte(now.UTC()),
|
||||||
|
).
|
||||||
|
Group(q.Channel.BatchNo).
|
||||||
|
Scan(&batchQueried)
|
||||||
|
if err != nil {
|
||||||
|
return 0, core.NewServErr("查询过期通道失败", err)
|
||||||
|
}
|
||||||
|
for _, batch := range batchQueried {
|
||||||
|
delete(batchSet, batch.BatchNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理过期通道
|
||||||
|
slog.Info("批量清理过期通道", "count", len(batchSet))
|
||||||
|
for batchNo, _ := range batchSet {
|
||||||
|
err := s.RemoveChannels(batchNo)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("清理过期通道失败", "batch", batchNo, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(batchSet), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockChannelCreateKey(resourceNo string) string {
|
||||||
|
return fmt.Sprintf("platform:channel:create:%s", resourceNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockChannelRemoveKey(bid string) string {
|
||||||
|
return fmt.Sprintf("platform:batch:remove_expired:%s", bid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectProxy(count int) (*m.Proxy, g.GatewayClient, error) {
|
||||||
|
// 获取在线节点
|
||||||
|
proxies, err := q.Proxy.Where(
|
||||||
|
q.Proxy.Type.Eq(int(m.ProxyTypeBaiYin)),
|
||||||
|
q.Proxy.Status.Eq(int(m.ProxyStatusOnline)),
|
||||||
|
).Find()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, core.NewBizErr("获取可用代理失败", err)
|
||||||
|
}
|
||||||
|
if len(proxies) == 0 {
|
||||||
|
return nil, nil, core.NewBizErr("无可用代理")
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyIDs := make([]int32, 0, len(proxies))
|
||||||
|
proxyMap := make(map[int32]*m.Proxy, len(proxies))
|
||||||
|
for _, item := range proxies {
|
||||||
|
proxyIDs = append(proxyIDs, item.ID)
|
||||||
|
proxyMap[item.ID] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最空闲节点
|
||||||
|
maxId := int32(0)
|
||||||
|
maxCount := -1
|
||||||
|
for _, id := range proxyIDs {
|
||||||
|
idCount, err := g.Redis.SCard(context.Background(), freeChansKey(id)).Result()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("查询可用通道数量失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if idCount > int64(maxCount) {
|
||||||
|
maxCount = int(idCount)
|
||||||
|
maxId = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxCount < count {
|
||||||
|
return nil, nil, core.NewBizErr("无可用代理")
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy := proxyMap[maxId]
|
||||||
|
gateway, err := proxyGateway(proxy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, core.NewServErr("创建代理网关失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxy, gateway, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectPorts(proxyId int32, batchNo string, count int, expire time.Time) ([]netip.AddrPort, error) {
|
||||||
|
chans, err := lockChans(proxyId, batchNo, count)
|
||||||
|
if err != nil {
|
||||||
|
return nil, core.NewBizErr("无可用通道,请稍后再试", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = g.Asynq.Enqueue(
|
||||||
|
e.NewRemoveChannel(batchNo),
|
||||||
|
asynq.ProcessAt(expire),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, core.NewServErr("注册异步关闭通道任务失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureEdges 检查本地节点是否足够,如果不足从云端连入
|
||||||
|
// 本地节点通过 Assigned = false 排除已分配节点
|
||||||
|
// 云端节点通过 NoRepeat = true 排除已分配节点
|
||||||
|
func ensureEdges(proxy *m.Proxy, gateway g.GatewayClient, filter *EdgeFilter, count int) error {
|
||||||
|
if filter.IsEmpty() {
|
||||||
|
return nil // 没有过滤条件,直接返回空,避免无意义的查询
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先查本地
|
||||||
|
localEdges, err := gateway.GatewayEdge(&g.GatewayEdgeReq{
|
||||||
|
Province: filter.Prov,
|
||||||
|
City: filter.City,
|
||||||
|
Isp: u.X(filter.Isp.String()),
|
||||||
|
Limit: &count,
|
||||||
|
Assigned: u.P(false),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("检查可用节点失败[1]", err)
|
||||||
|
}
|
||||||
|
if len(localEdges) >= count {
|
||||||
|
return nil // 本地节点足够,直接返回空,后续逻辑会优先使用本地节点
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再查云端
|
||||||
|
remaining := count - len(localEdges)
|
||||||
|
cloudEdges, err := g.Cloud.CloudEdges(&g.CloudEdgesReq{
|
||||||
|
Province: filter.Prov,
|
||||||
|
City: filter.City,
|
||||||
|
Isp: u.X(filter.Isp.String()),
|
||||||
|
Limit: &remaining,
|
||||||
|
NoRepeat: u.P(true),
|
||||||
|
ActiveTime: u.P(3600),
|
||||||
|
IpUnchangedTime: u.P(3600),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("检查可用节点失败[2]", err)
|
||||||
|
}
|
||||||
|
if len(cloudEdges.Edges) < remaining {
|
||||||
|
return core.NewBizErr("地区可用节点数量不足")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连入云端节点
|
||||||
|
edges := make([]string, remaining)
|
||||||
|
for i, edge := range cloudEdges.Edges {
|
||||||
|
edges[i] = edge.EdgeID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.Cloud.CloudConnect(&g.CloudConnectReq{Uuid: proxy.Mac, Edge: &edges}); err != nil {
|
||||||
|
return core.NewServErr("连接云平台失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Debug("清除代理端口配置", "time", time.Since(start).String())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EdgeInfo struct {
|
||||||
|
Type EdgeInfoType
|
||||||
|
EdgeID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EdgeInfoType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EdgeInfoLocal EdgeInfoType = "local"
|
||||||
|
EdgeInfoCloud EdgeInfoType = "cloud"
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
@@ -27,36 +28,32 @@ func (s *couponService) Page(req *core.PageReq) (result []*m.Coupon, count int64
|
|||||||
|
|
||||||
func (s *couponService) Create(data CreateCouponData) error {
|
func (s *couponService) Create(data CreateCouponData) error {
|
||||||
return q.Coupon.Create(&m.Coupon{
|
return q.Coupon.Create(&m.Coupon{
|
||||||
UserID: data.UserID,
|
Name: data.Name,
|
||||||
Code: data.Code,
|
Amount: data.Amount,
|
||||||
Remark: data.Remark,
|
MinAmount: data.MinAmount,
|
||||||
Amount: data.Amount,
|
Count: int32(u.Else(data.Count, 1)),
|
||||||
MinAmount: data.MinAmount,
|
Status: u.Else(data.Status, m.CouponStatusEnabled),
|
||||||
Status: m.CouponStatusUnused,
|
ExpireType: u.Else(data.ExpireType, m.CouponExpireTypeNever),
|
||||||
ExpireAt: data.ExpireAt,
|
ExpireAt: data.ExpireAt,
|
||||||
|
ExpireIn: data.ExpireIn,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateCouponData struct {
|
type CreateCouponData struct {
|
||||||
UserID *int32 `json:"user_id"`
|
Name string `json:"name" validate:"required"`
|
||||||
Code string `json:"code" validate:"required"`
|
Amount decimal.Decimal `json:"amount" validate:"required"`
|
||||||
Remark *string `json:"remark"`
|
MinAmount decimal.Decimal `json:"min_amount"`
|
||||||
Amount decimal.Decimal `json:"amount" validate:"required"`
|
Count *int `json:"count"`
|
||||||
MinAmount decimal.Decimal `json:"min_amount"`
|
Status *m.CouponStatus `json:"status"`
|
||||||
ExpireAt *time.Time `json:"expire_at"`
|
ExpireType *m.CouponExpireType `json:"expire_type"`
|
||||||
|
ExpireAt *time.Time `json:"expire_at"`
|
||||||
|
ExpireIn *int `json:"expire_in"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *couponService) Update(data UpdateCouponData) error {
|
func (s *couponService) Update(data UpdateCouponData) error {
|
||||||
do := make([]field.AssignExpr, 0)
|
do := make([]field.AssignExpr, 0)
|
||||||
|
if data.Name != nil {
|
||||||
if data.UserID != nil {
|
do = append(do, q.Coupon.Name.Value(*data.Name))
|
||||||
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 {
|
if data.Amount != nil {
|
||||||
do = append(do, q.Coupon.Amount.Value(*data.Amount))
|
do = append(do, q.Coupon.Amount.Value(*data.Amount))
|
||||||
@@ -64,41 +61,80 @@ func (s *couponService) Update(data UpdateCouponData) error {
|
|||||||
if data.MinAmount != nil {
|
if data.MinAmount != nil {
|
||||||
do = append(do, q.Coupon.MinAmount.Value(*data.MinAmount))
|
do = append(do, q.Coupon.MinAmount.Value(*data.MinAmount))
|
||||||
}
|
}
|
||||||
|
if data.Count != nil {
|
||||||
|
do = append(do, q.Coupon.Count_.Value(int32(*data.Count)))
|
||||||
|
}
|
||||||
if data.Status != nil {
|
if data.Status != nil {
|
||||||
do = append(do, q.Coupon.Status.Value(int(*data.Status)))
|
do = append(do, q.Coupon.Status.Value(int(*data.Status)))
|
||||||
}
|
}
|
||||||
if data.ExpireAt != nil {
|
if data.ExpireType != nil {
|
||||||
do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt))
|
switch *data.ExpireType {
|
||||||
|
case m.CouponExpireTypeNever:
|
||||||
|
do = append(do, q.Coupon.ExpireAt.Null(), q.Coupon.ExpireIn.Null())
|
||||||
|
|
||||||
|
case m.CouponExpireTypeFixed:
|
||||||
|
if data.ExpireAt == nil {
|
||||||
|
return core.NewBizErr("expire_at 不能为空")
|
||||||
|
}
|
||||||
|
do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt), q.Coupon.ExpireIn.Null())
|
||||||
|
|
||||||
|
case m.CouponExpireTypeRelative:
|
||||||
|
if data.ExpireIn == nil {
|
||||||
|
return core.NewBizErr("expire_in 不能为空")
|
||||||
|
}
|
||||||
|
do = append(do, q.Coupon.ExpireAt.Null(), q.Coupon.ExpireIn.Value(*data.ExpireIn))
|
||||||
|
}
|
||||||
|
do = append(do, q.Coupon.ExpireType.Value(int(*data.ExpireType)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := q.Coupon.Where(q.Coupon.ID.Eq(data.ID)).UpdateSimple(do...)
|
r, err := q.Coupon.Where(q.Coupon.ID.Eq(data.ID)).UpdateSimple(do...)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("优惠券状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateCouponData struct {
|
type UpdateCouponData struct {
|
||||||
ID int32 `json:"id" validate:"required"`
|
ID int32 `json:"id" validate:"required"`
|
||||||
UserID *int32 `json:"user_id"`
|
Name *string `json:"name"`
|
||||||
Code *string `json:"code"`
|
Amount *decimal.Decimal `json:"amount"`
|
||||||
Remark *string `json:"remark"`
|
MinAmount *decimal.Decimal `json:"min_amount"`
|
||||||
Amount *decimal.Decimal `json:"amount"`
|
Count *int `json:"count"`
|
||||||
MinAmount *decimal.Decimal `json:"min_amount"`
|
Status *m.CouponStatus `json:"status"`
|
||||||
Status *m.CouponStatus `json:"status"`
|
ExpireType *m.CouponExpireType `json:"expire_type"`
|
||||||
ExpireAt *time.Time `json:"expire_at"`
|
ExpireAt *time.Time `json:"expire_at"`
|
||||||
|
ExpireIn *int `json:"expire_in"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *couponService) Delete(id int32) error {
|
func (s *couponService) Delete(id int32) error {
|
||||||
_, err := q.Coupon.Where(q.Coupon.ID.Eq(id)).UpdateColumn(q.Coupon.DeletedAt, time.Now())
|
r, err := q.Coupon.Where(q.Coupon.ID.Eq(id)).UpdateColumn(q.Coupon.DeletedAt, time.Now())
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("优惠券状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *couponService) GetCouponAvailableByCode(code string, amount decimal.Decimal, uid *int32) (*m.Coupon, error) {
|
func (s *couponService) Assign(couponID int32, userID int32) error {
|
||||||
|
return CouponUser.Create(CreateCouponUserData{
|
||||||
|
CouponID: couponID,
|
||||||
|
UserID: userID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserCoupon 获取用户的指定优惠券
|
||||||
|
func (s *couponService) GetUserCoupon(uid int32, cuid int32, amount decimal.Decimal) (*m.CouponUser, error) {
|
||||||
// 获取优惠券
|
// 获取优惠券
|
||||||
coupon, err := q.Coupon.Where(
|
assigned, err := q.CouponUser.Joins(q.CouponUser.Coupon).Where(
|
||||||
q.Coupon.Code.Eq(code),
|
q.CouponUser.ID.Eq(cuid),
|
||||||
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
|
q.CouponUser.UserID.Eq(uid),
|
||||||
q.Coupon.
|
q.CouponUser.Status.Eq(int(m.CouponUserStatusUnused)),
|
||||||
Where(q.Coupon.ExpireAt.Gt(time.Now())).
|
q.CouponUser.Where(q.CouponUser.ExpireAt.IsNull()).Or(q.CouponUser.ExpireAt.Gt(time.Now().UTC())),
|
||||||
Or(q.Coupon.ExpireAt.IsNull()),
|
|
||||||
).Take()
|
).Take()
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, core.NewBizErr("优惠券不存在或已失效")
|
return nil, core.NewBizErr("优惠券不存在或已失效")
|
||||||
@@ -108,32 +144,28 @@ func (s *couponService) GetCouponAvailableByCode(code string, amount decimal.Dec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查最小使用额度
|
// 检查最小使用额度
|
||||||
if amount.Cmp(coupon.MinAmount) < 0 {
|
if amount.Cmp(assigned.Coupon.MinAmount) < 0 {
|
||||||
return nil, core.NewBizErr(fmt.Sprintf("使用此优惠券的最小额度为 %s", coupon.MinAmount))
|
return nil, core.NewBizErr(fmt.Sprintf("使用此优惠券的最小额度为 %s", assigned.Coupon.MinAmount))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查所属
|
return assigned, nil
|
||||||
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 {
|
func (s *couponService) UseCoupon(q *q.Query, cuid int32) error {
|
||||||
_, err := q.Coupon.
|
r, err := q.CouponUser.
|
||||||
Where(
|
Where(
|
||||||
q.Coupon.ID.Eq(id),
|
q.CouponUser.ID.Eq(cuid),
|
||||||
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
|
q.CouponUser.Status.Eq(int(m.CouponUserStatusUnused)),
|
||||||
q.Coupon.ExpireAt.Gt(time.Now()),
|
|
||||||
).
|
).
|
||||||
UpdateSimple(
|
UpdateSimple(
|
||||||
q.Coupon.Status.Value(int(m.CouponStatusUsed)),
|
q.CouponUser.Status.Value(int(m.CouponUserStatusUsed)),
|
||||||
|
q.CouponUser.UsedAt.Value(time.Now()),
|
||||||
)
|
)
|
||||||
return err
|
if err != nil {
|
||||||
|
return core.NewBizErr("使用优惠券失败", err)
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("优惠券状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
255
web/services/coupon_user.go
Normal file
255
web/services/coupon_user.go
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"platform/pkg/u"
|
||||||
|
"platform/web/core"
|
||||||
|
m "platform/web/models"
|
||||||
|
q "platform/web/queries"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var CouponUser = &couponUserService{}
|
||||||
|
|
||||||
|
type couponUserService struct{}
|
||||||
|
|
||||||
|
func (s *couponUserService) Create(data CreateCouponUserData) error {
|
||||||
|
now := time.Now()
|
||||||
|
status := u.Else(data.Status, m.CouponUserStatusUnused)
|
||||||
|
if err := validateCouponUserStatus(status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
coupon, err := tx.Coupon.Where(tx.Coupon.ID.Eq(data.CouponID)).Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取优惠券数据失败", err)
|
||||||
|
}
|
||||||
|
if coupon.Status != m.CouponStatusEnabled {
|
||||||
|
return core.NewBizErr("优惠券不可用")
|
||||||
|
}
|
||||||
|
if coupon.Count <= 0 {
|
||||||
|
return core.NewBizErr("优惠券已发放完")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.User.Where(tx.User.ID.Eq(data.UserID)).Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("用户不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取用户数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expireAt := data.ExpireAt
|
||||||
|
if expireAt == nil {
|
||||||
|
expireAt = couponUserExpireAt(coupon, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
usedAt := data.UsedAt
|
||||||
|
if status == m.CouponUserStatusUsed && usedAt == nil {
|
||||||
|
usedAt = &now
|
||||||
|
}
|
||||||
|
if status == m.CouponUserStatusUnused {
|
||||||
|
usedAt = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.CouponUser.Create(&m.CouponUser{
|
||||||
|
UserID: data.UserID,
|
||||||
|
CouponID: data.CouponID,
|
||||||
|
Status: status,
|
||||||
|
ExpireAt: expireAt,
|
||||||
|
UsedAt: usedAt,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("发放优惠券失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustCouponCount(tx, coupon.ID, -1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateCouponUserData struct {
|
||||||
|
CouponID int32 `json:"coupon_id" validate:"required"`
|
||||||
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
Status *m.CouponUserStatus `json:"status"`
|
||||||
|
ExpireAt *time.Time `json:"expire_at"`
|
||||||
|
UsedAt *time.Time `json:"used_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *couponUserService) Update(data UpdateCouponUserData) error {
|
||||||
|
return q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
current, err := tx.CouponUser.Where(tx.CouponUser.ID.Eq(data.ID)).Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("已发放优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取已发放优惠券失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
do := make([]field.AssignExpr, 0)
|
||||||
|
if data.ExpireAtClear != nil && *data.ExpireAtClear {
|
||||||
|
do = append(do, tx.CouponUser.ExpireAt.Null())
|
||||||
|
} else if data.ExpireAt != nil {
|
||||||
|
do = append(do, tx.CouponUser.ExpireAt.Value(*data.ExpireAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.UsedAtClear != nil && *data.UsedAtClear {
|
||||||
|
do = append(do, tx.CouponUser.UsedAt.Null())
|
||||||
|
} else if data.UsedAt != nil {
|
||||||
|
do = append(do, tx.CouponUser.UsedAt.Value(*data.UsedAt))
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Status != nil {
|
||||||
|
if err := validateCouponUserStatus(*data.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if current.Status != *data.Status {
|
||||||
|
if current.Status == m.CouponUserStatusUsed {
|
||||||
|
return core.NewBizErr("已使用的优惠券不能修改状态")
|
||||||
|
}
|
||||||
|
if current.Status == m.CouponUserStatusDisabled && *data.Status == m.CouponUserStatusUsed {
|
||||||
|
return core.NewBizErr("已禁用的优惠券不能标记为已使用")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *data.Status {
|
||||||
|
case m.CouponUserStatusUnused:
|
||||||
|
if current.Status == m.CouponUserStatusDisabled {
|
||||||
|
if err := adjustCouponCount(tx, current.CouponID, -1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.UsedAt == nil && (data.UsedAtClear == nil || !*data.UsedAtClear) {
|
||||||
|
do = append(do, tx.CouponUser.UsedAt.Null())
|
||||||
|
}
|
||||||
|
|
||||||
|
case m.CouponUserStatusUsed:
|
||||||
|
if data.UsedAt == nil && (data.UsedAtClear == nil || !*data.UsedAtClear) {
|
||||||
|
do = append(do, tx.CouponUser.UsedAt.Value(time.Now()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case m.CouponUserStatusDisabled:
|
||||||
|
if current.Status == m.CouponUserStatusUnused {
|
||||||
|
if err := adjustCouponCount(tx, current.CouponID, 1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do = append(do, tx.CouponUser.Status.Value(int(*data.Status)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(do) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := tx.CouponUser.
|
||||||
|
Where(
|
||||||
|
tx.CouponUser.ID.Eq(data.ID),
|
||||||
|
tx.CouponUser.Status.Eq(int(current.Status)),
|
||||||
|
).
|
||||||
|
UpdateSimple(do...)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("更新已发放优惠券失败", err)
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("已发放优惠券状态已变化,请重试")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateCouponUserData struct {
|
||||||
|
ID int32 `json:"id" validate:"required"`
|
||||||
|
Status *m.CouponUserStatus `json:"status"`
|
||||||
|
ExpireAt *time.Time `json:"expire_at"`
|
||||||
|
ExpireAtClear *bool `json:"expire_at_clear"`
|
||||||
|
UsedAt *time.Time `json:"used_at"`
|
||||||
|
UsedAtClear *bool `json:"used_at_clear"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *couponUserService) Delete(id int32) error {
|
||||||
|
status := m.CouponUserStatusDisabled
|
||||||
|
return s.Update(UpdateCouponUserData{
|
||||||
|
ID: id,
|
||||||
|
Status: &status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *couponUserService) DeleteOfUser(id int32, userID int32) error {
|
||||||
|
assigned, err := q.CouponUser.Where(
|
||||||
|
q.CouponUser.ID.Eq(id),
|
||||||
|
q.CouponUser.UserID.Eq(userID),
|
||||||
|
).Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("已发放优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取已发放优惠券失败", err)
|
||||||
|
}
|
||||||
|
if assigned.Status != m.CouponUserStatusUnused {
|
||||||
|
return core.NewBizErr("只能撤销未使用的优惠券")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func couponUserExpireAt(coupon *m.Coupon, now time.Time) *time.Time {
|
||||||
|
if coupon == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch coupon.ExpireType {
|
||||||
|
case m.CouponExpireTypeFixed:
|
||||||
|
return coupon.ExpireAt
|
||||||
|
case m.CouponExpireTypeRelative:
|
||||||
|
if coupon.ExpireIn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expireAt := now.Add(time.Duration(*coupon.ExpireIn) * 24 * time.Hour)
|
||||||
|
return &expireAt
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateCouponUserStatus(status m.CouponUserStatus) error {
|
||||||
|
switch status {
|
||||||
|
case m.CouponUserStatusUnused, m.CouponUserStatusUsed, m.CouponUserStatusDisabled:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return core.NewBizErr("优惠券发放状态无效")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustCouponCount(tx *q.Query, couponID int32, delta int32) error {
|
||||||
|
coupon, err := tx.Coupon.Where(tx.Coupon.ID.Eq(couponID)).Take()
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return core.NewBizErr("优惠券不存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("获取优惠券数据失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
next := coupon.Count + delta
|
||||||
|
if next < 0 {
|
||||||
|
return core.NewBizErr("优惠券已发放完")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := tx.Coupon.
|
||||||
|
Where(tx.Coupon.ID.Eq(couponID), tx.Coupon.Count_.Eq(coupon.Count)).
|
||||||
|
UpdateSimple(tx.Coupon.Count_.Value(next))
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("更新优惠券数量失败", err)
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("优惠券库存已变化,请重试")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"platform/pkg/u"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
)
|
)
|
||||||
@@ -37,3 +38,15 @@ type EdgeFilter struct {
|
|||||||
Prov *string `json:"prov"`
|
Prov *string `json:"prov"`
|
||||||
City *string `json:"city"`
|
City *string `json:"city"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *EdgeFilter) IsEmpty() bool {
|
||||||
|
if f == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Isp.String() == "" || u.Z(f.Prov) != "" || u.Z(f.City) != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,11 +13,6 @@ var Product = &productService{}
|
|||||||
|
|
||||||
type productService struct{}
|
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) {
|
func (s *productService) AllProducts() ([]*m.Product, error) {
|
||||||
return q.Product.
|
return q.Product.
|
||||||
@@ -25,6 +20,62 @@ func (s *productService) AllProducts() ([]*m.Product, error) {
|
|||||||
Find()
|
Find()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *productService) AllProductSaleInfos() ([]*m.Product, error) {
|
||||||
|
products, err := q.Product.
|
||||||
|
Select(
|
||||||
|
q.Product.ID,
|
||||||
|
q.Product.Code,
|
||||||
|
q.Product.Name,
|
||||||
|
q.Product.Description,
|
||||||
|
q.Product.Sort,
|
||||||
|
).
|
||||||
|
Where(
|
||||||
|
q.Product.Status.Eq(int(m.ProductStatusEnabled)),
|
||||||
|
).
|
||||||
|
Order(q.Product.Sort).
|
||||||
|
Find()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pids := make([]int32, len(products))
|
||||||
|
for i, p := range products {
|
||||||
|
pids[i] = p.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
skus, err := q.ProductSku.
|
||||||
|
Select(
|
||||||
|
q.ProductSku.ID,
|
||||||
|
q.ProductSku.ProductID,
|
||||||
|
q.ProductSku.Name,
|
||||||
|
q.ProductSku.Code,
|
||||||
|
q.ProductSku.Price,
|
||||||
|
q.ProductSku.CountMin,
|
||||||
|
).
|
||||||
|
Where(
|
||||||
|
q.ProductSku.ProductID.In(pids...),
|
||||||
|
q.ProductSku.Status.Eq(int32(m.SkuStatusEnabled)),
|
||||||
|
).
|
||||||
|
Order(q.ProductSku.Sort).
|
||||||
|
Find()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pmap := make(map[int32]*m.Product, len(products))
|
||||||
|
for _, p := range products {
|
||||||
|
pmap[p.ID] = p
|
||||||
|
p.Skus = make([]*m.ProductSku, 0)
|
||||||
|
}
|
||||||
|
for _, s := range skus {
|
||||||
|
if p, ok := pmap[s.ProductID]; ok {
|
||||||
|
p.Skus = append(p.Skus, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return products, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 新增产品
|
// 新增产品
|
||||||
func (s *productService) CreateProduct(create *CreateProductData) error {
|
func (s *productService) CreateProduct(create *CreateProductData) error {
|
||||||
return q.Product.Create(&m.Product{
|
return q.Product.Create(&m.Product{
|
||||||
@@ -66,8 +117,14 @@ func (s *productService) UpdateProduct(update *UpdateProductData) error {
|
|||||||
if update.Status != nil {
|
if update.Status != nil {
|
||||||
do = append(do, q.Product.Status.Value(*update.Status))
|
do = append(do, q.Product.Status.Value(*update.Status))
|
||||||
}
|
}
|
||||||
_, err := q.Product.Where(q.Product.ID.Eq(update.Id)).UpdateSimple(do...)
|
r, err := q.Product.Where(q.Product.ID.Eq(update.Id)).UpdateSimple(do...)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("产品状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProductData struct {
|
type UpdateProductData struct {
|
||||||
@@ -81,6 +138,12 @@ type UpdateProductData struct {
|
|||||||
|
|
||||||
// 删除产品
|
// 删除产品
|
||||||
func (s *productService) DeleteProduct(id int32) error {
|
func (s *productService) DeleteProduct(id int32) error {
|
||||||
_, err := q.Product.Where(q.Product.ID.Eq(id)).UpdateColumn(q.Product.DeletedAt, time.Now())
|
r, err := q.Product.Where(q.Product.ID.Eq(id)).UpdateColumn(q.Product.DeletedAt, time.Now())
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("产品状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,14 @@ func (s *productDiscountService) Update(data UpdateProductDiscountData) (err err
|
|||||||
do = append(do, q.ProductDiscount.Discount.Value(*data.Discount))
|
do = append(do, q.ProductDiscount.Discount.Value(*data.Discount))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(data.ID)).UpdateSimple(do...)
|
r, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(data.ID)).UpdateSimple(do...)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("产品折扣状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProductDiscountData struct {
|
type UpdateProductDiscountData struct {
|
||||||
@@ -54,6 +60,12 @@ type UpdateProductDiscountData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *productDiscountService) Delete(id int32) (err error) {
|
func (s *productDiscountService) Delete(id int32) (err error) {
|
||||||
_, err = q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(id)).UpdateColumn(q.ProductDiscount.DeletedAt, time.Now())
|
r, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(id)).UpdateColumn(q.ProductDiscount.DeletedAt, time.Now())
|
||||||
return
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("产品折扣状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func (s *productSkuService) All(product_code string) (result []*m.ProductSku, er
|
|||||||
Joins(q.ProductSku.Product).
|
Joins(q.ProductSku.Product).
|
||||||
Where(q.Product.As("Product").Code.Eq(product_code)).
|
Where(q.Product.As("Product").Code.Eq(product_code)).
|
||||||
Select(q.ProductSku.ALL).
|
Select(q.ProductSku.ALL).
|
||||||
Order(q.ProductSku.CreatedAt.Desc()).
|
Order(q.ProductSku.Sort).
|
||||||
Find()
|
Find()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []
|
|||||||
do = append(do, q.ProductSku.ProductID.Eq(*productId))
|
do = append(do, q.ProductSku.ProductID.Eq(*productId))
|
||||||
}
|
}
|
||||||
return q.ProductSku.
|
return q.ProductSku.
|
||||||
Joins(q.ProductSku.Discount).
|
Joins(q.ProductSku.Discount, q.ProductSku.Product).
|
||||||
Where(do...).
|
Where(do...).
|
||||||
Order(q.ProductSku.CreatedAt.Desc()).
|
Order(q.ProductSku.Sort).
|
||||||
FindByPage(req.GetOffset(), req.GetLimit())
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +42,25 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
|
|||||||
return core.NewBizErr("产品价格的格式不正确", err)
|
return core.NewBizErr("产品价格的格式不正确", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priceMin, err := decimal.NewFromString(create.PriceMin)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("产品最低价格的格式不正确", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
countMin := int32(1)
|
||||||
|
if create.CountMin != nil {
|
||||||
|
countMin = *create.CountMin
|
||||||
|
}
|
||||||
|
|
||||||
return q.ProductSku.Create(&m.ProductSku{
|
return q.ProductSku.Create(&m.ProductSku{
|
||||||
ProductID: create.ProductID,
|
ProductID: create.ProductID,
|
||||||
DiscountId: create.DiscountID,
|
DiscountId: create.DiscountID,
|
||||||
Code: create.Code,
|
Code: create.Code,
|
||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
Price: price,
|
Price: price,
|
||||||
|
PriceMin: priceMin,
|
||||||
|
Sort: create.Sort,
|
||||||
|
CountMin: countMin,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +70,9 @@ type CreateProductSkuData struct {
|
|||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Price string `json:"price"`
|
Price string `json:"price"`
|
||||||
|
PriceMin string `json:"price_min"`
|
||||||
|
Sort int32 `json:"sort"`
|
||||||
|
CountMin *int32 `json:"count_min"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
|
func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
|
||||||
@@ -69,6 +85,13 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
|
|||||||
}
|
}
|
||||||
do = append(do, q.ProductSku.Price.Value(price))
|
do = append(do, q.ProductSku.Price.Value(price))
|
||||||
}
|
}
|
||||||
|
if update.PriceMin != "" {
|
||||||
|
priceMin, err := decimal.NewFromString(update.PriceMin)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("产品最低价格的格式不正确", err)
|
||||||
|
}
|
||||||
|
do = append(do, q.ProductSku.PriceMin.Value(priceMin))
|
||||||
|
}
|
||||||
if update.DiscountID != nil {
|
if update.DiscountID != nil {
|
||||||
do = append(do, q.ProductSku.DiscountId.Value(*update.DiscountID))
|
do = append(do, q.ProductSku.DiscountId.Value(*update.DiscountID))
|
||||||
}
|
}
|
||||||
@@ -78,9 +101,24 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
|
|||||||
if update.Name != nil {
|
if update.Name != nil {
|
||||||
do = append(do, q.ProductSku.Name.Value(*update.Name))
|
do = append(do, q.ProductSku.Name.Value(*update.Name))
|
||||||
}
|
}
|
||||||
|
if update.Status != nil {
|
||||||
|
do = append(do, q.ProductSku.Status.Value(*update.Status))
|
||||||
|
}
|
||||||
|
if update.Sort != nil {
|
||||||
|
do = append(do, q.ProductSku.Sort.Value(*update.Sort))
|
||||||
|
}
|
||||||
|
if update.CountMin != nil {
|
||||||
|
do = append(do, q.ProductSku.CountMin.Value(*update.CountMin))
|
||||||
|
}
|
||||||
|
|
||||||
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
|
r, err := q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("产品套餐状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProductSkuData struct {
|
type UpdateProductSkuData struct {
|
||||||
@@ -89,18 +127,34 @@ type UpdateProductSkuData struct {
|
|||||||
Code *string `json:"code"`
|
Code *string `json:"code"`
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Price *string `json:"price"`
|
Price *string `json:"price"`
|
||||||
|
PriceMin string `json:"price_min"`
|
||||||
|
Status *int32 `json:"status"`
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
|
CountMin *int32 `json:"count_min"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *productSkuService) Delete(id int32) (err error) {
|
func (s *productSkuService) Delete(id int32) (err error) {
|
||||||
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(id)).UpdateColumn(q.ProductSku.DeletedAt, time.Now())
|
r, err := q.ProductSku.Where(q.ProductSku.ID.Eq(id)).UpdateColumn(q.ProductSku.DeletedAt, time.Now())
|
||||||
return
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("产品套餐状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *productSkuService) BatchUpdateDiscount(data BatchUpdateSkuDiscountData) (err error) {
|
func (s *productSkuService) BatchUpdateDiscount(data BatchUpdateSkuDiscountData) (err error) {
|
||||||
_, err = q.ProductSku.Where(q.ProductSku.ProductID.Eq(data.ProductID)).UpdateSimple(
|
r, err := q.ProductSku.Where(q.ProductSku.ProductID.Eq(data.ProductID)).UpdateSimple(
|
||||||
q.ProductSku.DiscountId.Value(data.DiscountID),
|
q.ProductSku.DiscountId.Value(data.DiscountID),
|
||||||
)
|
)
|
||||||
return
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewServErr("产品套餐状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchUpdateSkuDiscountData struct {
|
type BatchUpdateSkuDiscountData struct {
|
||||||
|
|||||||
@@ -1,65 +1,223 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
|
g "platform/web/globals"
|
||||||
"platform/web/globals/orm"
|
"platform/web/globals/orm"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gen/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Proxy = &proxyService{}
|
var Proxy = &proxyService{}
|
||||||
|
|
||||||
type proxyService struct{}
|
type proxyService struct{}
|
||||||
|
|
||||||
// AllProxies 获取所有代理
|
func hasUsedChans(proxyID int32) (bool, error) {
|
||||||
func (s *proxyService) AllProxies(proxyType m.ProxyType, channels bool) ([]*m.Proxy, error) {
|
ctx := context.Background()
|
||||||
proxies, err := q.Proxy.Where(
|
pattern := usedChansKey(proxyID, "*")
|
||||||
q.Proxy.Type.Eq(int(proxyType)),
|
keys, _, err := g.Redis.Scan(ctx, 0, pattern, 1).Result()
|
||||||
q.Proxy.Status.Eq(int(m.ProxyStatusOnline)),
|
|
||||||
).Preload(
|
|
||||||
q.Proxy.Channels.On(q.Channel.ExpiredAt.Gte(time.Now())),
|
|
||||||
).Find()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
return len(keys) > 0, nil
|
||||||
return proxies, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterBaiyin 注册新代理服务
|
func rebuildFreeChans(proxyID int32, addr netip.Addr) error {
|
||||||
func (s *proxyService) RegisterBaiyin(Name string, IP netip.Addr, username, password string) error {
|
if err := remChans(proxyID); err != nil {
|
||||||
|
return err
|
||||||
// 保存代理信息
|
|
||||||
proxy := &m.Proxy{
|
|
||||||
Version: 0,
|
|
||||||
Mac: Name,
|
|
||||||
IP: orm.Inet{Addr: IP},
|
|
||||||
Secret: u.P(fmt.Sprintf("%s:%s", username, password)),
|
|
||||||
Type: m.ProxyTypeBaiYin,
|
|
||||||
Status: m.ProxyStatusOnline,
|
|
||||||
}
|
|
||||||
if err := q.Proxy.Create(proxy); err != nil {
|
|
||||||
return core.NewServErr("保存通道数据失败")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加可用通道到 redis
|
|
||||||
chans := make([]netip.AddrPort, 10000)
|
chans := make([]netip.AddrPort, 10000)
|
||||||
for i := range 10000 {
|
for i := range 10000 {
|
||||||
chans[i] = netip.AddrPortFrom(IP, uint16(i+10000))
|
chans[i] = netip.AddrPortFrom(addr, uint16(i+10000))
|
||||||
}
|
}
|
||||||
err := regChans(proxy.ID, chans)
|
|
||||||
|
if err := regChans(proxyID, chans); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) Page(req core.PageReq) (result []*m.Proxy, count int64, err error) {
|
||||||
|
return q.Proxy.
|
||||||
|
Omit(q.Proxy.Version, q.Proxy.Meta).
|
||||||
|
Order(q.Proxy.CreatedAt.Desc()).
|
||||||
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) All() (result []*m.Proxy, err error) {
|
||||||
|
return q.Proxy.
|
||||||
|
Omit(q.Proxy.Version, q.Proxy.Meta).
|
||||||
|
Order(q.Proxy.CreatedAt.Desc()).
|
||||||
|
Find()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateProxy struct {
|
||||||
|
Mac string `json:"mac" validate:"required"`
|
||||||
|
IP string `json:"ip" validate:"required"`
|
||||||
|
Host *string `json:"host"`
|
||||||
|
Secret *string `json:"secret"`
|
||||||
|
Type *m.ProxyType `json:"type"`
|
||||||
|
Status *m.ProxyStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) Create(create *CreateProxy) error {
|
||||||
|
addr, err := netip.ParseAddr(create.IP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("添加通道失败", err)
|
return core.NewServErr("IP地址格式错误", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
proxy := &m.Proxy{
|
||||||
|
Mac: create.Mac,
|
||||||
|
IP: orm.Inet{Addr: addr},
|
||||||
|
Host: create.Host,
|
||||||
|
Secret: create.Secret,
|
||||||
|
Type: u.Else(create.Type, m.ProxyTypeSelfHosted),
|
||||||
|
Status: u.Else(create.Status, m.ProxyStatusOffline),
|
||||||
|
}
|
||||||
|
if err := tx.Proxy.Create(proxy); err != nil {
|
||||||
|
return core.NewServErr("保存代理数据失败", err)
|
||||||
|
}
|
||||||
|
if err := rebuildFreeChans(proxy.ID, addr); err != nil {
|
||||||
|
return core.NewServErr("初始化代理通道失败", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateProxy struct {
|
||||||
|
ID int32 `json:"id" validate:"required"`
|
||||||
|
Mac *string `json:"mac"`
|
||||||
|
IP *string `json:"ip"`
|
||||||
|
Host *string `json:"host"`
|
||||||
|
Secret *string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) Update(update *UpdateProxy) error {
|
||||||
|
simples := make([]field.AssignExpr, 0)
|
||||||
|
hasSideEffect := false
|
||||||
|
|
||||||
|
if update.Mac != nil {
|
||||||
|
hasSideEffect = true
|
||||||
|
simples = append(simples, q.Proxy.Mac.Value(*update.Mac))
|
||||||
|
}
|
||||||
|
if update.IP != nil {
|
||||||
|
addr, err := netip.ParseAddr(*update.IP)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("IP地址格式错误", err)
|
||||||
|
}
|
||||||
|
hasSideEffect = true
|
||||||
|
simples = append(simples, q.Proxy.IP.Value(orm.Inet{Addr: addr}))
|
||||||
|
}
|
||||||
|
if update.Host != nil {
|
||||||
|
simples = append(simples, q.Proxy.Host.Value(*update.Host))
|
||||||
|
}
|
||||||
|
if update.Secret != nil {
|
||||||
|
hasSideEffect = true
|
||||||
|
simples = append(simples, q.Proxy.Secret.Value(*update.Secret))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(simples) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasSideEffect {
|
||||||
|
used, err := hasUsedChans(update.ID)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("检查代理通道状态失败", err)
|
||||||
|
}
|
||||||
|
if used {
|
||||||
|
return core.NewBizErr("代理存在未关闭通道,禁止修改")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := q.Proxy.
|
||||||
|
Where(
|
||||||
|
q.Proxy.ID.Eq(update.ID),
|
||||||
|
q.Proxy.Status.Eq(int(m.ProxyStatusOffline)),
|
||||||
|
).
|
||||||
|
UpdateSimple(simples...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rs.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("代理未下线,禁止修改")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnregisterBaiyin 注销代理服务
|
func (s *proxyService) Remove(id int32) error {
|
||||||
func (s *proxyService) UnregisterBaiyin(id int) error {
|
used, err := hasUsedChans(id)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewServErr("检查代理通道状态失败", err)
|
||||||
|
}
|
||||||
|
if used {
|
||||||
|
return core.NewBizErr("代理存在未关闭通道,禁止删除")
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := q.Proxy.
|
||||||
|
Where(
|
||||||
|
q.Proxy.ID.Eq(id),
|
||||||
|
q.Proxy.Status.Eq(int(m.ProxyStatusOffline)),
|
||||||
|
).
|
||||||
|
UpdateColumn(q.Proxy.DeletedAt, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rs.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("代理未下线,禁止删除")
|
||||||
|
}
|
||||||
|
if err := remChans(id); err != nil {
|
||||||
|
return core.NewServErr("注销代理通道失败", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateProxyStatus struct {
|
||||||
|
ID int32 `json:"id" validate:"required"`
|
||||||
|
Status m.ProxyStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) UpdateStatus(update *UpdateProxyStatus) error {
|
||||||
|
return q.Q.Transaction(func(tx *q.Query) error {
|
||||||
|
proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(update.ID)).Take()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxy.Status == update.Status {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.Status == m.ProxyStatusOnline {
|
||||||
|
if err := rebuildFreeChans(proxy.ID, proxy.IP.Addr); err != nil {
|
||||||
|
return core.NewServErr("初始化代理通道失败", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = q.Proxy.
|
||||||
|
Where(q.Proxy.ID.Eq(update.ID)).
|
||||||
|
UpdateSimple(q.Proxy.Status.Value(int(update.Status)))
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func proxyGateway(proxy *m.Proxy) (g.GatewayClient, error) {
|
||||||
|
|
||||||
|
secret := strings.Split(u.Z(proxy.Secret), ":")
|
||||||
|
if len(secret) != 2 {
|
||||||
|
return nil, core.NewServErr(fmt.Sprintf("代理 %s 密钥格式错误", proxy.IP.String()), nil)
|
||||||
|
}
|
||||||
|
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
|
||||||
|
|
||||||
|
return gateway, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"platform/pkg/u"
|
"platform/pkg/u"
|
||||||
"platform/web/core"
|
"platform/web/core"
|
||||||
m "platform/web/models"
|
m "platform/web/models"
|
||||||
@@ -28,11 +29,6 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
|
|||||||
|
|
||||||
return q.Q.Transaction(func(q *q.Query) error {
|
return q.Q.Transaction(func(q *q.Query) error {
|
||||||
|
|
||||||
// 更新用户余额
|
|
||||||
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
|
|
||||||
return core.NewServErr("更新用户余额失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存套餐
|
// 保存套餐
|
||||||
resource, err := s.Create(q, user.ID, now, data)
|
resource, err := s.Create(q, user.ID, now, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,14 +36,19 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// 生成账单
|
||||||
err = Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
|
bill, err := Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新用户余额
|
||||||
|
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil, &bill.ID); err != nil {
|
||||||
|
return core.NewServErr("更新用户余额失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
// 核销优惠券
|
// 核销优惠券
|
||||||
if detail.CouponId != nil {
|
if detail.CouponUserId != nil {
|
||||||
err = Coupon.UseCoupon(q, *detail.CouponId)
|
err = Coupon.UseCoupon(q, *detail.CouponUserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("核销优惠券失败", err)
|
return core.NewServErr("核销优惠券失败", err)
|
||||||
}
|
}
|
||||||
@@ -62,9 +63,10 @@ func (s *resourceService) Create(q *q.Query, uid int32, now time.Time, data *Cre
|
|||||||
var resource = m.Resource{
|
var resource = m.Resource{
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
ResourceNo: u.P(ID.GenReadable("res")),
|
ResourceNo: u.P(ID.GenReadable("res")),
|
||||||
Active: true,
|
|
||||||
Type: data.Type,
|
Type: data.Type,
|
||||||
Code: data.Type.Code(),
|
Code: data.Type.Code(),
|
||||||
|
Active: true,
|
||||||
|
CheckIP: true,
|
||||||
}
|
}
|
||||||
switch data.Type {
|
switch data.Type {
|
||||||
|
|
||||||
@@ -120,59 +122,70 @@ func (s *resourceService) Create(q *q.Query, uid int32, now time.Time, data *Cre
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *resourceService) Update(data *UpdateResourceData) error {
|
func (s *resourceService) Update(data *UpdateResourceData) error {
|
||||||
if data.Active == nil {
|
|
||||||
return core.NewBizErr("更新套餐失败,active 不能为空")
|
|
||||||
}
|
|
||||||
|
|
||||||
do := make([]field.AssignExpr, 0)
|
do := make([]field.AssignExpr, 0)
|
||||||
if data.Active != nil {
|
if data.Active != nil {
|
||||||
do = append(do, q.Resource.Active.Value(*data.Active))
|
do = append(do, q.Resource.Active.Value(*data.Active))
|
||||||
}
|
}
|
||||||
|
if data.CheckIP != nil {
|
||||||
|
do = append(do, q.Resource.CheckIP.Value(*data.CheckIP))
|
||||||
|
}
|
||||||
|
|
||||||
_, err := q.Resource.
|
r, err := q.Resource.
|
||||||
Where(q.Resource.ID.Eq(data.Id)).
|
Where(q.Resource.ID.Eq(data.Id)).
|
||||||
UpdateSimple(do...)
|
UpdateSimple(do...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新套餐失败", err)
|
return core.NewServErr("更新套餐失败", err)
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("套餐状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateResourceData struct {
|
type UpdateResourceData struct {
|
||||||
core.IdReq
|
core.IdReq
|
||||||
Active *bool `json:"active"`
|
Active *bool `json:"active"`
|
||||||
|
CheckIP *bool `json:"checkip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, couponCode *string) (*m.ProductSku, *m.ProductDiscount, *m.Coupon, decimal.Decimal, decimal.Decimal, error) {
|
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, cuid *int32) (*m.ProductSku, *m.ProductDiscount, *m.CouponUser, decimal.Decimal, decimal.Decimal, decimal.Decimal, error) {
|
||||||
|
if count <= 0 {
|
||||||
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr("购买数量必须大于 0")
|
||||||
|
}
|
||||||
|
|
||||||
sku, err := q.ProductSku.
|
sku, err := q.ProductSku.
|
||||||
Joins(q.ProductSku.Discount).
|
Joins(q.ProductSku.Discount).
|
||||||
Where(q.ProductSku.Code.Eq(skuCode)).
|
Where(q.ProductSku.Code.Eq(skuCode), q.ProductSku.Status.Eq(int32(m.SkuStatusEnabled))).
|
||||||
Take()
|
Take()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("产品不可用", err)
|
slog.Debug("查询产品失败", "skuCode", skuCode)
|
||||||
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr("产品不可用", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if count < sku.CountMin {
|
||||||
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewBizErr(fmt.Sprintf("购买数量不能少于 %d", sku.CountMin))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原价
|
// 原价
|
||||||
price := sku.Price
|
amountMin := sku.PriceMin.Mul(decimal.NewFromInt32(count))
|
||||||
amount := price.Mul(decimal.NewFromInt32(count))
|
amount := sku.Price.Mul(decimal.NewFromInt32(count))
|
||||||
|
|
||||||
// 折扣价
|
// 折扣价
|
||||||
discount := sku.Discount
|
discount := sku.Discount
|
||||||
if discount == nil {
|
if discount == nil {
|
||||||
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("价格查询失败", err)
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewServErr("产品未配置折扣", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
discountRate := discount.Rate()
|
discountRate := discount.Rate()
|
||||||
if user != nil && user.DiscountID != nil { // 用户特殊优惠
|
if user != nil && user.DiscountID != nil { // 用户特殊优惠
|
||||||
uDiscount, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(*user.DiscountID)).Take()
|
uDiscount, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(*user.DiscountID)).Take()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("客户特殊价查询失败", err)
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewServErr("客户特殊价查询失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
uDiscountRate := uDiscount.Rate()
|
uDiscountRate := uDiscount.Rate()
|
||||||
if uDiscountRate.Cmp(discountRate) > 0 {
|
if uDiscountRate.Cmp(discountRate) < 0 {
|
||||||
discountRate = uDiscountRate
|
discountRate = uDiscountRate
|
||||||
discount = uDiscount
|
discount = uDiscount
|
||||||
}
|
}
|
||||||
@@ -180,30 +193,33 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
|
|||||||
discounted := amount.Mul(discountRate)
|
discounted := amount.Mul(discountRate)
|
||||||
|
|
||||||
// 优惠价
|
// 优惠价
|
||||||
uid := (*int32)(nil)
|
coupon := (*m.CouponUser)(nil)
|
||||||
if user != nil {
|
|
||||||
uid = &user.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
coupon := (*m.Coupon)(nil)
|
|
||||||
couponApplied := discounted.Copy()
|
couponApplied := discounted.Copy()
|
||||||
if couponCode != nil {
|
if user != nil && cuid != nil {
|
||||||
var err error
|
var err error
|
||||||
coupon, err = Coupon.GetCouponAvailableByCode(*couponCode, discounted, uid)
|
coupon, err = Coupon.GetUserCoupon(user.ID, *cuid, discounted)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, decimal.Zero, decimal.Zero, err
|
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, err
|
||||||
}
|
}
|
||||||
couponApplied = discounted.Sub(coupon.Amount)
|
couponApplied = discounted.Sub(coupon.Coupon.Amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sku, discount, coupon, discounted, couponApplied, nil
|
// 约束到最低价格
|
||||||
|
if discounted.Cmp(amountMin) < 0 {
|
||||||
|
discounted = amountMin.Copy()
|
||||||
|
}
|
||||||
|
if couponApplied.Cmp(amountMin) < 0 {
|
||||||
|
couponApplied = amountMin.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sku, discount, coupon, amount.RoundCeil(2), discounted.RoundCeil(2), couponApplied.RoundCeil(2), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateResourceData struct {
|
type CreateResourceData struct {
|
||||||
Type m.ResourceType `json:"type" validate:"required"`
|
Type m.ResourceType `json:"type" validate:"required"`
|
||||||
Short *CreateShortResourceData `json:"short,omitempty"`
|
Short *CreateShortResourceData `json:"short,omitempty"`
|
||||||
Long *CreateLongResourceData `json:"long,omitempty"`
|
Long *CreateLongResourceData `json:"long,omitempty"`
|
||||||
CouponCode *string `json:"coupon,omitempty"`
|
CouponId *int32 `json:"coupon,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateShortResourceData struct {
|
type CreateShortResourceData struct {
|
||||||
@@ -244,20 +260,20 @@ func (data *CreateResourceData) Code() string {
|
|||||||
|
|
||||||
case data.Type == m.ResourceTypeShort && data.Short != nil:
|
case data.Type == m.ResourceTypeShort && data.Short != nil:
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"mode=%s,live=%d,expire=%d",
|
"mode=%s&live=%d&expire=%d",
|
||||||
data.Short.Mode.Code(), data.Short.Live, u.Else(data.Short.Expire, 0),
|
data.Short.Mode.Code(), data.Short.Live, u.Else(data.Short.Expire, 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
case data.Type == m.ResourceTypeLong && data.Long != nil:
|
case data.Type == m.ResourceTypeLong && data.Long != nil:
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"mode=%s,live=%d,expire=%d",
|
"mode=%s&live=%d&expire=%d",
|
||||||
data.Long.Mode.Code(), data.Long.Live, u.Else(data.Long.Expire, 0),
|
data.Long.Mode.Code(), data.Long.Live, u.Else(data.Long.Expire, 0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error) {
|
func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error) {
|
||||||
sku, discount, coupon, amount, actual, err := Resource.CalcPrice(data.Code(), data.Count(), user, data.CouponCode)
|
sku, discount, coupon, amount, discounted, actual, err := Resource.CalcPrice(data.Code(), data.Count(), user, data.CouponId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -266,17 +282,16 @@ func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error)
|
|||||||
if discount != nil {
|
if discount != nil {
|
||||||
discountId = &discount.ID
|
discountId = &discount.ID
|
||||||
}
|
}
|
||||||
var couponId *int32 = nil
|
var couponUserId *int32 = nil
|
||||||
if coupon != nil {
|
if coupon != nil {
|
||||||
couponId = &coupon.ID
|
couponUserId = &coupon.ID
|
||||||
}
|
}
|
||||||
return &TradeDetail{
|
return &TradeDetail{
|
||||||
data,
|
data,
|
||||||
m.TradeTypePurchase,
|
m.TradeTypePurchase,
|
||||||
sku.Name,
|
sku.Name,
|
||||||
amount, actual,
|
amount, discounted, actual,
|
||||||
discountId, discount,
|
discountId, couponUserId,
|
||||||
couponId, coupon,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
|||||||
|
|
||||||
err = q.Q.Transaction(func(q *q.Query) error {
|
err = q.Q.Transaction(func(q *q.Query) error {
|
||||||
// 更新交易信息
|
// 更新交易信息
|
||||||
_, err := q.Trade.
|
r, err := q.Trade.
|
||||||
Where(
|
Where(
|
||||||
q.Trade.InnerNo.Eq(interNo),
|
q.Trade.InnerNo.Eq(interNo),
|
||||||
q.Trade.Status.Eq(int(m.TradeStatusPending)),
|
q.Trade.Status.Eq(int(m.TradeStatusPending)),
|
||||||
@@ -299,20 +299,24 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新交易信息失败", err)
|
return core.NewServErr("更新交易信息失败", err)
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("交易状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
switch trade.Type {
|
switch trade.Type {
|
||||||
case m.TradeTypeRecharge:
|
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)
|
bill, err := Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新用户余额
|
||||||
|
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil, &bill.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
case m.TradeTypePurchase:
|
case m.TradeTypePurchase:
|
||||||
data, ok := detail.Product.(*CreateResourceData)
|
data, ok := detail.Product.(*CreateResourceData)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -326,14 +330,14 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// 生成账单
|
||||||
err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
|
_, err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("生成账单失败", err)
|
return core.NewServErr("生成账单失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 核销优惠券
|
// 核销优惠券
|
||||||
if detail.CouponId != nil {
|
if detail.CouponUserId != nil {
|
||||||
err = Coupon.UseCoupon(q, *detail.CouponId)
|
err = Coupon.UseCoupon(q, *detail.CouponUserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("核销优惠券失败", err)
|
return core.NewServErr("核销优惠券失败", err)
|
||||||
}
|
}
|
||||||
@@ -405,7 +409,7 @@ func (s *tradeService) CancelTrade(ref *TradeRef) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (s *tradeService) OnCancelTrade(tradeNo string, now time.Time) error {
|
func (s *tradeService) OnCancelTrade(tradeNo string, now time.Time) error {
|
||||||
_, err := q.Trade.
|
r, err := q.Trade.
|
||||||
Where(
|
Where(
|
||||||
q.Trade.InnerNo.Eq(tradeNo),
|
q.Trade.InnerNo.Eq(tradeNo),
|
||||||
q.Trade.Status.Eq(int(m.TradeStatusPending)),
|
q.Trade.Status.Eq(int(m.TradeStatusPending)),
|
||||||
@@ -417,6 +421,9 @@ func (s *tradeService) OnCancelTrade(tradeNo string, now time.Time) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新交易状态失败", err)
|
return core.NewServErr("更新交易状态失败", err)
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("交易状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -614,15 +621,14 @@ type ProductData interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TradeDetail struct {
|
type TradeDetail struct {
|
||||||
Product ProductData `json:"product"`
|
Product ProductData `json:"product"`
|
||||||
Type m.TradeType `json:"type"`
|
Type m.TradeType `json:"type"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Amount decimal.Decimal `json:"amount"`
|
Amount decimal.Decimal `json:"amount"`
|
||||||
Actual decimal.Decimal `json:"actual"`
|
Discounted decimal.Decimal `json:"discounted"`
|
||||||
DiscountId *int32 `json:"discount_id,omitempty"`
|
Actual decimal.Decimal `json:"actual"`
|
||||||
Discount *m.ProductDiscount `json:"-"` // 不应缓存
|
DiscountId *int32 `json:"discount_id,omitempty"`
|
||||||
CouponId *int32 `json:"coupon_id,omitempty"`
|
CouponUserId *int32 `json:"coupon_id,omitempty"`
|
||||||
Coupon *m.Coupon `json:"-"` // 不应缓存
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TradeErr string
|
type TradeErr string
|
||||||
|
|||||||
@@ -39,18 +39,18 @@ func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Deci
|
|||||||
}
|
}
|
||||||
|
|
||||||
return q.Q.Transaction(func(q *q.Query) error {
|
return q.Q.Transaction(func(q *q.Query) error {
|
||||||
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
|
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
|
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32, billId *int32) error {
|
||||||
balance := user.Balance.Add(amount)
|
balance := user.Balance.Add(amount)
|
||||||
if balance.IsNegative() {
|
if balance.IsNegative() {
|
||||||
return core.NewServErr("用户余额不足")
|
return core.NewServErr("用户余额不足")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新余额
|
// 更新余额
|
||||||
_, err := q.User.
|
r, err := q.User.
|
||||||
Where(
|
Where(
|
||||||
q.User.ID.Eq(user.ID),
|
q.User.ID.Eq(user.ID),
|
||||||
q.User.Balance.Eq(user.Balance),
|
q.User.Balance.Eq(user.Balance),
|
||||||
@@ -61,11 +61,15 @@ func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Dec
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return core.NewServErr("更新用户余额失败", err)
|
return core.NewServErr("更新用户余额失败", err)
|
||||||
}
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("余额状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
// 新增动账记录
|
// 新增动账记录
|
||||||
err = q.BalanceActivity.Create(&m.BalanceActivity{
|
err = q.BalanceActivity.Create(&m.BalanceActivity{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
AdminID: adminId,
|
AdminID: adminId,
|
||||||
|
BillID: billId,
|
||||||
Amount: amount.StringFixed(2),
|
Amount: amount.StringFixed(2),
|
||||||
BalancePrev: user.Balance.StringFixed(2),
|
BalancePrev: user.Balance.StringFixed(2),
|
||||||
BalanceCurr: balance.StringFixed(2),
|
BalanceCurr: balance.StringFixed(2),
|
||||||
@@ -87,13 +91,15 @@ type UpdateBalanceData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
|
func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
|
||||||
|
if data.Amount <= 0 {
|
||||||
|
return nil, core.NewBizErr("充值金额必须大于0")
|
||||||
|
}
|
||||||
amount := decimal.NewFromInt(int64(data.Amount)).Div(decimal.NewFromInt(100))
|
amount := decimal.NewFromInt(int64(data.Amount)).Div(decimal.NewFromInt(100))
|
||||||
return &TradeDetail{
|
return &TradeDetail{
|
||||||
data,
|
data,
|
||||||
m.TradeTypeRecharge,
|
m.TradeTypeRecharge,
|
||||||
fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)),
|
fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)),
|
||||||
amount, amount,
|
amount, amount, amount,
|
||||||
nil, nil,
|
|
||||||
nil, nil,
|
nil, nil,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -118,7 +124,6 @@ func (s *userService) CreateByAdmin(data CreateUserByAdminData) error {
|
|||||||
Email: data.Email,
|
Email: data.Email,
|
||||||
Password: hashedPwd,
|
Password: hashedPwd,
|
||||||
Source: &source,
|
Source: &source,
|
||||||
Name: data.Name,
|
|
||||||
Avatar: data.Avatar,
|
Avatar: data.Avatar,
|
||||||
Status: u.Else(data.Status, m.UserStatusEnabled),
|
Status: u.Else(data.Status, m.UserStatusEnabled),
|
||||||
ContactQQ: data.ContactQQ,
|
ContactQQ: data.ContactQQ,
|
||||||
@@ -141,7 +146,6 @@ type CreateUserByAdminData struct {
|
|||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Password *string `json:"password"`
|
Password *string `json:"password"`
|
||||||
Name *string `json:"name"`
|
|
||||||
Avatar *string `json:"avatar"`
|
Avatar *string `json:"avatar"`
|
||||||
Status *m.UserStatus `json:"status"`
|
Status *m.UserStatus `json:"status"`
|
||||||
ContactQQ *string `json:"contact_qq"`
|
ContactQQ *string `json:"contact_qq"`
|
||||||
@@ -203,12 +207,18 @@ func (s *userService) UpdateByAdmin(data UpdateUserByAdminData) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := q.User.Where(q.User.ID.Eq(data.ID)).UpdateSimple(do...)
|
r, err := q.User.Where(q.User.ID.Eq(data.ID)).UpdateSimple(do...)
|
||||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
|
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户状态已过期")
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserByAdminData struct {
|
type UpdateUserByAdminData struct {
|
||||||
@@ -230,6 +240,12 @@ type UpdateUserByAdminData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *userService) RemoveByAdmin(id int32) error {
|
func (s *userService) RemoveByAdmin(id int32) error {
|
||||||
_, err := q.User.Where(q.User.ID.Eq(id)).UpdateColumn(q.User.DeletedAt, time.Now())
|
r, err := q.User.Where(q.User.ID.Eq(id)).UpdateColumn(q.User.DeletedAt, time.Now())
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.RowsAffected == 0 {
|
||||||
|
return core.NewBizErr("用户状态已过期")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (s *verifierService) SendSms(ctx context.Context, phone string, purpose Ver
|
|||||||
code := rand.Intn(900000) + 100000 // 6-digit code between 100000-999999
|
code := rand.Intn(900000) + 100000 // 6-digit code between 100000-999999
|
||||||
|
|
||||||
// 发送短信验证码
|
// 发送短信验证码
|
||||||
if env.DebugExternalChange {
|
if env.RunMode == env.RunModeProd {
|
||||||
params, err := json.Marshal(map[string]string{
|
params, err := json.Marshal(map[string]string{
|
||||||
"code": strconv.Itoa(code),
|
"code": strconv.Itoa(code),
|
||||||
})
|
})
|
||||||
@@ -89,8 +89,8 @@ func (s *verifierService) SendSms(ctx context.Context, phone string, purpose Ver
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *verifierService) VerifySms(ctx context.Context, phone, code string) error {
|
func (s *verifierService) VerifySms(ctx context.Context, phone, code string, purpose VerifierSmsPurpose) error {
|
||||||
key := smsKey(phone, VerifierSmsPurposeLogin)
|
key := smsKey(phone, purpose)
|
||||||
keyLock := key + ":lock"
|
keyLock := key + ":lock"
|
||||||
|
|
||||||
err := g.Redis.Watch(ctx, func(tx *redis.Tx) error {
|
err := g.Redis.Watch(ctx, func(tx *redis.Tx) error {
|
||||||
@@ -146,7 +146,8 @@ func smsKey(phone string, purpose VerifierSmsPurpose) string {
|
|||||||
type VerifierSmsPurpose int
|
type VerifierSmsPurpose int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VerifierSmsPurposeLogin VerifierSmsPurpose = iota // 登录
|
VerifierSmsPurposeLogin VerifierSmsPurpose = iota // 登录
|
||||||
|
VerifierSmsPurposePassword // 修改密码
|
||||||
)
|
)
|
||||||
|
|
||||||
// region 服务异常
|
// region 服务异常
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func HandleCompleteTrade(_ context.Context, task *asynq.Task) error {
|
func HandleCompleteTrade(_ context.Context, task *asynq.Task) error {
|
||||||
|
slog.Info("[event]尝试结束交易")
|
||||||
var event events.CloseTradeData
|
var event events.CloseTradeData
|
||||||
if err := json.Unmarshal(task.Payload(), &event); err != nil {
|
if err := json.Unmarshal(task.Payload(), &event); err != nil {
|
||||||
return fmt.Errorf("解析任务参数失败: %w", err)
|
return fmt.Errorf("解析任务参数失败: %w", err)
|
||||||
@@ -30,11 +31,11 @@ func HandleCompleteTrade(_ context.Context, task *asynq.Task) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Trade.CompleteTrade(user, &data); err != nil {
|
if err := s.Trade.CompleteTrade(user, &data); err != nil {
|
||||||
slog.Debug("完成交易失败[异步结束订单]", "err", err)
|
slog.Debug("结束交易失败:完成交易失败", "err", err)
|
||||||
|
|
||||||
// 交易无法完成,关闭交易
|
// 交易无法完成,关闭交易
|
||||||
if err := s.Trade.CancelTrade(&data); err != nil {
|
if err := s.Trade.CancelTrade(&data); err != nil {
|
||||||
return fmt.Errorf("取消交易失败[异步结束订单]: %w", err)
|
return fmt.Errorf("结束交易失败:取消交易失败: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +44,21 @@ func HandleCompleteTrade(_ context.Context, task *asynq.Task) error {
|
|||||||
|
|
||||||
func HandleRemoveChannel(_ context.Context, task *asynq.Task) (err error) {
|
func HandleRemoveChannel(_ context.Context, task *asynq.Task) (err error) {
|
||||||
batch := string(task.Payload())
|
batch := string(task.Payload())
|
||||||
|
slog.Info("[event]删除通道", "batch", batch)
|
||||||
|
|
||||||
err = s.Channel.RemoveChannels(batch)
|
err = s.Channel.RemoveChannels(batch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("删除通道失败: %w", err)
|
return fmt.Errorf("删除通道失败: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleRefreshEdges(_ context.Context, task *asynq.Task) (err error) {
|
||||||
|
slog.Info("[event]刷新边缘节点")
|
||||||
|
|
||||||
|
err = s.Channel.RefreshEdges()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("刷新边缘节点失败: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
61
web/web.go
61
web/web.go
@@ -42,6 +42,10 @@ func RunApp(pCtx context.Context) error {
|
|||||||
return RunTask(ctx)
|
return RunTask(ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
g.Go(func() error {
|
||||||
|
return RunCron(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +84,18 @@ func RunWeb(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunTask(ctx context.Context) error {
|
func RunTask(ctx context.Context) error {
|
||||||
var server = asynq.NewServerFromRedisClient(deps.Redis, asynq.Config{
|
server := asynq.NewServerFromRedisClient(deps.Redis, asynq.Config{
|
||||||
ShutdownTimeout: 5 * time.Second,
|
ShutdownTimeout: 5 * time.Second,
|
||||||
ErrorHandler: asynq.ErrorHandlerFunc(func(ctx context.Context, task *asynq.Task, err error) {
|
ErrorHandler: asynq.ErrorHandlerFunc(func(ctx context.Context, task *asynq.Task, err error) {
|
||||||
slog.Error("任务执行失败", "task", task.Type(), "error", err)
|
slog.Error("任务执行失败", "task", task.Type(), "error", err)
|
||||||
}),
|
}),
|
||||||
|
Logger: &AppAsynqLogger{},
|
||||||
})
|
})
|
||||||
|
|
||||||
var mux = asynq.NewServeMux()
|
var mux = asynq.NewServeMux()
|
||||||
mux.HandleFunc(events.RemoveChannel, tasks.HandleRemoveChannel)
|
mux.HandleFunc(events.RemoveChannel, tasks.HandleRemoveChannel)
|
||||||
mux.HandleFunc(events.CloseTrade, tasks.HandleCompleteTrade)
|
mux.HandleFunc(events.CloseTrade, tasks.HandleCompleteTrade)
|
||||||
|
mux.HandleFunc(events.RefreshEdge, tasks.HandleRefreshEdges)
|
||||||
|
|
||||||
// 停止服务
|
// 停止服务
|
||||||
go func() {
|
go func() {
|
||||||
@@ -105,3 +111,56 @@ func RunTask(ctx context.Context) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunCron(ctx context.Context) error {
|
||||||
|
cron := asynq.NewSchedulerFromRedisClient(deps.Redis, &asynq.SchedulerOpts{
|
||||||
|
Logger: &AppAsynqLogger{},
|
||||||
|
Location: time.Local,
|
||||||
|
})
|
||||||
|
|
||||||
|
cron.Register("0/10 * * * *", events.NewRefreshEdge())
|
||||||
|
|
||||||
|
// 停止服务
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
cron.Shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 启动服务
|
||||||
|
err := cron.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("定时任务服务运行失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppAsynqLogger struct{}
|
||||||
|
|
||||||
|
func (l *AppAsynqLogger) Debug(args ...any) {
|
||||||
|
slog.Debug("Asynq", anyToAttrs(args)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AppAsynqLogger) Info(args ...any) {
|
||||||
|
slog.Info("Asynq", anyToAttrs(args)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AppAsynqLogger) Warn(args ...any) {
|
||||||
|
slog.Warn("Asynq", anyToAttrs(args)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AppAsynqLogger) Error(args ...any) {
|
||||||
|
slog.Error("Asynq", anyToAttrs(args)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AppAsynqLogger) Fatal(args ...any) {
|
||||||
|
slog.Error("Asynq[Fatal]", anyToAttrs(args)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func anyToAttrs(args ...any) []any {
|
||||||
|
attrs := make([]any, len(args))
|
||||||
|
for i, arg := range args {
|
||||||
|
attrs[i] = slog.Any(fmt.Sprintf("arg%d", i), arg)
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user