Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 982cbb4cab | |||
| a964fe4d69 | |||
| 6db3caaecb | |||
| fd475d3e63 | |||
| 9b3546b45f | |||
| b8c8c7d7b1 | |||
| 58b8849d8d |
@@ -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
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -1,8 +1,10 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
交易信息持久化
|
proxy 的删除和更新,锁粒度应该有问题
|
||||||
|
|
||||||
用户请求需要检查数据权限
|
最低价格 0.01
|
||||||
|
|
||||||
|
交易信息持久化
|
||||||
|
|
||||||
用反射实现环境变量解析,以简化函数签名
|
用反射实现环境变量解析,以简化函数签名
|
||||||
|
|
||||||
@@ -10,12 +12,8 @@
|
|||||||
|
|
||||||
分离 task 的客户端,支持多进程(prefork 必要!)
|
分离 task 的客户端,支持多进程(prefork 必要!)
|
||||||
|
|
||||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|
||||||
|
|
||||||
慢速请求底层调用埋点监控
|
慢速请求底层调用埋点监控
|
||||||
|
|
||||||
数据库转模型文件
|
|
||||||
|
|
||||||
冷数据迁移方案
|
冷数据迁移方案
|
||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
@@ -49,13 +47,6 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
|||||||
3. 异步回调事件,收到支付成功事件后自动完成订单
|
3. 异步回调事件,收到支付成功事件后自动完成订单
|
||||||
4. 用户退出支付界面,客户端主动发起关闭订单
|
4. 用户退出支付界面,客户端主动发起关闭订单
|
||||||
|
|
||||||
### 产品字典表
|
|
||||||
|
|
||||||
| 代码 | 产品 |
|
|
||||||
| ----- | ------------ |
|
|
||||||
| short | 短效动态代理 |
|
|
||||||
| long | 长效动态代理 |
|
|
||||||
|
|
||||||
### 节点分配与存储逻辑
|
### 节点分配与存储逻辑
|
||||||
|
|
||||||
提取:
|
提取:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
30
pkg/u/u.go
30
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
|
||||||
|
}
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
// 数组
|
// 数组
|
||||||
// ====================
|
// ====================
|
||||||
@@ -110,3 +122,21 @@ func CombineErrors(errs []error) error {
|
|||||||
}
|
}
|
||||||
return combinedErr
|
return combinedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
// 业务
|
||||||
|
// ====================
|
||||||
|
|
||||||
|
func MaskPhone(phone string) string {
|
||||||
|
if len(phone) < 11 {
|
||||||
|
return phone
|
||||||
|
}
|
||||||
|
return phone[:3] + "****" + phone[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaskIdNo(idNo string) string {
|
||||||
|
if len(idNo) < 18 {
|
||||||
|
return idNo
|
||||||
|
}
|
||||||
|
return idNo[:3] + "*********" + idNo[14:]
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,9 +15,93 @@ insert into admin (username, password, name, lock) values ('admin', '', '超级
|
|||||||
-- region 产品
|
-- region 产品
|
||||||
-- ====================
|
-- ====================
|
||||||
|
|
||||||
insert into product (code, name, description) values ('short', '短效动态', '短效动态');
|
delete from product where true;
|
||||||
insert into product (code, name, description) values ('long', '长效动态', '长效动态');
|
|
||||||
insert into product (code, name, description) values ('static', '长效静态', '长效静态');
|
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 权限
|
-- region 权限
|
||||||
@@ -42,7 +126,8 @@ insert into permission (name, description, sort) values
|
|||||||
('channel', 'IP', 11),
|
('channel', 'IP', 11),
|
||||||
('trade', '交易', 12),
|
('trade', '交易', 12),
|
||||||
('bill', '账单', 13),
|
('bill', '账单', 13),
|
||||||
('balance_activity', '余额变动', 14);
|
('balance_activity', '余额变动', 14),
|
||||||
|
('proxy', '代理', 15);
|
||||||
|
|
||||||
-- --------------------------
|
-- --------------------------
|
||||||
-- level 2
|
-- level 2
|
||||||
@@ -105,6 +190,11 @@ 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:read', '读取 IP 列表', 1),
|
||||||
((select id from permission where name = 'channel' and deleted_at is null), 'channel:write', '写入 IP', 2);
|
((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 子权限
|
-- trade 子权限
|
||||||
insert into permission (parent_id, name, description, sort) values
|
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:read', '读取交易列表', 1),
|
||||||
@@ -127,6 +217,10 @@ insert into permission (parent_id, name, description, sort) values
|
|||||||
insert into permission (parent_id, name, description, sort) values
|
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);
|
((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 子权限
|
-- resource:short 子权限
|
||||||
insert into permission (parent_id, name, description, sort) values
|
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);
|
((select id from permission where name = 'resource:short' and deleted_at is null), 'resource:short:read', '读取用户短效动态套餐列表', 1);
|
||||||
|
|||||||
@@ -761,6 +761,8 @@ create table product_sku (
|
|||||||
price decimal not null,
|
price decimal not null,
|
||||||
price_min decimal not null,
|
price_min decimal not null,
|
||||||
status int not null default 1,
|
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
|
||||||
@@ -778,6 +780,8 @@ comment on column product_sku.code is 'SKU 代码:格式为 key=value,key=valu
|
|||||||
comment on column product_sku.name is 'SKU 可读名称';
|
comment on column product_sku.name is 'SKU 可读名称';
|
||||||
comment on column product_sku.price_min is '最低价格';
|
comment on column product_sku.price_min is '最低价格';
|
||||||
comment on column product_sku.status is 'SKU状态:0-禁用,1-正常';
|
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 '删除时间';
|
||||||
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -537,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 {
|
||||||
@@ -579,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"`
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ const (
|
|||||||
ScopeChannelReadOfUser = string("channel:read:of_user") // 读取指定用户的 IP 列表
|
ScopeChannelReadOfUser = string("channel:read:of_user") // 读取指定用户的 IP 列表
|
||||||
ScopeChannelWrite = string("channel:write") // 写入 IP
|
ScopeChannelWrite = string("channel:write") // 写入 IP
|
||||||
|
|
||||||
|
ScopeProxy = string("proxy") // 代理
|
||||||
|
ScopeProxyRead = string("proxy:read") // 读取代理列表
|
||||||
|
ScopeProxyWrite = string("proxy:write") // 写入代理
|
||||||
|
ScopeProxyWriteStatus = string("proxy:write:status") // 更改代理状态
|
||||||
|
|
||||||
ScopeTrade = string("trade") // 交易
|
ScopeTrade = string("trade") // 交易
|
||||||
ScopeTradeRead = string("trade:read") // 读取交易列表
|
ScopeTradeRead = string("trade:read") // 读取交易列表
|
||||||
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表
|
ScopeTradeReadOfUser = string("trade:read:of_user") // 读取指定用户的交易列表
|
||||||
|
|||||||
@@ -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.Page(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 {
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ func PageBalanceActivityByAdmin(c *fiber.Ctx) error {
|
|||||||
if req.UserPhone != nil {
|
if req.UserPhone != nil {
|
||||||
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
|
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 {
|
if req.CreatedAtStart != nil {
|
||||||
t := u.DateHead(*req.CreatedAtStart)
|
t := u.DateHead(*req.CreatedAtStart)
|
||||||
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
|
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
|
||||||
@@ -68,6 +71,7 @@ func PageBalanceActivityByAdmin(c *fiber.Ctx) error {
|
|||||||
type PageBalanceActivityByAdminReq struct {
|
type PageBalanceActivityByAdminReq struct {
|
||||||
core.PageReq
|
core.PageReq
|
||||||
UserPhone *string `json:"user_phone,omitempty"`
|
UserPhone *string `json:"user_phone,omitempty"`
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -88,6 +92,9 @@ func PageBalanceActivityOfUserByAdmin(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// 构造查询条件
|
// 构造查询条件
|
||||||
do := q.BalanceActivity.Where(q.BalanceActivity.UserID.Eq(req.UserID))
|
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 {
|
if req.CreatedAtStart != nil {
|
||||||
t := u.DateHead(*req.CreatedAtStart)
|
t := u.DateHead(*req.CreatedAtStart)
|
||||||
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
|
do = do.Where(q.BalanceActivity.CreatedAt.Gte(t))
|
||||||
@@ -124,6 +131,7 @@ func PageBalanceActivityOfUserByAdmin(c *fiber.Ctx) error {
|
|||||||
type PageBalanceActivityOfUserByAdminReq struct {
|
type PageBalanceActivityOfUserByAdminReq struct {
|
||||||
core.PageReq
|
core.PageReq
|
||||||
UserID int32 `json:"user_id" validate:"required"`
|
UserID int32 `json:"user_id" validate:"required"`
|
||||||
|
BillNo *string `json:"bill_no,omitempty"`
|
||||||
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
|
||||||
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ func CreateChannel(c *fiber.Ctx) error {
|
|||||||
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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,61 +1,123 @@
|
|||||||
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 报告上线
|
||||||
|
|||||||
@@ -797,10 +797,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)
|
||||||
@@ -809,7 +806,7 @@ func ResourcePrice(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取套餐价格
|
// 获取套餐价格
|
||||||
detail, err := req.TradeDetail(nil)
|
detail, err := req.TradeDetail(ac.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -817,11 +814,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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,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)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -305,14 +306,26 @@ func UpdateUser(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
|
do := make([]field.AssignExpr, 0)
|
||||||
|
if req.Username != nil && *req.Username != "" {
|
||||||
|
do = append(do, q.User.Username.Value(*req.Username))
|
||||||
|
}
|
||||||
|
if req.Email != nil {
|
||||||
|
if *req.Email == "" {
|
||||||
|
do = append(do, q.User.Email.Null())
|
||||||
|
} else {
|
||||||
|
do = append(do, q.User.Email.Value(*req.Email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.ContactQQ != nil {
|
||||||
|
do = append(do, q.User.ContactQQ.Value(*req.ContactQQ))
|
||||||
|
}
|
||||||
|
if req.ContactWechat != nil {
|
||||||
|
do = append(do, q.User.ContactWechat.Value(*req.ContactWechat))
|
||||||
|
}
|
||||||
_, err = q.User.
|
_, err = q.User.
|
||||||
Where(q.User.ID.Eq(authCtx.User.ID)).
|
Where(q.User.ID.Eq(authCtx.User.ID)).
|
||||||
Updates(m.User{
|
UpdateSimple(do...)
|
||||||
Username: &req.Username,
|
|
||||||
Email: &req.Email,
|
|
||||||
ContactQQ: &req.ContactQQ,
|
|
||||||
ContactWechat: &req.ContactWechat,
|
|
||||||
})
|
|
||||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
return core.NewBizErr("用户名或邮箱已被占用")
|
return core.NewBizErr("用户名或邮箱已被占用")
|
||||||
}
|
}
|
||||||
@@ -325,10 +338,10 @@ func UpdateUser(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新账号信息
|
// 更新账号信息
|
||||||
@@ -379,16 +392,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
|
||||||
}
|
}
|
||||||
@@ -411,7 +422,6 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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 产品状态枚举
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ type ProductSku struct {
|
|||||||
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"` // 最低价格
|
PriceMin decimal.Decimal `json:"price_min" gorm:"column:price_min"` // 最低价格
|
||||||
Status SkuStatus `json:"status" gorm:"column:status"` // SKU 状态:0-禁用,1-正常
|
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"`
|
||||||
|
|||||||
@@ -145,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
|
||||||
@@ -173,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
|
||||||
@@ -184,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
|
||||||
@@ -192,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
|
||||||
@@ -482,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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -540,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -38,10 +38,33 @@ func newProductSku(db *gorm.DB, opts ...gen.DOOption) productSku {
|
|||||||
_productSku.Price = field.NewField(tableName, "price")
|
_productSku.Price = field.NewField(tableName, "price")
|
||||||
_productSku.PriceMin = field.NewField(tableName, "price_min")
|
_productSku.PriceMin = field.NewField(tableName, "price_min")
|
||||||
_productSku.Status = field.NewInt32(tableName, "status")
|
_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{
|
||||||
@@ -70,6 +93,8 @@ type productSku struct {
|
|||||||
Price field.Field
|
Price field.Field
|
||||||
PriceMin field.Field
|
PriceMin field.Field
|
||||||
Status field.Int32
|
Status field.Int32
|
||||||
|
Sort field.Int32
|
||||||
|
CountMin field.Int32
|
||||||
Product productSkuBelongsToProduct
|
Product productSkuBelongsToProduct
|
||||||
|
|
||||||
Discount productSkuBelongsToDiscount
|
Discount productSkuBelongsToDiscount
|
||||||
@@ -100,6 +125,8 @@ func (p *productSku) updateTableName(table string) *productSku {
|
|||||||
p.Price = field.NewField(table, "price")
|
p.Price = field.NewField(table, "price")
|
||||||
p.PriceMin = field.NewField(table, "price_min")
|
p.PriceMin = field.NewField(table, "price_min")
|
||||||
p.Status = field.NewInt32(table, "status")
|
p.Status = field.NewInt32(table, "status")
|
||||||
|
p.Sort = field.NewInt32(table, "sort")
|
||||||
|
p.CountMin = field.NewInt32(table, "count_min")
|
||||||
|
|
||||||
p.fillFieldMap()
|
p.fillFieldMap()
|
||||||
|
|
||||||
@@ -116,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, 13)
|
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
|
||||||
@@ -128,6 +155,8 @@ func (p *productSku) fillFieldMap() {
|
|||||||
p.fieldMap["price"] = p.Price
|
p.fieldMap["price"] = p.Price
|
||||||
p.fieldMap["price_min"] = p.PriceMin
|
p.fieldMap["price_min"] = p.PriceMin
|
||||||
p.fieldMap["status"] = p.Status
|
p.fieldMap["status"] = p.Status
|
||||||
|
p.fieldMap["sort"] = p.Sort
|
||||||
|
p.fieldMap["count_min"] = p.CountMin
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,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
|
||||||
|
|||||||
@@ -44,6 +44,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 +55,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
|
||||||
@@ -266,6 +277,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
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ 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())).Find()
|
||||||
@@ -109,6 +108,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 +123,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 +133,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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管理员接口路由
|
// 管理员接口路由
|
||||||
@@ -188,6 +192,15 @@ func adminRouter(api fiber.Router) {
|
|||||||
channel.Post("/page", handlers.PageChannelByAdmin)
|
channel.Post("/page", handlers.PageChannelByAdmin)
|
||||||
channel.Post("/page/of-user", handlers.PageChannelOfUserByAdmin)
|
channel.Post("/page/of-user", handlers.PageChannelOfUserByAdmin)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *T
|
|||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, trade
|
|||||||
CouponUserID: detail.CouponUserId,
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ 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, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error)
|
||||||
RemoveChannels(batch string) error
|
RemoveChannels(batch string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ 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, resourceId int32, 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, resourceId, authWhitelist, authPassword, count, edgeFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *channelServer) RemoveChannels(batch string) error {
|
func (s *channelServer) RemoveChannels(batch string) error {
|
||||||
@@ -94,7 +94,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
|
||||||
@@ -232,7 +232,7 @@ 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 + ":" + strconv.Itoa(int(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)
|
||||||
}
|
}
|
||||||
@@ -268,11 +268,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
|
||||||
|
|
||||||
@@ -301,16 +302,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
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,9 @@ 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, resourceId int32, 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()
|
||||||
@@ -64,10 +63,27 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
}
|
}
|
||||||
proxy := proxyResult.Proxy
|
proxy := proxyResult.Proxy
|
||||||
|
|
||||||
// 获取可用通道
|
// 锁内确认状态并锁定端口,避免与状态切换并发穿透
|
||||||
chans, err := lockChans(proxy.ID, batch, count)
|
var chans []netip.AddrPort
|
||||||
|
err = g.Redsync.WithLock(proxyStatusLockKey(proxy.ID), func() error {
|
||||||
|
lockedProxy, err := q.Proxy.Where(q.Proxy.ID.Eq(proxy.ID)).Take()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if lockedProxy.Status != m.ProxyStatusOnline {
|
||||||
|
return core.NewBizErr("无可用主机,请稍后再试")
|
||||||
|
}
|
||||||
|
|
||||||
|
chans, err = lockChans(proxy.ID, batch, count)
|
||||||
|
if err != nil {
|
||||||
|
return core.NewBizErr("无可用通道,请稍后再试", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = *lockedProxy
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, core.NewBizErr("无可用通道,请稍后再试", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取可用节点
|
// 获取可用节点
|
||||||
@@ -86,7 +102,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
return nil, core.NewBizErr("获取可用节点失败", err)
|
return nil, core.NewBizErr("获取可用节点失败", err)
|
||||||
}
|
}
|
||||||
if edgesResp.Total != count && len(edgesResp.Edges) != count {
|
if edgesResp.Total != count && len(edgesResp.Edges) != count {
|
||||||
return nil, core.NewBizErr("地区可用节点数量不足 [%s, %s] [%s]")
|
return nil, core.NewBizErr("地区可用节点数量不足")
|
||||||
}
|
}
|
||||||
edges := edgesResp.Edges
|
edges := edgesResp.Edges
|
||||||
|
|
||||||
@@ -98,10 +114,6 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
|
|||||||
ch := chans[i]
|
ch := chans[i]
|
||||||
edge := edges[i]
|
edge := edges[i]
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, core.NewBizErr("解析通道地址失败", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通道数据
|
// 通道数据
|
||||||
channels[i] = &m.Channel{
|
channels[i] = &m.Channel{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
@@ -301,7 +313,7 @@ func (s *channelBaiyinProvider) RemoveChannels(batch string) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 清空通道配置
|
// 清空通道配置
|
||||||
secret := strings.Split(*proxy.Secret, ":")
|
secret := strings.Split(u.Z(proxy.Secret), ":")
|
||||||
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
|
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
|
||||||
err := gateway.GatewayPortConfigs(configs)
|
err := gateway.GatewayPortConfigs(configs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -321,6 +333,6 @@ func (s *channelBaiyinProvider) RemoveChannels(batch string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Debug("清除代理端口配置", "time", time.Since(start).String())
|
slog.Debug("清除代理端口配置", "duration", time.Since(start).String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,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{
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []
|
|||||||
return q.ProductSku.
|
return q.ProductSku.
|
||||||
Joins(q.ProductSku.Discount, q.ProductSku.Product).
|
Joins(q.ProductSku.Discount, q.ProductSku.Product).
|
||||||
Where(do...).
|
Where(do...).
|
||||||
Order(q.ProductSku.ID).
|
Order(q.ProductSku.Sort).
|
||||||
FindByPage(req.GetOffset(), req.GetLimit())
|
FindByPage(req.GetOffset(), req.GetLimit())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +47,11 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
|
|||||||
return core.NewBizErr("产品最低价格的格式不正确", err)
|
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,
|
||||||
@@ -54,6 +59,8 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
|
|||||||
Name: create.Name,
|
Name: create.Name,
|
||||||
Price: price,
|
Price: price,
|
||||||
PriceMin: priceMin,
|
PriceMin: priceMin,
|
||||||
|
Sort: create.Sort,
|
||||||
|
CountMin: countMin,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +71,8 @@ type CreateProductSkuData struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Price string `json:"price"`
|
Price string `json:"price"`
|
||||||
PriceMin string `json:"price_min"`
|
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) {
|
||||||
@@ -95,6 +104,12 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
|
|||||||
if update.Status != nil {
|
if update.Status != nil {
|
||||||
do = append(do, q.ProductSku.Status.Value(*update.Status))
|
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...)
|
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
|
||||||
return err
|
return err
|
||||||
@@ -108,6 +123,8 @@ type UpdateProductSkuData struct {
|
|||||||
Price *string `json:"price"`
|
Price *string `json:"price"`
|
||||||
PriceMin string `json:"price_min"`
|
PriceMin string `json:"price_min"`
|
||||||
Status *int32 `json:"status"`
|
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) {
|
||||||
|
|||||||
@@ -1,65 +1,216 @@
|
|||||||
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"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gen/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Proxy = &proxyService{}
|
var Proxy = &proxyService{}
|
||||||
|
|
||||||
type proxyService struct{}
|
type proxyService struct{}
|
||||||
|
|
||||||
// AllProxies 获取所有代理
|
func proxyStatusLockKey(id int32) string {
|
||||||
func (s *proxyService) AllProxies(proxyType m.ProxyType, channels bool) ([]*m.Proxy, error) {
|
return fmt.Sprintf("platform:proxy:status:%d", id)
|
||||||
proxies, err := q.Proxy.Where(
|
|
||||||
q.Proxy.Type.Eq(int(proxyType)),
|
|
||||||
q.Proxy.Status.Eq(int(m.ProxyStatusOnline)),
|
|
||||||
).Preload(
|
|
||||||
q.Proxy.Channels.On(q.Channel.ExpiredAt.Gte(time.Now())),
|
|
||||||
).Find()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return proxies, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterBaiyin 注册新代理服务
|
func hasUsedChans(proxyID int32) (bool, error) {
|
||||||
func (s *proxyService) RegisterBaiyin(Name string, IP netip.Addr, username, password string) error {
|
ctx := context.Background()
|
||||||
|
pattern := usedChansKey + ":" + strconv.Itoa(int(proxyID)) + ":*"
|
||||||
// 保存代理信息
|
keys, _, err := g.Redis.Scan(ctx, 0, pattern, 1).Result()
|
||||||
proxy := &m.Proxy{
|
if err != nil {
|
||||||
Version: 0,
|
return false, err
|
||||||
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 len(keys) > 0, nil
|
||||||
return core.NewServErr("保存通道数据失败")
|
}
|
||||||
|
|
||||||
|
func rebuildFreeChans(proxyID int32, addr netip.Addr) error {
|
||||||
|
if err := remChans(proxyID); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加可用通道到 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" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *proxyService) UpdateStatus(update *UpdateProxyStatus) error {
|
||||||
|
return g.Redsync.WithLock(proxyStatusLockKey(update.ID), func() 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -144,35 +145,36 @@ type UpdateResourceData struct {
|
|||||||
Active *bool `json:"active"`
|
Active *bool `json:"active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, cuid *int32) (*m.ProductSku, *m.ProductDiscount, *m.CouponUser, 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) {
|
||||||
|
|
||||||
sku, err := q.ProductSku.
|
sku, err := q.ProductSku.
|
||||||
Joins(q.ProductSku.Discount).
|
Joins(q.ProductSku.Discount).
|
||||||
Where(q.ProductSku.Code.Eq(skuCode), q.ProductSku.Status.Eq(int32(m.SkuStatusEnabled))).
|
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(fmt.Sprintf("产品不可用", skuCode), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原价
|
// 原价
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -186,20 +188,20 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
|
|||||||
var err error
|
var err error
|
||||||
coupon, err = Coupon.GetUserCoupon(user.ID, *cuid, discounted)
|
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.Coupon.Amount)
|
couponApplied = discounted.Sub(coupon.Coupon.Amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 约束到最低价格
|
// 约束到最低价格
|
||||||
if discounted.Cmp(sku.PriceMin) < 0 {
|
if discounted.Cmp(amountMin) < 0 {
|
||||||
discounted = sku.PriceMin.Copy()
|
discounted = amountMin.Copy()
|
||||||
}
|
}
|
||||||
if couponApplied.Cmp(sku.PriceMin) < 0 {
|
if couponApplied.Cmp(amountMin) < 0 {
|
||||||
couponApplied = sku.PriceMin.Copy()
|
couponApplied = amountMin.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return sku, discount, coupon, discounted, couponApplied, nil
|
return sku, discount, coupon, amount, discounted, couponApplied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateResourceData struct {
|
type CreateResourceData struct {
|
||||||
@@ -260,7 +262,7 @@ func (data *CreateResourceData) Code() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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.CouponId)
|
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
|
||||||
}
|
}
|
||||||
@@ -277,7 +279,7 @@ func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error)
|
|||||||
data,
|
data,
|
||||||
m.TradeTypePurchase,
|
m.TradeTypePurchase,
|
||||||
sku.Name,
|
sku.Name,
|
||||||
amount, actual,
|
amount, discounted, actual,
|
||||||
discountId, couponUserId,
|
discountId, couponUserId,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -619,6 +619,7 @@ type TradeDetail struct {
|
|||||||
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"`
|
||||||
|
Discounted decimal.Decimal `json:"discounted"`
|
||||||
Actual decimal.Decimal `json:"actual"`
|
Actual decimal.Decimal `json:"actual"`
|
||||||
DiscountId *int32 `json:"discount_id,omitempty"`
|
DiscountId *int32 `json:"discount_id,omitempty"`
|
||||||
CouponUserId *int32 `json:"coupon_id,omitempty"`
|
CouponUserId *int32 `json:"coupon_id,omitempty"`
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 服务异常
|
||||||
|
|||||||
Reference in New Issue
Block a user