11 Commits

59 changed files with 2999 additions and 499 deletions

57
.env.example Normal file
View File

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

View File

@@ -1,5 +1,7 @@
## TODO
交易信息持久化
用户请求需要检查数据权限
用反射实现环境变量解析,以简化函数签名

View File

@@ -66,6 +66,7 @@ func main() {
m.Inquiry{},
m.ProductDiscount{},
m.BalanceActivity{},
m.CouponUser{},
)
g.Execute()
}

2
pkg/env/env.go vendored
View File

@@ -24,7 +24,6 @@ var (
SessionAccessExpire = 60 * 60 * 2 // 访问令牌过期时间,单位秒。默认 2 小时
SessionRefreshExpire = 60 * 60 * 24 * 7 // 刷新令牌过期时间,单位秒。默认 7 天
DebugHttpDump = false // 是否打印请求和响应的原始数据
DebugExternalChange = true // 是否实际执行外部非幂等接口调用,在开发调试时可以关闭,避免对外部数据产生影响
DbHost = "localhost"
DbPort = "5432"
@@ -106,7 +105,6 @@ func Init() {
errs = append(errs, parse(&SessionAccessExpire, "SESSION_ACCESS_EXPIRE", true, nil))
errs = append(errs, parse(&SessionRefreshExpire, "SESSION_REFRESH_EXPIRE", true, nil))
errs = append(errs, parse(&DebugHttpDump, "DEBUG_HTTP_DUMP", true, nil))
errs = append(errs, parse(&DebugExternalChange, "DEBUG_EXTERNAL_CHANGE", true, nil))
errs = append(errs, parse(&DbHost, "DB_HOST", true, nil))
errs = append(errs, parse(&DbPort, "DB_PORT", true, nil))

View File

@@ -53,6 +53,18 @@ func X[T comparable](v T) *T {
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
}
// ====================
// 业务
// ====================
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:]
}

View File

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

View File

@@ -196,6 +196,7 @@ create table admin (
last_login timestamptz,
last_login_ip inet,
last_login_ua text,
lock bool not null default false,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -217,6 +218,7 @@ comment on column admin.status is '状态0-禁用1-正常';
comment on column admin.last_login is '最后登录时间';
comment on column admin.last_login_ip is '最后登录地址';
comment on column admin.last_login_ua is '最后登录代理';
comment on column admin.lock is '是否锁定编辑';
comment on column admin.created_at is '创建时间';
comment on column admin.updated_at is '更新时间';
comment on column admin.deleted_at is '删除时间';
@@ -757,6 +759,9 @@ create table product_sku (
code text not null unique,
name text not null,
price decimal not null,
price_min decimal not null,
status int not null default 1,
sort int not null default 0,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
@@ -772,7 +777,9 @@ comment on column product_sku.product_id is '产品ID';
comment on column product_sku.discount_id is '折扣ID';
comment on column product_sku.code is 'SKU 代码:格式为 key=value,key=value,...其中key:value 是 SKU 的属性,多个属性用逗号分隔';
comment on column product_sku.name is 'SKU 可读名称';
comment on column product_sku.price is '定价';
comment on column product_sku.price_min is '最低价格';
comment on column product_sku.status is 'SKU状态0-禁用1-正常';
comment on column product_sku.sort is '排序';
comment on column product_sku.created_at is '创建时间';
comment on column product_sku.updated_at is '更新时间';
comment on column product_sku.deleted_at is '删除时间';
@@ -984,7 +991,7 @@ create table bill (
trade_id int,
resource_id int,
refund_id int,
coupon_id int,
coupon_user_id int,
bill_no text not null,
info text,
type int not null,
@@ -999,7 +1006,7 @@ create index idx_bill_user_id on bill (user_id) where deleted_at is null;
create index idx_bill_trade_id on bill (trade_id) where deleted_at is null;
create index idx_bill_resource_id on bill (resource_id) where deleted_at is null;
create index idx_bill_refund_id on bill (refund_id) where deleted_at is null;
create index idx_bill_coupon_id on bill (coupon_id) where deleted_at is null;
create index idx_bill_coupon_id on bill (coupon_user_id) where deleted_at is null;
create index idx_bill_created_at on bill (created_at) where deleted_at is null;
-- bill表字段注释
@@ -1009,6 +1016,7 @@ comment on column bill.user_id is '用户ID';
comment on column bill.trade_id is '订单ID';
comment on column bill.resource_id is '套餐ID';
comment on column bill.refund_id is '退款ID';
comment on column bill.coupon_user_id is '优惠券发放ID';
comment on column bill.bill_no is '易读账单号';
comment on column bill.info is '产品可读信息';
comment on column bill.type is '账单类型1-消费2-退款3-充值';
@@ -1051,36 +1059,59 @@ comment on column balance_activity.created_at is '创建时间';
-- coupon 优惠券
drop table if exists coupon cascade;
create table coupon (
id int generated by default as identity primary key,
user_id int,
code text not null,
remark text,
amount decimal(12, 2) not null default 0,
min_amount decimal(12, 2) not null default 0,
status int not null default 0,
expire_at timestamptz,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
id int generated by default as identity primary key,
name text not null,
amount decimal(12, 2) not null default 0,
min_amount decimal(12, 2) not null default 0,
count int not null default 0,
status int not null default 1,
expire_type int not null default 0,
expire_at timestamptz,
expire_in int,
created_at timestamptz default current_timestamp,
updated_at timestamptz default current_timestamp,
deleted_at timestamptz
);
create index idx_coupon_user_id on coupon (user_id) where deleted_at is null;
create index idx_coupon_code on coupon (code) where deleted_at is null;
create index idx_coupon_created_at on coupon (created_at) where deleted_at is null;
-- coupon表字段注释
comment on table coupon is '优惠券表';
comment on column coupon.id is '优惠券ID';
comment on column coupon.user_id is '用户ID';
comment on column coupon.code is '优惠券代码';
comment on column coupon.remark is '优惠券备注';
comment on column coupon.name is '优惠券名称';
comment on column coupon.amount is '优惠券金额';
comment on column coupon.min_amount is '最低消费金额';
comment on column coupon.status is '优惠券状态0-未使用1-已使用2-已过期';
comment on column coupon.expire_at is '过期时间';
comment on column coupon.count is '优惠券数量';
comment on column coupon.status is '优惠券状态0-禁用1-正常';
comment on column coupon.expire_type is '过期类型0-不过期1-固定日期2-相对日期(从发放时间算起)';
comment on column coupon.expire_at is '过期时间,固定日期必填';
comment on column coupon.expire_in is '过期时长(天),相对日期必填';
comment on column coupon.created_at is '创建时间';
comment on column coupon.updated_at is '更新时间';
comment on column coupon.deleted_at is '删除时间';
-- coupon_user 优惠券发放
drop table if exists coupon_user cascade;
create table coupon_user (
id int generated by default as identity primary key,
coupon_id int not null,
user_id int not null,
status int not null default 0,
expire_at timestamptz,
used_at timestamptz,
created_at timestamptz default current_timestamp
);
create index idx_coupon_user_coupon_id on coupon_user (coupon_id);
create index idx_coupon_user_user_id on coupon_user (user_id);
-- coupon_user表字段注释
comment on table coupon_user is '优惠券发放表';
comment on column coupon_user.id is '记录ID';
comment on column coupon_user.coupon_id is '优惠券ID';
comment on column coupon_user.user_id is '用户ID';
comment on column coupon_user.status is '使用状态0-未使用1-已使用';
comment on column coupon_user.expire_at is '过期时间';
comment on column coupon_user.used_at is '使用时间';
comment on column coupon_user.created_at is '创建时间';
-- endregion
-- ====================
@@ -1185,11 +1216,13 @@ alter table bill
alter table bill
add constraint fk_bill_refund_id foreign key (refund_id) references refund (id) on delete set null;
alter table bill
add constraint fk_bill_coupon_id foreign key (coupon_id) references coupon (id) on delete set null;
add constraint fk_bill_coupon_id foreign key (coupon_user_id) references coupon_user (id) on delete set null;
-- coupon表外键
alter table coupon
add constraint fk_coupon_user_id foreign key (user_id) references "user" (id) on delete cascade;
-- coupon_user表外键
alter table coupon_user
add constraint fk_coupon_user_user_id foreign key (user_id) references "user" (id) on delete cascade;
alter table coupon_user
add constraint fk_coupon_user_coupon_id foreign key (coupon_id) references coupon (id) on delete cascade;
-- product_sku表外键
alter table product_sku

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

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

View File

@@ -4,7 +4,6 @@ import (
"context"
"errors"
"log/slog"
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
@@ -50,9 +49,8 @@ func authUser(loginType PwdLoginType, username, password string) (user *m.User,
}
if user == nil {
user = &m.User{
Phone: username,
Username: u.P(username),
Status: m.UserStatusEnabled,
Phone: username,
Status: m.UserStatusEnabled,
}
}
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) {
// 验证验证码
err := s.Verifier.VerifySms(context.Background(), username, code)
err := s.Verifier.VerifySms(context.Background(), username, code, s.VerifierSmsPurposeLogin)
if err != nil {
return nil, core.NewBizErr("短信认证失败", err)
}
@@ -169,6 +167,9 @@ func adminScopes(admin *m.Admin) ([]string, error) {
scopeNames := make([]string, 0, len(scopes))
for _, scope := range scopes {
if scope.Name == "" {
continue
}
scopeNames = append(scopeNames, scope.Name)
}
return scopeNames, nil

View File

@@ -356,6 +356,11 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
return nil, err
}
// 非锁定管理员,不允许为空权限
if !admin.Lock && (len(scopes) == 0) {
return nil, ErrAuthorizeInvalidScope // 没有配置权限
}
// 更新管理员登录时间
admin.LastLogin = u.P(time.Now())
admin.LastLoginIP = ip
@@ -532,10 +537,10 @@ func introspectUser(ctx *fiber.Ctx, authCtx *AuthCtx) error {
// 掩码敏感信息
if profile.Phone != "" {
profile.Phone = maskPhone(profile.Phone)
profile.Phone = u.MaskPhone(profile.Phone)
}
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 {
@@ -574,20 +579,6 @@ func introspectAdmin(ctx *fiber.Ctx, authCtx *AuthCtx) error {
}{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 {
UserID int32 `json:"user_id"`
ClientID int32 `json:"client_id"`

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ func PageAdminByAdmin(c *fiber.Ctx) error {
return err
}
list, total, err := s.Admin.PageAdmins(req.PageReq)
list, total, err := s.Admin.Page(req.PageReq)
if err != nil {
return err
}
@@ -62,7 +62,7 @@ func CreateAdmin(c *fiber.Ctx) error {
return err
}
if err := s.Admin.CreateAdmin(&req); err != nil {
if err := s.Admin.Create(&req); err != nil {
return err
}
@@ -80,7 +80,7 @@ func UpdateAdmin(c *fiber.Ctx) error {
return err
}
if err := s.Admin.UpdateAdmin(&req); err != nil {
if err := s.Admin.Update(&req); err != nil {
return err
}
@@ -98,7 +98,7 @@ func RemoveAdmin(c *fiber.Ctx) error {
return err
}
if err := s.Admin.RemoveAdmin(req.Id); err != nil {
if err := s.Admin.Remove(req.Id); err != nil {
return err
}

View File

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

View File

@@ -71,10 +71,10 @@ func PageBatchByAdmin(c *fiber.Ctx) error {
do := q.LogsUserUsage.Where()
if req.UserPhone != nil {
do = do.Where(q.User.Phone.Eq(*req.UserPhone))
do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
@@ -128,3 +128,75 @@ type PageBatchByAdminReq struct {
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// PageBatchOfUserByAdmin 分页查询指定用户的提取记录
func PageBatchOfUserByAdmin(ctx *fiber.Ctx) error {
_, err := auth.GetAuthCtx(ctx).PermitAdmin(core.ScopeBatchReadOfUser)
if err != nil {
return err
}
var req PageBatchOfUserByAdminReq
if err = g.Validator.ParseBody(ctx, &req); err != nil {
return err
}
do := q.LogsUserUsage.Where(q.LogsUserUsage.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.LogsUserUsage.BatchNo.Eq(*req.BatchNo))
}
if req.Prov != nil {
do = do.Where(q.LogsUserUsage.Prov.Eq(*req.Prov))
}
if req.City != nil {
do = do.Where(q.LogsUserUsage.City.Eq(*req.City))
}
if req.Isp != nil {
do = do.Where(q.LogsUserUsage.ISP.Eq(*req.Isp))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.LogsUserUsage.Time.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.LogsUserUsage.Time.Lte(t))
}
list, total, err := q.LogsUserUsage.
Joins(q.LogsUserUsage.User, q.LogsUserUsage.Resource).
Select(
q.LogsUserUsage.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.LogsUserUsage.Time.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return ctx.JSON(core.NewBizErr("获取数据失败", err))
}
return ctx.JSON(c.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBatchOfUserByAdminReq struct {
c.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
Prov *string `json:"prov"`
City *string `json:"city"`
Isp *string `json:"isp"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}

View File

@@ -101,6 +101,90 @@ type PageBillByAdminReq struct {
SkuCode *string `json:"sku_code,omitempty"`
}
// PageBillOfUserByAdmin 分页查询指定用户账单
func PageBillOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeBillReadOfUser)
if err != nil {
return err
}
// 解析请求参数
req := new(PageBillOfUserByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构造查询条件
do := q.Bill.Where(q.Bill.UserID.Eq(req.UserID))
if req.TradeInnerNo != nil {
do = do.Where(q.Trade.As("Trade").InnerNo.Eq(*req.TradeInnerNo))
}
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BillNo != nil {
do = do.Where(q.Bill.BillNo.Eq(*req.BillNo))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Bill.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateHead(*req.CreatedAtEnd)
do = do.Where(q.Bill.CreatedAt.Lte(time))
}
if req.ProductCode != nil {
do = do.Where(q.Resource.As("Resource").Code.Eq(*req.ProductCode))
}
if req.SkuCode != nil {
do = do.Where(q.Bill.
Where(q.ResourceShort.As("Resource__Short").Code.Eq(*req.SkuCode)).
Or(q.ResourceLong.As("Resource__Long").Code.Eq(*req.SkuCode)))
}
// 查询账单列表
list, total, err := q.Bill.
Joins(
q.Bill.Resource,
q.Bill.Trade,
q.Bill.Resource.Short,
q.Bill.Resource.Long,
).
Select(
q.Bill.ALL,
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
).
Where(do).
Order(q.Bill.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageBillOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
TradeInnerNo *string `json:"trade_inner_no,omitempty"`
ResourceNo *string `json:"resource_no,omitempty"`
BillNo *string `json:"bill_no,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
ProductCode *string `json:"product_code,omitempty"`
SkuCode *string `json:"sku_code,omitempty"`
}
// ListBill 获取账单列表
func ListBill(c *fiber.Ctx) error {
// 检查权限

View File

@@ -271,3 +271,76 @@ func RemoveChannels(c *fiber.Ctx) error {
type RemoveChannelsReq struct {
Batch string `json:"batch" validate:"required"`
}
// PageChannelOfUserByAdmin 分页查询指定用户的通道
func PageChannelOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeChannelReadOfUser)
if err != nil {
return err
}
// 解析请求参数
var req PageChannelOfUserByAdminReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 构建查询条件
do := q.Channel.Where(q.Channel.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.As("Resource").ResourceNo.Eq(*req.ResourceNo))
}
if req.BatchNo != nil {
do = do.Where(q.Channel.BatchNo.Eq(*req.BatchNo))
}
if req.ProxyHost != nil {
do = do.Where(q.Channel.Host.Eq(*req.ProxyHost))
}
if req.ProxyPort != nil {
do = do.Where(q.Channel.Port.Eq(*req.ProxyPort))
}
if req.ExpiredAtStart != nil {
t := u.DateHead(*req.ExpiredAtStart)
do = do.Where(q.Channel.ExpiredAt.Gte(t))
}
if req.ExpiredAtEnd != nil {
t := u.DateHead(*req.ExpiredAtEnd)
do = do.Where(q.Channel.ExpiredAt.Lte(t))
}
// 查询通道列表
list, total, err := q.Channel.
Joins(q.Channel.User, q.Channel.Resource).
Select(
q.Channel.ALL,
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Channel.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageChannelOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
BatchNo *string `json:"batch_no"`
ProxyHost *string `json:"proxy_host"`
ProxyPort *uint16 `json:"proxy_port"`
ExpiredAtStart *time.Time `json:"expired_at_start"`
ExpiredAtEnd *time.Time `json:"expired_at_end"`
}

View File

@@ -34,6 +34,15 @@ func AllProductByAdmin(c *fiber.Ctx) error {
type AllProductsByAdminReq struct {
}
func AllProduct(c *fiber.Ctx) error {
infos, err := s.Product.AllProductSaleInfos()
if err != nil {
return err
}
return c.JSON(infos)
}
func CreateProduct(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite)
if err != nil {
@@ -181,6 +190,32 @@ func UpdateProductSku(c *fiber.Ctx) error {
return nil
}
func UpdateProductStatusSku(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {
return err
}
type Params struct {
ID int32 `json:"id"`
Status int32 `json:"status"`
}
var req Params
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
err = s.ProductSku.Update(s.UpdateProductSkuData{
ID: req.ID,
Status: &req.Status,
})
if err != nil {
return err
}
return nil
}
func BatchUpdateProductSkuDiscount(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite)
if err != nil {

View File

@@ -209,7 +209,7 @@ type PageResourceLongReq struct {
// PageResourceShortByAdmin 分页查询全部短效套餐
func PageResourceShortByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceRead)
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortRead)
if err != nil {
return err
}
@@ -303,7 +303,7 @@ type PageResourceShortByAdminReq struct {
// PageResourceLongByAdmin 分页查询全部长效套餐
func PageResourceLongByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceRead)
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongRead)
if err != nil {
return err
}
@@ -393,6 +393,148 @@ type PageResourceLongByAdminReq struct {
Expired *bool `json:"expired" form:"expired"`
}
// PageResourceShortOfUserByAdmin 分页查询指定用户的短效套餐
func PageResourceShortOfUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortReadOfUser)
if err != nil {
return err
}
var req PageResourceShortOfUserByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode)))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Resource.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Resource.CreatedAt.Lte(t))
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceShort.As("Short").Type.As("Short__type"),
q.ResourceShort.As("Short").Live.As("Short__live"),
q.ResourceShort.As("Short").Quota.As("Short__quota"),
q.ResourceShort.As("Short").Used.As("Short__used"),
q.ResourceShort.As("Short").Daily.As("Short__daily"),
q.ResourceShort.As("Short").LastAt.As("Short__last_at"),
q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"),
q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceShortOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Mode *int `json:"mode"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// PageResourceLongOfUserByAdmin 分页查询指定用户的长效套餐
func PageResourceLongOfUserByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceLongReadOfUser)
if err != nil {
return err
}
var req PageResourceLongOfUserByAdminReq
if err = g.Validator.ParseBody(c, &req); err != nil {
return err
}
do := q.Resource.Where(q.Resource.UserID.Eq(req.UserID))
if req.ResourceNo != nil {
do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo))
}
if req.Active != nil {
do = do.Where(q.Resource.Active.Is(*req.Active))
}
if req.Mode != nil {
do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode))
}
if req.CreatedAtStart != nil {
t := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Resource.CreatedAt.Gte(t))
}
if req.CreatedAtEnd != nil {
t := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Resource.CreatedAt.Lte(t))
}
list, total, err := q.Resource.
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
Select(
q.Resource.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
q.ResourceLong.As("Long").Type.As("Long__type"),
q.ResourceLong.As("Long").Live.As("Long__live"),
q.ResourceLong.As("Long").Quota.As("Long__quota"),
q.ResourceLong.As("Long").Used.As("Long__used"),
q.ResourceLong.As("Long").Daily.As("Long__daily"),
q.ResourceLong.As("Long").LastAt.As("Long__last_at"),
q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"),
q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"),
).
Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do).
Order(q.Resource.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageResourceLongOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
ResourceNo *string `json:"resource_no"`
Active *bool `json:"active"`
Mode *int `json:"mode"`
CreatedAtStart *time.Time `json:"created_at_start"`
CreatedAtEnd *time.Time `json:"created_at_end"`
}
// AllActiveResource 所有可用套餐
func AllActiveResource(c *fiber.Ctx) error {
// 检查权限
@@ -449,9 +591,6 @@ func AllActiveResource(c *fiber.Ctx) error {
return c.JSON(resources)
}
type AllResourceReq struct {
}
func UpdateResourceByAdmin(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceWrite)
if err != nil {
@@ -670,15 +809,6 @@ func ResourcePrice(c *fiber.Ctx) error {
}
// 获取套餐价格
// sku, err := s.Resource.GetSku(req.CreateResourceData.Code())
// if err != nil {
// return err
// }
// _, amount, discounted, couponApplied, err := s.Resource.GetPrice(sku, req.Count(), nil, nil)
// if err != nil {
// return err
// }
detail, err := req.TradeDetail(nil)
if err != nil {
return err
@@ -687,11 +817,13 @@ func ResourcePrice(c *fiber.Ctx) error {
// 计算折扣
return c.JSON(ResourcePriceResp{
Price: detail.Amount.StringFixed(2),
Discounted: detail.Actual.StringFixed(2),
Discounted: detail.Discounted.StringFixed(2),
Actual: detail.Actual.StringFixed(2),
})
}
type ResourcePriceResp struct {
Price string `json:"price"`
Discounted string `json:"discounted_price"`
Discounted string `json:"discounted"`
Actual string `json:"actual"`
}

View File

@@ -97,6 +97,84 @@ type PageTradeByAdminReq struct {
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
}
// PageTradeOfUserByAdmin 分页查询指定用户的订单
func PageTradeOfUserByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeReadOfUser)
if err != nil {
return err
}
// 解析请求参数
req := new(PageTradeOfUserByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询语句
do := q.Trade.Where(q.Trade.UserID.Eq(req.UserID))
if req.InnerNo != nil {
do = do.Where(q.Trade.InnerNo.Eq(*req.InnerNo))
}
if req.OuterNo != nil {
do = do.Where(q.Trade.OuterNo.Eq(*req.OuterNo))
}
if req.Method != nil {
do = do.Where(q.Trade.Method.Eq(*req.Method))
}
if req.Platform != nil {
do = do.Where(q.Trade.Platform.Eq(*req.Platform))
}
if req.Status != nil {
do = do.Where(q.Trade.Status.Eq(*req.Status))
}
if req.CreatedAtStart != nil {
time := u.DateHead(*req.CreatedAtStart)
do = do.Where(q.Trade.CreatedAt.Gte(time))
}
if req.CreatedAtEnd != nil {
time := u.DateTail(*req.CreatedAtEnd)
do = do.Where(q.Trade.CreatedAt.Lte(time))
}
// 查询订单列表
list, total, err := q.Trade.
Joins(q.Trade.User).
Select(
q.Trade.ALL,
q.User.As("User").Phone.As("User__phone"),
q.User.As("User").Name.As("User__name"),
).
Where(do).
Order(q.Trade.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
List: list,
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
})
}
type PageTradeOfUserByAdminReq struct {
core.PageReq
UserID int32 `json:"user_id" validate:"required"`
InnerNo *string `json:"inner_no,omitempty"`
OuterNo *string `json:"outer_no,omitempty"`
Method *int `json:"method,omitempty"`
Platform *int `json:"platform,omitempty"`
Status *int `json:"status,omitempty"`
CreatedAtStart *time.Time `json:"created_at_start,omitempty"`
CreatedAtEnd *time.Time `json:"created_at_end,omitempty"`
}
// ============================================================
// 创建订单
func TradeCreate(c *fiber.Ctx) error {
// 检查权限
@@ -143,6 +221,8 @@ type TradeCreateReq struct {
Recharge *s.UpdateBalanceData `json:"recharge,omitempty"`
}
// ============================================================
// 完成订单
func TradeComplete(c *fiber.Ctx) error {
// 检查权限
@@ -152,13 +232,13 @@ func TradeComplete(c *fiber.Ctx) error {
}
// 解析请求参数
req := new(TradeCompleteReq)
if err := g.Validator.ParseBody(c, req); err != nil {
var req s.TradeRef
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 检查订单状态
err = s.Trade.CompleteTrade(authCtx.User, &req.TradeRef)
err = s.Trade.CompleteTrade(authCtx.User, &req)
if err != nil {
return err
}
@@ -166,10 +246,40 @@ func TradeComplete(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
type TradeCompleteReq struct {
s.TradeRef
// 管理员完成订单
func TradeCompleteByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeTradeWriteComplete)
if err != nil {
return err
}
// 解析请求参数
var req struct {
s.TradeRef
UserID int32 `json:"user_id" validate:"required"`
}
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
// 获取用户信息
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
// 完成订单
err = s.Trade.CompleteTrade(user, &req.TradeRef)
if err != nil {
return err
}
return c.SendStatus(fiber.StatusNoContent)
}
// ============================================================
// 取消订单
func TradeCancel(c *fiber.Ctx) error {
// 检查权限
@@ -198,6 +308,8 @@ type TradeCancelReq struct {
s.TradeRef
}
// ============================================================
// 检查订单
func TradeCheck(c *fiber.Ctx) error {
// 检查权限sse 接口暂时不检查权限

View File

@@ -1,6 +1,7 @@
package handlers
import (
"errors"
"platform/web/auth"
"platform/web/core"
g "platform/web/globals"
@@ -11,6 +12,7 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal"
"golang.org/x/crypto/bcrypt"
"gorm.io/gen/field"
"gorm.io/gorm"
)
@@ -67,7 +69,7 @@ func PageUserByAdmin(c *fiber.Ctx) error {
// 查询用户列表
users, total, err := q.User.
Preload(q.User.Admin, q.User.Discount).
Omit(q.User.Password).
Omit(q.User.Password, q.Admin.Password).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
@@ -76,6 +78,12 @@ func PageUserByAdmin(c *fiber.Ctx) error {
}
for _, user := range users {
if user.IDNo != nil && len(*user.IDNo) == 18 {
var str = *user.IDNo
*user.IDNo = str[:6] + "****" + str[len(str)-2:]
}
if user.Admin != nil {
user.Admin = &m.Admin{
Name: user.Admin.Name,
@@ -133,7 +141,7 @@ func GetUserByAdmin(c *fiber.Ctx) error {
// 查询用户
user, err := q.User.
Preload(q.User.Admin, q.User.Discount).
Omit(q.User.Password).
Omit(q.User.Password, q.Admin.Password).
Where(do).
Order(q.User.CreatedAt.Desc()).
First()
@@ -298,14 +306,29 @@ func UpdateUser(c *fiber.Ctx) error {
}
// 更新用户信息
do := make([]field.AssignExpr, 0)
if req.Username != nil && *req.Username != "" {
do = append(do, q.User.Username.Value(*req.Username))
}
if req.Email != nil {
if *req.Email == "" {
do = append(do, q.User.Email.Null())
} else {
do = append(do, q.User.Email.Value(*req.Email))
}
}
if req.ContactQQ != nil {
do = append(do, q.User.ContactQQ.Value(*req.ContactQQ))
}
if req.ContactWechat != nil {
do = append(do, q.User.ContactWechat.Value(*req.ContactWechat))
}
_, err = q.User.
Where(q.User.ID.Eq(authCtx.User.ID)).
Updates(m.User{
Username: &req.Username,
Email: &req.Email,
ContactQQ: &req.ContactQQ,
ContactWechat: &req.ContactWechat,
})
UpdateSimple(do...)
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("用户名或邮箱已被占用")
}
if err != nil {
return err
}
@@ -315,10 +338,10 @@ func UpdateUser(c *fiber.Ctx) error {
}
type UpdateUserReq struct {
Username string `json:"username" validate:"omitempty,min=3,max=20"`
Email string `json:"email" validate:"omitempty,email"`
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
Username *string `json:"username" validate:"omitempty,min=3,max=20"`
Email *string `json:"email" validate:"omitempty,email"`
ContactQQ *string `json:"contact_qq" validate:"omitempty,qq"`
ContactWechat *string `json:"contact_wechat" validate:"omitempty,wechat"`
}
// 更新账号信息
@@ -369,16 +392,14 @@ func UpdatePassword(c *fiber.Ctx) error {
return err
}
// 验证手机号
if req.Phone != authCtx.User.Phone {
return fiber.NewError(fiber.StatusBadRequest, "手机号码不正确")
}
// 验证手机令牌
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 {
return err
}
@@ -401,7 +422,121 @@ func UpdatePassword(c *fiber.Ctx) error {
}
type UpdatePasswordReq struct {
Phone string `json:"phone"`
Code string `json:"code"`
Password string `json:"password"`
}
// PageUserNotBindByAdmin 分页获取未绑定管理员的用户
func PageUserNotBindByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadNotBind)
if err != nil {
return err
}
// 解析请求参数
req := new(PageUserNotBindByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询条件(强制过滤未绑定管理员的用户)
do := q.User.Where(q.User.AdminID.IsNull())
if req.Phone != nil {
do = do.Where(q.User.Phone.Eq(*req.Phone))
}
// 查询用户列表
users, total, err := q.User.
Omit(q.User.Password, q.User.IDNo).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: users,
})
}
type PageUserNotBindByAdminReq struct {
core.PageReq
Phone *string `json:"phone,omitempty"`
}
// UpdateUserBalanceIncByAdmin 管理员增加用户余额
func UpdateUserBalanceIncByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Add(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
// UpdateUserBalanceDecByAdmin 管理员减少用户余额
func UpdateUserBalanceDecByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Sub(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
type UpdateUserBalanceChangeByAdminData struct {
UserID int32 `json:"user_id" validate:"required"`
Amount string `json:"amount" validate:"required"`
}

View File

@@ -5,6 +5,7 @@ import (
"platform/pkg/env"
"platform/web/auth"
"platform/web/services"
s "platform/web/services"
"regexp"
"strconv"
@@ -13,12 +14,11 @@ import (
)
type VerifierReq struct {
Purpose services.VerifierSmsPurpose `json:"purpose"`
Phone string `json:"phone"`
Purpose s.VerifierSmsPurpose `json:"purpose"`
Phone string `json:"phone"`
}
func SmsCode(c *fiber.Ctx) error {
func SendSmsCode(c *fiber.Ctx) error {
_, err := auth.GetAuthCtx(c).PermitOfficialClient()
if err != nil {
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 {
var sErr services.VerifierServiceSendLimitErr
var sErr s.VerifierServiceSendLimitErr
if errors.As(err, &sErr) {
return fiber.NewError(fiber.StatusTooManyRequests, strconv.Itoa(int(sErr)))
}
@@ -51,6 +51,23 @@ func SmsCode(c *fiber.Ctx) error {
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 {
if env.RunMode != env.RunModeDev {
return fiber.NewError(fiber.StatusForbidden, "not allowed")

View File

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

View File

@@ -11,7 +11,7 @@ import (
type Admin struct {
core.Model
Username string `json:"username" gorm:"column:username"` // 用户名
Password string `json:"password" gorm:"column:password"` // 密码
Password string `json:"-" gorm:"column:password"` // 密码
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL
Phone *string `json:"phone,omitempty" gorm:"column:phone"` // 手机号码
@@ -20,6 +20,7 @@ type Admin struct {
LastLogin *time.Time `json:"last_login,omitempty" gorm:"column:last_login"` // 最后登录时间
LastLoginIP *orm.Inet `json:"last_login_ip,omitempty" gorm:"column:last_login_ip"` // 最后登录地址
LastLoginUA *string `json:"last_login_ua,omitempty" gorm:"column:last_login_ua"` // 最后登录代理
Lock bool `json:"lock" gorm:"column:lock"` // 是否锁定编辑
Roles []*AdminRole `json:"roles" gorm:"many2many:link_admin_role"`
}

View File

@@ -16,7 +16,7 @@ type BalanceActivity struct {
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
Admin *User `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
Admin *Admin `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
}

View File

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

View File

@@ -8,7 +8,7 @@ import (
type Client struct {
core.Model
ClientID string `json:"client_id" gorm:"column:client_id"` // OAuth2客户端标识符
ClientSecret string `json:"client_secret" gorm:"column:client_secret"` // OAuth2客户端密钥
ClientSecret string `json:"-" gorm:"column:client_secret"` // OAuth2客户端密钥
RedirectURI *string `json:"redirect_uri,omitempty" gorm:"column:redirect_uri"` // OAuth2 重定向URI
Spec ClientSpec `json:"spec" gorm:"column:spec"` // 安全规范1-native2-browser3-web4-api
Name string `json:"name" gorm:"column:name"` // 名称

View File

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

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

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

View File

@@ -12,6 +12,8 @@ type Product struct {
Description *string `json:"description,omitempty" gorm:"column:description"` // 产品描述
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Status ProductStatus `json:"status" gorm:"column:status"` // 产品状态0-禁用1-正常
Skus []*ProductSku `json:"skus,omitempty" gorm:"foreignKey:ProductID"` // 产品包含的SKU列表
}
// ProductStatus 产品状态枚举

View File

@@ -14,7 +14,18 @@ type ProductSku struct {
Code string `json:"code" gorm:"column:code"` // SSKU 代码:格式为 key=value,key=value,...其中key:value 是 SKU 的属性,多个属性用逗号分隔
Name string `json:"name" gorm:"column:name"` // SKU 可读名称
Price decimal.Decimal `json:"price" gorm:"column:price"` // 定价
PriceMin decimal.Decimal `json:"price_min" gorm:"column:price_min"` // 最低价格
Status SkuStatus `json:"status" gorm:"column:status"` // SKU 状态0-禁用1-正常
Sort int32 `json:"sort" gorm:"column:sort"` // 排序
Product *Product `json:"product,omitempty" gorm:"foreignKey:ProductID"`
Discount *ProductDiscount `json:"discount,omitempty" gorm:"foreignKey:DiscountId"`
}
// SkuStatus SKU 状态
type SkuStatus int32
const (
SkuStatusDisabled SkuStatus = 0 // 禁用
SkuStatusEnabled SkuStatus = 1 // 正常
)

View File

@@ -16,7 +16,7 @@ type User struct {
Phone string `json:"phone" gorm:"column:phone"` // 手机号码
Username *string `json:"username,omitempty" gorm:"column:username"` // 用户名
Email *string `json:"email,omitempty" gorm:"column:email"` // 邮箱
Password *string `json:"password,omitempty" gorm:"column:password"` // 用户密码
Password *string `json:"-" gorm:"column:password"` // 用户密码
Source *UserSource `json:"source,omitempty" gorm:"column:source"` // 用户来源0-官网注册1-管理员添加2-代理商注册3-代理商添加
Name *string `json:"name,omitempty" gorm:"column:name"` // 真实姓名
Avatar *string `json:"avatar,omitempty" gorm:"column:avatar"` // 头像URL

View File

@@ -41,6 +41,7 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
_admin.LastLogin = field.NewTime(tableName, "last_login")
_admin.LastLoginIP = field.NewField(tableName, "last_login_ip")
_admin.LastLoginUA = field.NewString(tableName, "last_login_ua")
_admin.Lock = field.NewBool(tableName, "lock")
_admin.Roles = adminManyToManyRoles{
db: db.Session(&gorm.Session{}),
@@ -91,6 +92,7 @@ type admin struct {
LastLogin field.Time
LastLoginIP field.Field
LastLoginUA field.String
Lock field.Bool
Roles adminManyToManyRoles
fieldMap map[string]field.Expr
@@ -122,6 +124,7 @@ func (a *admin) updateTableName(table string) *admin {
a.LastLogin = field.NewTime(table, "last_login")
a.LastLoginIP = field.NewField(table, "last_login_ip")
a.LastLoginUA = field.NewString(table, "last_login_ua")
a.Lock = field.NewBool(table, "lock")
a.fillFieldMap()
@@ -138,7 +141,7 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 15)
a.fieldMap = make(map[string]field.Expr, 16)
a.fieldMap["id"] = a.ID
a.fieldMap["created_at"] = a.CreatedAt
a.fieldMap["updated_at"] = a.UpdatedAt
@@ -153,6 +156,7 @@ func (a *admin) fillFieldMap() {
a.fieldMap["last_login"] = a.LastLogin
a.fieldMap["last_login_ip"] = a.LastLoginIP
a.fieldMap["last_login_ua"] = a.LastLoginUA
a.fieldMap["lock"] = a.Lock
}

View File

@@ -36,10 +36,10 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
_balanceActivity.BalanceCurr = field.NewString(tableName, "balance_curr")
_balanceActivity.Remark = field.NewString(tableName, "remark")
_balanceActivity.CreatedAt = field.NewTime(tableName, "created_at")
_balanceActivity.Admin = balanceActivityHasOneAdmin{
_balanceActivity.User = balanceActivityBelongsToUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Admin", "models.User"),
RelationField: field.NewRelation("User", "models.User"),
Admin: struct {
field.RelationField
Roles struct {
@@ -55,7 +55,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
}
}
}{
RelationField: field.NewRelation("Admin.Admin", "models.Admin"),
RelationField: field.NewRelation("User.Admin", "models.Admin"),
Roles: struct {
field.RelationField
Permissions struct {
@@ -68,7 +68,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
}
}
}{
RelationField: field.NewRelation("Admin.Admin.Roles", "models.AdminRole"),
RelationField: field.NewRelation("User.Admin.Roles", "models.AdminRole"),
Permissions: struct {
field.RelationField
Parent struct {
@@ -78,16 +78,16 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
field.RelationField
}
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions", "models.Permission"),
RelationField: field.NewRelation("User.Admin.Roles.Permissions", "models.Permission"),
Parent: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Parent", "models.Permission"),
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Parent", "models.Permission"),
},
Children: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Children", "models.Permission"),
RelationField: field.NewRelation("User.Admin.Roles.Permissions.Children", "models.Permission"),
},
},
},
@@ -95,7 +95,7 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
Discount: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Discount", "models.ProductDiscount"),
RelationField: field.NewRelation("User.Discount", "models.ProductDiscount"),
},
Roles: struct {
field.RelationField
@@ -103,21 +103,15 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
field.RelationField
}
}{
RelationField: field.NewRelation("Admin.Roles", "models.UserRole"),
RelationField: field.NewRelation("User.Roles", "models.UserRole"),
Permissions: struct {
field.RelationField
}{
RelationField: field.NewRelation("Admin.Roles.Permissions", "models.Permission"),
RelationField: field.NewRelation("User.Roles.Permissions", "models.Permission"),
},
},
}
_balanceActivity.User = balanceActivityBelongsToUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("User", "models.User"),
}
_balanceActivity.Bill = balanceActivityBelongsToBill{
db: db.Session(&gorm.Session{}),
@@ -151,6 +145,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -179,6 +176,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -190,6 +190,9 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -198,8 +201,16 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -232,6 +243,33 @@ func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
}{
RelationField: field.NewRelation("Bill.Refund", "models.Refund"),
},
CouponUser: struct {
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Bill.CouponUser", "models.CouponUser"),
Coupon: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.CouponUser.Coupon", "models.Coupon"),
},
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("Bill.CouponUser.User", "models.User"),
},
},
}
_balanceActivity.Admin = balanceActivityBelongsToAdmin{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Admin", "models.Admin"),
}
_balanceActivity.fillFieldMap()
@@ -252,12 +290,12 @@ type balanceActivity struct {
BalanceCurr field.String
Remark field.String
CreatedAt field.Time
Admin balanceActivityHasOneAdmin
User balanceActivityBelongsToUser
User balanceActivityBelongsToUser
Bill balanceActivityBelongsToBill
Admin balanceActivityBelongsToAdmin
fieldMap map[string]field.Expr
}
@@ -313,24 +351,24 @@ func (b *balanceActivity) fillFieldMap() {
func (b balanceActivity) clone(db *gorm.DB) balanceActivity {
b.balanceActivityDo.ReplaceConnPool(db.Statement.ConnPool)
b.Admin.db = db.Session(&gorm.Session{Initialized: true})
b.Admin.db.Statement.ConnPool = db.Statement.ConnPool
b.User.db = db.Session(&gorm.Session{Initialized: true})
b.User.db.Statement.ConnPool = db.Statement.ConnPool
b.Bill.db = db.Session(&gorm.Session{Initialized: true})
b.Bill.db.Statement.ConnPool = db.Statement.ConnPool
b.Admin.db = db.Session(&gorm.Session{Initialized: true})
b.Admin.db.Statement.ConnPool = db.Statement.ConnPool
return b
}
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
b.balanceActivityDo.ReplaceDB(db)
b.Admin.db = db.Session(&gorm.Session{})
b.User.db = db.Session(&gorm.Session{})
b.Bill.db = db.Session(&gorm.Session{})
b.Admin.db = db.Session(&gorm.Session{})
return b
}
type balanceActivityHasOneAdmin struct {
type balanceActivityBelongsToUser struct {
db *gorm.DB
field.RelationField
@@ -361,87 +399,6 @@ type balanceActivityHasOneAdmin struct {
}
}
func (a balanceActivityHasOneAdmin) Where(conds ...field.Expr) *balanceActivityHasOneAdmin {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a balanceActivityHasOneAdmin) WithContext(ctx context.Context) *balanceActivityHasOneAdmin {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityHasOneAdmin) Session(session *gorm.Session) *balanceActivityHasOneAdmin {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityHasOneAdmin) Model(m *models.BalanceActivity) *balanceActivityHasOneAdminTx {
return &balanceActivityHasOneAdminTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityHasOneAdmin) Unscoped() *balanceActivityHasOneAdmin {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityHasOneAdminTx struct{ tx *gorm.Association }
func (a balanceActivityHasOneAdminTx) Find() (result *models.User, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityHasOneAdminTx) Append(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a balanceActivityHasOneAdminTx) Replace(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a balanceActivityHasOneAdminTx) Delete(values ...*models.User) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a balanceActivityHasOneAdminTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityHasOneAdminTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityHasOneAdminTx) Unscoped() *balanceActivityHasOneAdminTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityBelongsToUser struct {
db *gorm.DB
field.RelationField
}
func (a balanceActivityBelongsToUser) Where(conds ...field.Expr) *balanceActivityBelongsToUser {
if len(conds) == 0 {
return &a
@@ -542,6 +499,9 @@ type balanceActivityBelongsToBill struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -561,6 +521,15 @@ type balanceActivityBelongsToBill struct {
Refund struct {
field.RelationField
}
CouponUser struct {
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}
}
func (a balanceActivityBelongsToBill) Where(conds ...field.Expr) *balanceActivityBelongsToBill {
@@ -638,6 +607,87 @@ func (a balanceActivityBelongsToBillTx) Unscoped() *balanceActivityBelongsToBill
return &a
}
type balanceActivityBelongsToAdmin struct {
db *gorm.DB
field.RelationField
}
func (a balanceActivityBelongsToAdmin) Where(conds ...field.Expr) *balanceActivityBelongsToAdmin {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a balanceActivityBelongsToAdmin) WithContext(ctx context.Context) *balanceActivityBelongsToAdmin {
a.db = a.db.WithContext(ctx)
return &a
}
func (a balanceActivityBelongsToAdmin) Session(session *gorm.Session) *balanceActivityBelongsToAdmin {
a.db = a.db.Session(session)
return &a
}
func (a balanceActivityBelongsToAdmin) Model(m *models.BalanceActivity) *balanceActivityBelongsToAdminTx {
return &balanceActivityBelongsToAdminTx{a.db.Model(m).Association(a.Name())}
}
func (a balanceActivityBelongsToAdmin) Unscoped() *balanceActivityBelongsToAdmin {
a.db = a.db.Unscoped()
return &a
}
type balanceActivityBelongsToAdminTx struct{ tx *gorm.Association }
func (a balanceActivityBelongsToAdminTx) Find() (result *models.Admin, err error) {
return result, a.tx.Find(&result)
}
func (a balanceActivityBelongsToAdminTx) Append(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Replace(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Delete(values ...*models.Admin) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a balanceActivityBelongsToAdminTx) Clear() error {
return a.tx.Clear()
}
func (a balanceActivityBelongsToAdminTx) Count() int64 {
return a.tx.Count()
}
func (a balanceActivityBelongsToAdminTx) Unscoped() *balanceActivityBelongsToAdminTx {
a.tx = a.tx.Unscoped()
return &a
}
type balanceActivityDo struct{ gen.DO }
func (b balanceActivityDo) Debug() *balanceActivityDo {

View File

@@ -35,7 +35,7 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
_bill.TradeID = field.NewInt32(tableName, "trade_id")
_bill.ResourceID = field.NewInt32(tableName, "resource_id")
_bill.RefundID = field.NewInt32(tableName, "refund_id")
_bill.CouponID = field.NewInt32(tableName, "coupon_id")
_bill.CouponUserID = field.NewInt32(tableName, "coupon_user_id")
_bill.BillNo = field.NewString(tableName, "bill_no")
_bill.Info = field.NewString(tableName, "info")
_bill.Type = field.NewInt(tableName, "type")
@@ -143,6 +143,9 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -154,6 +157,9 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -162,8 +168,16 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -198,6 +212,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
RelationField: field.NewRelation("Refund", "models.Refund"),
}
_bill.CouponUser = billBelongsToCouponUser{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("CouponUser", "models.CouponUser"),
Coupon: struct {
field.RelationField
}{
RelationField: field.NewRelation("CouponUser.Coupon", "models.Coupon"),
},
User: struct {
field.RelationField
}{
RelationField: field.NewRelation("CouponUser.User", "models.User"),
},
}
_bill.fillFieldMap()
return _bill
@@ -206,22 +236,22 @@ func newBill(db *gorm.DB, opts ...gen.DOOption) bill {
type bill struct {
billDo
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
UserID field.Int32
TradeID field.Int32
ResourceID field.Int32
RefundID field.Int32
CouponID field.Int32
BillNo field.String
Info field.String
Type field.Int
Amount field.Field
Actual field.Field
User billBelongsToUser
ALL field.Asterisk
ID field.Int32
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
UserID field.Int32
TradeID field.Int32
ResourceID field.Int32
RefundID field.Int32
CouponUserID field.Int32
BillNo field.String
Info field.String
Type field.Int
Amount field.Field
Actual field.Field
User billBelongsToUser
Trade billBelongsToTrade
@@ -229,6 +259,8 @@ type bill struct {
Refund billBelongsToRefund
CouponUser billBelongsToCouponUser
fieldMap map[string]field.Expr
}
@@ -252,7 +284,7 @@ func (b *bill) updateTableName(table string) *bill {
b.TradeID = field.NewInt32(table, "trade_id")
b.ResourceID = field.NewInt32(table, "resource_id")
b.RefundID = field.NewInt32(table, "refund_id")
b.CouponID = field.NewInt32(table, "coupon_id")
b.CouponUserID = field.NewInt32(table, "coupon_user_id")
b.BillNo = field.NewString(table, "bill_no")
b.Info = field.NewString(table, "info")
b.Type = field.NewInt(table, "type")
@@ -274,7 +306,7 @@ func (b *bill) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (b *bill) fillFieldMap() {
b.fieldMap = make(map[string]field.Expr, 18)
b.fieldMap = make(map[string]field.Expr, 19)
b.fieldMap["id"] = b.ID
b.fieldMap["created_at"] = b.CreatedAt
b.fieldMap["updated_at"] = b.UpdatedAt
@@ -283,7 +315,7 @@ func (b *bill) fillFieldMap() {
b.fieldMap["trade_id"] = b.TradeID
b.fieldMap["resource_id"] = b.ResourceID
b.fieldMap["refund_id"] = b.RefundID
b.fieldMap["coupon_id"] = b.CouponID
b.fieldMap["coupon_user_id"] = b.CouponUserID
b.fieldMap["bill_no"] = b.BillNo
b.fieldMap["info"] = b.Info
b.fieldMap["type"] = b.Type
@@ -302,6 +334,8 @@ func (b bill) clone(db *gorm.DB) bill {
b.Resource.db.Statement.ConnPool = db.Statement.ConnPool
b.Refund.db = db.Session(&gorm.Session{Initialized: true})
b.Refund.db.Statement.ConnPool = db.Statement.ConnPool
b.CouponUser.db = db.Session(&gorm.Session{Initialized: true})
b.CouponUser.db.Statement.ConnPool = db.Statement.ConnPool
return b
}
@@ -311,6 +345,7 @@ func (b bill) replaceDB(db *gorm.DB) bill {
b.Trade.db = db.Session(&gorm.Session{})
b.Resource.db = db.Session(&gorm.Session{})
b.Refund.db = db.Session(&gorm.Session{})
b.CouponUser.db = db.Session(&gorm.Session{})
return b
}
@@ -519,6 +554,9 @@ type billBelongsToResource struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -692,6 +730,94 @@ func (a billBelongsToRefundTx) Unscoped() *billBelongsToRefundTx {
return &a
}
type billBelongsToCouponUser struct {
db *gorm.DB
field.RelationField
Coupon struct {
field.RelationField
}
User struct {
field.RelationField
}
}
func (a billBelongsToCouponUser) Where(conds ...field.Expr) *billBelongsToCouponUser {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a billBelongsToCouponUser) WithContext(ctx context.Context) *billBelongsToCouponUser {
a.db = a.db.WithContext(ctx)
return &a
}
func (a billBelongsToCouponUser) Session(session *gorm.Session) *billBelongsToCouponUser {
a.db = a.db.Session(session)
return &a
}
func (a billBelongsToCouponUser) Model(m *models.Bill) *billBelongsToCouponUserTx {
return &billBelongsToCouponUserTx{a.db.Model(m).Association(a.Name())}
}
func (a billBelongsToCouponUser) Unscoped() *billBelongsToCouponUser {
a.db = a.db.Unscoped()
return &a
}
type billBelongsToCouponUserTx struct{ tx *gorm.Association }
func (a billBelongsToCouponUserTx) Find() (result *models.CouponUser, err error) {
return result, a.tx.Find(&result)
}
func (a billBelongsToCouponUserTx) Append(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a billBelongsToCouponUserTx) Replace(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a billBelongsToCouponUserTx) Delete(values ...*models.CouponUser) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a billBelongsToCouponUserTx) Clear() error {
return a.tx.Clear()
}
func (a billBelongsToCouponUserTx) Count() int64 {
return a.tx.Count()
}
func (a billBelongsToCouponUserTx) Unscoped() *billBelongsToCouponUserTx {
a.tx = a.tx.Unscoped()
return &a
}
type billDo struct{ gen.DO }
func (b billDo) Debug() *billDo {

View File

@@ -138,6 +138,9 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -149,6 +152,9 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -157,8 +163,16 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -490,6 +504,9 @@ type channelBelongsToResource struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ var (
Channel *channel
Client *client
Coupon *coupon
CouponUser *couponUser
Edge *edge
Inquiry *inquiry
LinkAdminRole *linkAdminRole
@@ -63,6 +64,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Channel = &Q.Channel
Client = &Q.Client
Coupon = &Q.Coupon
CouponUser = &Q.CouponUser
Edge = &Q.Edge
Inquiry = &Q.Inquiry
LinkAdminRole = &Q.LinkAdminRole
@@ -102,6 +104,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Channel: newChannel(db, opts...),
Client: newClient(db, opts...),
Coupon: newCoupon(db, opts...),
CouponUser: newCouponUser(db, opts...),
Edge: newEdge(db, opts...),
Inquiry: newInquiry(db, opts...),
LinkAdminRole: newLinkAdminRole(db, opts...),
@@ -142,6 +145,7 @@ type Query struct {
Channel channel
Client client
Coupon coupon
CouponUser couponUser
Edge edge
Inquiry inquiry
LinkAdminRole linkAdminRole
@@ -183,6 +187,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
Channel: q.Channel.clone(db),
Client: q.Client.clone(db),
Coupon: q.Coupon.clone(db),
CouponUser: q.CouponUser.clone(db),
Edge: q.Edge.clone(db),
Inquiry: q.Inquiry.clone(db),
LinkAdminRole: q.LinkAdminRole.clone(db),
@@ -231,6 +236,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Channel: q.Channel.replaceDB(db),
Client: q.Client.replaceDB(db),
Coupon: q.Coupon.replaceDB(db),
CouponUser: q.CouponUser.replaceDB(db),
Edge: q.Edge.replaceDB(db),
Inquiry: q.Inquiry.replaceDB(db),
LinkAdminRole: q.LinkAdminRole.replaceDB(db),
@@ -269,6 +275,7 @@ type queryCtx struct {
Channel *channelDo
Client *clientDo
Coupon *couponDo
CouponUser *couponUserDo
Edge *edgeDo
Inquiry *inquiryDo
LinkAdminRole *linkAdminRoleDo
@@ -307,6 +314,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Channel: q.Channel.WithContext(ctx),
Client: q.Client.WithContext(ctx),
Coupon: q.Coupon.WithContext(ctx),
CouponUser: q.CouponUser.WithContext(ctx),
Edge: q.Edge.WithContext(ctx),
Inquiry: q.Inquiry.WithContext(ctx),
LinkAdminRole: q.LinkAdminRole.WithContext(ctx),

View File

@@ -128,6 +128,9 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -139,6 +142,9 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -147,8 +153,16 @@ func newLogsUserUsage(db *gorm.DB, opts ...gen.DOOption) logsUserUsage {
RelationField: field.NewRelation("Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Resource.Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -391,6 +405,9 @@ type logsUserUsageBelongsToResource struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -36,6 +36,29 @@ func newProduct(db *gorm.DB, opts ...gen.DOOption) product {
_product.Description = field.NewString(tableName, "description")
_product.Sort = field.NewInt32(tableName, "sort")
_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()
@@ -55,6 +78,7 @@ type product struct {
Description field.String
Sort field.Int32
Status field.Int
Skus productHasManySkus
fieldMap map[string]field.Expr
}
@@ -96,7 +120,7 @@ func (p *product) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
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["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
@@ -106,18 +130,113 @@ func (p *product) fillFieldMap() {
p.fieldMap["description"] = p.Description
p.fieldMap["sort"] = p.Sort
p.fieldMap["status"] = p.Status
}
func (p product) clone(db *gorm.DB) product {
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
}
func (p product) replaceDB(db *gorm.DB) product {
p.productDo.ReplaceDB(db)
p.Skus.db = db.Session(&gorm.Session{})
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 }
func (p productDo) Debug() *productDo {

View File

@@ -36,10 +36,34 @@ func newProductSku(db *gorm.DB, opts ...gen.DOOption) productSku {
_productSku.Code = field.NewString(tableName, "code")
_productSku.Name = field.NewString(tableName, "name")
_productSku.Price = field.NewField(tableName, "price")
_productSku.PriceMin = field.NewField(tableName, "price_min")
_productSku.Status = field.NewInt32(tableName, "status")
_productSku.Sort = field.NewInt32(tableName, "sort")
_productSku.Product = productSkuBelongsToProduct{
db: db.Session(&gorm.Session{}),
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{
@@ -66,6 +90,9 @@ type productSku struct {
Code field.String
Name field.String
Price field.Field
PriceMin field.Field
Status field.Int32
Sort field.Int32
Product productSkuBelongsToProduct
Discount productSkuBelongsToDiscount
@@ -94,6 +121,9 @@ func (p *productSku) updateTableName(table string) *productSku {
p.Code = field.NewString(table, "code")
p.Name = field.NewString(table, "name")
p.Price = field.NewField(table, "price")
p.PriceMin = field.NewField(table, "price_min")
p.Status = field.NewInt32(table, "status")
p.Sort = field.NewInt32(table, "sort")
p.fillFieldMap()
@@ -110,7 +140,7 @@ func (p *productSku) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (p *productSku) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 11)
p.fieldMap = make(map[string]field.Expr, 14)
p.fieldMap["id"] = p.ID
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
@@ -120,6 +150,9 @@ func (p *productSku) fillFieldMap() {
p.fieldMap["code"] = p.Code
p.fieldMap["name"] = p.Name
p.fieldMap["price"] = p.Price
p.fieldMap["price_min"] = p.PriceMin
p.fieldMap["status"] = p.Status
p.fieldMap["sort"] = p.Sort
}
@@ -143,6 +176,16 @@ type productSkuBelongsToProduct struct {
db *gorm.DB
field.RelationField
Skus struct {
field.RelationField
Product struct {
field.RelationField
}
Discount struct {
field.RelationField
}
}
}
func (a productSkuBelongsToProduct) Where(conds ...field.Expr) *productSkuBelongsToProduct {

View File

@@ -115,8 +115,16 @@ func newProductSkuUser(db *gorm.DB, opts ...gen.DOOption) productSkuUser {
RelationField: field.NewRelation("ProductSku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("ProductSku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("ProductSku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -331,6 +339,9 @@ type productSkuUserBelongsToProductSku struct {
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -153,6 +153,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -181,6 +184,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -192,6 +198,9 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -200,8 +209,16 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
RelationField: field.NewRelation("Channels.Resource.Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
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 {
field.RelationField
@@ -387,6 +404,9 @@ type proxyHasManyChannels struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -44,6 +44,9 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField
@@ -52,8 +55,16 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource {
RelationField: field.NewRelation("Short.Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Short.Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Short.Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -266,6 +277,9 @@ type resourceHasOneShort struct {
field.RelationField
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -43,8 +43,16 @@ func newResourceLong(db *gorm.DB, opts ...gen.DOOption) resourceLong {
RelationField: field.NewRelation("Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -149,6 +157,9 @@ type resourceLongHasOneSku struct {
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -43,8 +43,16 @@ func newResourceShort(db *gorm.DB, opts ...gen.DOOption) resourceShort {
RelationField: field.NewRelation("Sku", "models.ProductSku"),
Product: struct {
field.RelationField
Skus struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Sku.Product", "models.Product"),
Skus: struct {
field.RelationField
}{
RelationField: field.NewRelation("Sku.Product.Skus", "models.ProductSku"),
},
},
Discount: struct {
field.RelationField
@@ -149,6 +157,9 @@ type resourceShortHasOneSku struct {
Product struct {
field.RelationField
Skus struct {
field.RelationField
}
}
Discount struct {
field.RelationField

View File

@@ -109,6 +109,14 @@ func userRouter(api fiber.Router) {
// 前台
inquiry := api.Group("/inquiry")
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 +124,7 @@ func clientRouter(api fiber.Router) {
client := api
// 验证短信令牌
client.Post("/verify/sms", handlers.SmsCode)
client.Post("/verify/sms", handlers.SendSmsCode)
// 套餐定价查询
resource := client.Group("/resource")
@@ -159,35 +167,50 @@ func adminRouter(api fiber.Router) {
// user 用户
var user = api.Group("/user")
user.Post("/page", handlers.PageUserByAdmin)
user.Post("/page/not-bind", handlers.PageUserNotBindByAdmin)
user.Post("/get", handlers.GetUserByAdmin)
user.Post("/create", handlers.CreateUserByAdmin)
user.Post("/update", handlers.UpdateUserByAdmin)
user.Post("/remove", handlers.RemoveUserByAdmin)
user.Post("/bind", handlers.BindAdmin)
user.Post("/update/bind", handlers.BindAdmin)
user.Post("/update/balance", handlers.UpdateUserBalanceByAdmin)
user.Post("/update/balance-inc", handlers.UpdateUserBalanceIncByAdmin)
user.Post("/update/balance-dec", handlers.UpdateUserBalanceDecByAdmin)
// resource 套餐
var resource = api.Group("/resource")
resource.Post("/short/page", handlers.PageResourceShortByAdmin)
resource.Post("/short/page/of-user", handlers.PageResourceShortOfUserByAdmin)
resource.Post("/long/page", handlers.PageResourceLongByAdmin)
resource.Post("/long/page/of-user", handlers.PageResourceLongOfUserByAdmin)
resource.Post("/update", handlers.UpdateResourceByAdmin)
// batch 批次
var batch = api.Group("/batch")
batch.Post("/page", handlers.PageBatchByAdmin)
batch.Post("/page/of-user", handlers.PageBatchOfUserByAdmin)
// channel 通道
var channel = api.Group("/channel")
channel.Post("/page", handlers.PageChannelByAdmin)
channel.Post("/page/of-user", handlers.PageChannelOfUserByAdmin)
// trade 交易
var trade = api.Group("/trade")
trade.Post("/page", handlers.PageTradeByAdmin)
trade.Post("/page/of-user", handlers.PageTradeOfUserByAdmin)
trade.Post("/complete", handlers.TradeCompleteByAdmin)
// bill 账单
var bill = api.Group("/bill")
bill.Post("/page", handlers.PageBillByAdmin)
bill.Post("/page/of-user", handlers.PageBillOfUserByAdmin)
// balance-activity 余额变动
var balanceActivity = api.Group("/balance-activity")
balanceActivity.Post("/page", handlers.PageBalanceActivityByAdmin)
balanceActivity.Post("/page/of-user", handlers.PageBalanceActivityOfUserByAdmin)
// product 产品
var product = api.Group("/product")
@@ -200,6 +223,7 @@ func adminRouter(api fiber.Router) {
product.Post("/sku/page", handlers.PageProductSkuByAdmin)
product.Post("/sku/create", handlers.CreateProductSku)
product.Post("/sku/update", handlers.UpdateProductSku)
product.Post("/sku/update/status", handlers.UpdateProductStatusSku)
product.Post("/sku/remove", handlers.DeleteProductSku)
product.Post("/sku/update/discount/batch", handlers.BatchUpdateProductSkuDiscount)

View File

@@ -15,7 +15,7 @@ var Admin = &adminService{}
type adminService struct{}
func (s *adminService) PageAdmins(req core.PageReq) (result []*m.Admin, count int64, err error) {
func (s *adminService) Page(req core.PageReq) (result []*m.Admin, count int64, err error) {
return q.Admin.
Preload(q.Admin.Roles).
Omit(q.Admin.Password).
@@ -30,25 +30,14 @@ func (s *adminService) All() (result []*m.Admin, err error) {
Find()
}
type CreateAdmin struct {
Username string `json:"username" validate:"required,min=3,max=50"`
Password string `json:"password" validate:"required,min=6,max=50"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Status *m.AdminStatus `json:"status"`
Roles []int32 `json:"roles"`
}
func (s *adminService) CreateAdmin(create *CreateAdmin) error {
func (s *adminService) Create(create *CreateAdmin) error {
// 哈希密码
hash, err := bcrypt.GenerateFromPassword([]byte(create.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
return q.Q.Transaction(func(tx *q.Query) error {
return q.Q.Transaction(func(q *q.Query) error {
// 创建管理员
admin := &m.Admin{
Username: create.Username,
@@ -59,7 +48,7 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
Email: create.Email,
Status: u.Else(create.Status, m.AdminStatusEnabled),
}
if err := tx.Admin.Create(admin); err != nil {
if err := q.Admin.Create(admin); err != nil {
return err
}
@@ -72,7 +61,7 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
RoleID: roleID,
}
}
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
if err := q.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
return err
}
}
@@ -81,18 +70,18 @@ func (s *adminService) CreateAdmin(create *CreateAdmin) error {
})
}
type UpdateAdmin struct {
Id int32 `json:"id" validate:"required"`
Password *string `json:"password"`
type CreateAdmin struct {
Username string `json:"username" validate:"required,min=3,max=50"`
Password string `json:"password" validate:"required,min=6,max=50"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Status *m.AdminStatus `json:"status"`
Roles *[]int32 `json:"roles"`
Roles []int32 `json:"roles"`
}
func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
func (s *adminService) Update(update *UpdateAdmin) error {
simples := make([]field.AssignExpr, 0)
if update.Password != nil {
@@ -118,11 +107,14 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
simples = append(simples, q.Admin.Status.Value(int(*update.Status)))
}
return q.Q.Transaction(func(tx *q.Query) error {
return q.Q.Transaction(func(q *q.Query) error {
// 更新管理员基本信息
if len(simples) > 0 {
_, err := tx.Admin.
Where(tx.Admin.ID.Eq(update.Id), tx.Admin.Username.Neq("admin")).
_, err := q.Admin.
Where(
q.Admin.ID.Eq(update.Id),
q.Admin.Lock.Is(false),
).
UpdateSimple(simples...)
if err != nil {
return err
@@ -132,7 +124,7 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
// 更新角色关联
if update.Roles != nil {
roles := *update.Roles
if _, err := tx.LinkAdminRole.Where(tx.LinkAdminRole.AdminID.Eq(update.Id)).Delete(); err != nil {
if _, err := q.LinkAdminRole.Where(q.LinkAdminRole.AdminID.Eq(update.Id)).Delete(); err != nil {
return err
}
if len(roles) > 0 {
@@ -143,7 +135,7 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
RoleID: roleID,
}
}
if err := tx.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
if err := q.LinkAdminRole.CreateInBatches(links, 1000); err != nil {
return err
}
}
@@ -153,7 +145,23 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
})
}
func (s *adminService) RemoveAdmin(id int32) error {
_, err := q.Admin.Where(q.Admin.ID.Eq(id), q.Admin.Username.Neq("admin")).UpdateColumn(q.Admin.DeletedAt, time.Now())
type UpdateAdmin struct {
Id int32 `json:"id" validate:"required"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Status *m.AdminStatus `json:"status"`
Roles *[]int32 `json:"roles"`
}
func (s *adminService) Remove(id int32) error {
_, err := q.Admin.
Where(
q.Admin.ID.Eq(id),
q.Admin.Lock.Is(false),
).
UpdateColumn(q.Admin.DeletedAt, time.Now())
return err
}

View File

@@ -9,28 +9,42 @@ var Bill = &billService{}
type billService struct{}
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) error {
return q.Bill.Create(&m.Bill{
func (s *billService) CreateForBalance(q *q.Query, uid, tradeId int32, detail *TradeDetail) (*m.Bill, error) {
bill := &m.Bill{
UserID: uid,
BillNo: ID.GenReadable("bil"),
TradeID: &tradeId,
Type: m.BillTypeRecharge,
Info: &detail.Subject,
Amount: detail.Amount,
Amount: detail.Discounted,
Actual: detail.Actual,
})
}
err := q.Bill.Create(bill)
if err != nil {
return nil, err
}
return bill, nil
}
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) error {
return q.Bill.Create(&m.Bill{
UserID: uid,
BillNo: ID.GenReadable("bil"),
ResourceID: &resourceId,
TradeID: tradeId,
CouponID: detail.CouponId,
Type: m.BillTypeConsume,
Info: &detail.Subject,
Amount: detail.Amount,
Actual: detail.Actual,
})
func (s *billService) CreateForResource(q *q.Query, uid, resourceId int32, tradeId *int32, detail *TradeDetail) (*m.Bill, error) {
bill := &m.Bill{
UserID: uid,
BillNo: ID.GenReadable("bil"),
ResourceID: &resourceId,
TradeID: tradeId,
CouponUserID: detail.CouponUserId,
Type: m.BillTypeConsume,
Info: &detail.Subject,
Amount: detail.Discounted,
Actual: detail.Actual,
}
err := q.Bill.Create(bill)
if err != nil {
return nil, err
}
return bill, nil
}

View File

@@ -94,7 +94,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) {
var sub = resource.Short
info.ShortId = &sub.ID
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.Quota = sub.Quota
info.Used = sub.Used

View File

@@ -229,7 +229,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int
// 提交配置
secret := strings.Split(u.Z(proxy.Secret), ":")
gateway := g.NewGateway(proxy.IP.String(), secret[0], secret[1])
if env.DebugExternalChange {
if env.RunMode == env.RunModeProd {
// 连接节点到网关
err = g.Cloud.CloudConnect(&g.CloudConnectReq{
@@ -292,7 +292,8 @@ func (s *channelBaiyinProvider) RemoveChannels(batch string) error {
}
// 提交配置
if env.DebugExternalChange {
if env.RunMode == env.RunModeProd {
// 断开节点连接
g.Cloud.CloudDisconnect(&g.CloudDisconnectReq{
Uuid: proxy.Mac,

View File

@@ -3,6 +3,7 @@ package services
import (
"errors"
"fmt"
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
@@ -27,36 +28,32 @@ func (s *couponService) Page(req *core.PageReq) (result []*m.Coupon, count int64
func (s *couponService) Create(data CreateCouponData) error {
return q.Coupon.Create(&m.Coupon{
UserID: data.UserID,
Code: data.Code,
Remark: data.Remark,
Amount: data.Amount,
MinAmount: data.MinAmount,
Status: m.CouponStatusUnused,
ExpireAt: data.ExpireAt,
Name: data.Name,
Amount: data.Amount,
MinAmount: data.MinAmount,
Count: int32(u.Else(data.Count, 1)),
Status: u.Else(data.Status, m.CouponStatusEnabled),
ExpireType: u.Else(data.ExpireType, m.CouponExpireTypeNever),
ExpireAt: data.ExpireAt,
ExpireIn: data.ExpireIn,
})
}
type CreateCouponData struct {
UserID *int32 `json:"user_id"`
Code string `json:"code" validate:"required"`
Remark *string `json:"remark"`
Amount decimal.Decimal `json:"amount" validate:"required"`
MinAmount decimal.Decimal `json:"min_amount"`
ExpireAt *time.Time `json:"expire_at"`
Name string `json:"name" validate:"required"`
Amount decimal.Decimal `json:"amount" validate:"required"`
MinAmount decimal.Decimal `json:"min_amount"`
Count *int `json:"count"`
Status *m.CouponStatus `json:"status"`
ExpireType *m.CouponExpireType `json:"expire_type"`
ExpireAt *time.Time `json:"expire_at"`
ExpireIn *int `json:"expire_in"`
}
func (s *couponService) Update(data UpdateCouponData) error {
do := make([]field.AssignExpr, 0)
if data.UserID != nil {
do = append(do, q.Coupon.UserID.Value(*data.UserID))
}
if data.Code != nil {
do = append(do, q.Coupon.Code.Value(*data.Code))
}
if data.Remark != nil {
do = append(do, q.Coupon.Remark.Value(*data.Remark))
if data.Name != nil {
do = append(do, q.Coupon.Name.Value(*data.Name))
}
if data.Amount != nil {
do = append(do, q.Coupon.Amount.Value(*data.Amount))
@@ -64,26 +61,36 @@ func (s *couponService) Update(data UpdateCouponData) error {
if data.MinAmount != nil {
do = append(do, q.Coupon.MinAmount.Value(*data.MinAmount))
}
if data.Count != nil {
do = append(do, q.Coupon.Count_.Value(int32(*data.Count)))
}
if data.Status != nil {
do = append(do, q.Coupon.Status.Value(int(*data.Status)))
}
if data.ExpireType != nil {
do = append(do, q.Coupon.ExpireType.Value(int(*data.ExpireType)))
}
if data.ExpireAt != nil {
do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt))
}
if data.ExpireIn != nil {
do = append(do, q.Coupon.ExpireIn.Value(*data.ExpireIn))
}
_, err := q.Coupon.Where(q.Coupon.ID.Eq(data.ID)).UpdateSimple(do...)
return err
}
type UpdateCouponData struct {
ID int32 `json:"id" validate:"required"`
UserID *int32 `json:"user_id"`
Code *string `json:"code"`
Remark *string `json:"remark"`
Amount *decimal.Decimal `json:"amount"`
MinAmount *decimal.Decimal `json:"min_amount"`
Status *m.CouponStatus `json:"status"`
ExpireAt *time.Time `json:"expire_at"`
ID int32 `json:"id" validate:"required"`
Name *string `json:"name"`
Amount *decimal.Decimal `json:"amount"`
MinAmount *decimal.Decimal `json:"min_amount"`
Count *int `json:"count"`
Status *m.CouponStatus `json:"status"`
ExpireType *m.CouponExpireType `json:"expire_type"`
ExpireAt *time.Time `json:"expire_at"`
ExpireIn *int `json:"expire_in"`
}
func (s *couponService) Delete(id int32) error {
@@ -91,14 +98,14 @@ func (s *couponService) Delete(id int32) error {
return err
}
func (s *couponService) GetCouponAvailableByCode(code string, amount decimal.Decimal, uid *int32) (*m.Coupon, error) {
// GetUserCoupon 获取用户的指定优惠券
func (s *couponService) GetUserCoupon(uid int32, cuid int32, amount decimal.Decimal) (*m.CouponUser, error) {
// 获取优惠券
coupon, err := q.Coupon.Where(
q.Coupon.Code.Eq(code),
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
q.Coupon.
Where(q.Coupon.ExpireAt.Gt(time.Now())).
Or(q.Coupon.ExpireAt.IsNull()),
assigned, err := q.CouponUser.Joins(q.CouponUser.Coupon).Where(
q.CouponUser.ID.Eq(cuid),
q.CouponUser.UserID.Eq(uid),
q.CouponUser.Status.Eq(int(m.CouponUserStatusUnused)),
q.CouponUser.ExpireAt.Gt(time.Now()),
).Take()
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, core.NewBizErr("优惠券不存在或已失效")
@@ -108,32 +115,22 @@ func (s *couponService) GetCouponAvailableByCode(code string, amount decimal.Dec
}
// 检查最小使用额度
if amount.Cmp(coupon.MinAmount) < 0 {
return nil, core.NewBizErr(fmt.Sprintf("使用此优惠券的最小额度为 %s", coupon.MinAmount))
if amount.Cmp(assigned.Coupon.MinAmount) < 0 {
return nil, core.NewBizErr(fmt.Sprintf("使用此优惠券的最小额度为 %s", assigned.Coupon.MinAmount))
}
// 检查所属
if coupon.UserID != nil {
if uid == nil {
return nil, core.NewBizErr("检查优惠券所属用户失败")
}
if *coupon.UserID != *uid {
return nil, core.NewBizErr("优惠券不属于当前用户")
}
}
return coupon, nil
return assigned, nil
}
func (s *couponService) UseCoupon(q *q.Query, id int32) error {
_, err := q.Coupon.
func (s *couponService) UseCoupon(q *q.Query, cuid int32) error {
_, err := q.CouponUser.
Where(
q.Coupon.ID.Eq(id),
q.Coupon.Status.Eq(int(m.CouponStatusUnused)),
q.Coupon.ExpireAt.Gt(time.Now()),
q.CouponUser.ID.Eq(cuid),
q.CouponUser.Status.Eq(int(m.CouponUserStatusUnused)),
).
UpdateSimple(
q.Coupon.Status.Value(int(m.CouponStatusUsed)),
q.CouponUser.Status.Value(int(m.CouponUserStatusUsed)),
q.CouponUser.UsedAt.Value(time.Now()),
)
return err
}

View File

@@ -13,11 +13,6 @@ var Product = &productService{}
type productService struct{}
// 获取产品价格
func (s *productService) GetPrice(code string) {
q.ProductSku.Where(q.ProductSku.Code.Eq(code)).Find()
}
// 获取所有产品
func (s *productService) AllProducts() ([]*m.Product, error) {
return q.Product.
@@ -25,6 +20,61 @@ func (s *productService) AllProducts() ([]*m.Product, error) {
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,
).
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 {
return q.Product.Create(&m.Product{

View File

@@ -20,7 +20,7 @@ func (s *productSkuService) All(product_code string) (result []*m.ProductSku, er
Joins(q.ProductSku.Product).
Where(q.Product.As("Product").Code.Eq(product_code)).
Select(q.ProductSku.ALL).
Order(q.ProductSku.CreatedAt.Desc()).
Order(q.ProductSku.Sort).
Find()
}
@@ -30,9 +30,9 @@ func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []
do = append(do, q.ProductSku.ProductID.Eq(*productId))
}
return q.ProductSku.
Joins(q.ProductSku.Discount).
Joins(q.ProductSku.Discount, q.ProductSku.Product).
Where(do...).
Order(q.ProductSku.CreatedAt.Desc()).
Order(q.ProductSku.Sort).
FindByPage(req.GetOffset(), req.GetLimit())
}
@@ -42,12 +42,18 @@ func (s *productSkuService) Create(create CreateProductSkuData) (err error) {
return core.NewBizErr("产品价格的格式不正确", err)
}
priceMin, err := decimal.NewFromString(create.PriceMin)
if err != nil {
return core.NewBizErr("产品最低价格的格式不正确", err)
}
return q.ProductSku.Create(&m.ProductSku{
ProductID: create.ProductID,
DiscountId: create.DiscountID,
Code: create.Code,
Name: create.Name,
Price: price,
PriceMin: priceMin,
})
}
@@ -57,6 +63,7 @@ type CreateProductSkuData struct {
Code string `json:"code"`
Name string `json:"name"`
Price string `json:"price"`
PriceMin string `json:"price_min"`
}
func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
@@ -69,6 +76,13 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
}
do = append(do, q.ProductSku.Price.Value(price))
}
if update.PriceMin != "" {
priceMin, err := decimal.NewFromString(update.PriceMin)
if err != nil {
return core.NewBizErr("产品最低价格的格式不正确", err)
}
do = append(do, q.ProductSku.PriceMin.Value(priceMin))
}
if update.DiscountID != nil {
do = append(do, q.ProductSku.DiscountId.Value(*update.DiscountID))
}
@@ -78,6 +92,9 @@ func (s *productSkuService) Update(update UpdateProductSkuData) (err error) {
if update.Name != nil {
do = append(do, q.ProductSku.Name.Value(*update.Name))
}
if update.Status != nil {
do = append(do, q.ProductSku.Status.Value(*update.Status))
}
_, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...)
return err
@@ -89,6 +106,8 @@ type UpdateProductSkuData struct {
Code *string `json:"code"`
Name *string `json:"name"`
Price *string `json:"price"`
PriceMin string `json:"price_min"`
Status *int32 `json:"status"`
}
func (s *productSkuService) Delete(id int32) (err error) {

View File

@@ -28,11 +28,6 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
return q.Q.Transaction(func(q *q.Query) error {
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
return core.NewServErr("更新用户余额失败", err)
}
// 保存套餐
resource, err := s.Create(q, user.ID, now, data)
if err != nil {
@@ -40,14 +35,19 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
}
// 生成账单
err = Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
bill, err := Bill.CreateForResource(q, user.ID, resource.ID, nil, detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil, &bill.ID); err != nil {
return core.NewServErr("更新用户余额失败", err)
}
// 核销优惠券
if detail.CouponId != nil {
err = Coupon.UseCoupon(q, *detail.CouponId)
if detail.CouponUserId != nil {
err = Coupon.UseCoupon(q, *detail.CouponUserId)
if err != nil {
return core.NewServErr("核销优惠券失败", err)
}
@@ -144,31 +144,31 @@ type UpdateResourceData struct {
Active *bool `json:"active"`
}
func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, couponCode *string) (*m.ProductSku, *m.ProductDiscount, *m.Coupon, decimal.Decimal, decimal.Decimal, error) {
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.
Joins(q.ProductSku.Discount).
Where(q.ProductSku.Code.Eq(skuCode)).
Where(q.ProductSku.Code.Eq(skuCode), q.ProductSku.Status.Eq(int32(m.SkuStatusEnabled))).
Take()
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(fmt.Sprintf("产品不可用 %s", skuCode), err)
}
// 原价
price := sku.Price
amount := price.Mul(decimal.NewFromInt32(count))
amountMin := sku.PriceMin.Mul(decimal.NewFromInt32(count))
amount := sku.Price.Mul(decimal.NewFromInt32(count))
// 折扣价
discount := sku.Discount
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()
if user != nil && user.DiscountID != nil { // 用户特殊优惠
uDiscount, err := q.ProductDiscount.Where(q.ProductDiscount.ID.Eq(*user.DiscountID)).Take()
if err != nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, core.NewServErr("客户特殊价查询失败", err)
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, core.NewServErr("客户特殊价查询失败", err)
}
uDiscountRate := uDiscount.Rate()
@@ -180,30 +180,33 @@ func (s *resourceService) CalcPrice(skuCode string, count int32, user *m.User, c
discounted := amount.Mul(discountRate)
// 优惠价
uid := (*int32)(nil)
if user != nil {
uid = &user.ID
}
coupon := (*m.Coupon)(nil)
coupon := (*m.CouponUser)(nil)
couponApplied := discounted.Copy()
if couponCode != nil {
if user != nil && cuid != nil {
var err error
coupon, err = Coupon.GetCouponAvailableByCode(*couponCode, discounted, uid)
coupon, err = Coupon.GetUserCoupon(user.ID, *cuid, discounted)
if err != nil {
return nil, nil, nil, decimal.Zero, decimal.Zero, err
return nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, err
}
couponApplied = discounted.Sub(coupon.Amount)
couponApplied = discounted.Sub(coupon.Coupon.Amount)
}
return sku, discount, coupon, discounted, couponApplied, nil
// 约束到最低价格
if discounted.Cmp(amountMin) < 0 {
discounted = amountMin.Copy()
}
if couponApplied.Cmp(amountMin) < 0 {
couponApplied = amountMin.Copy()
}
return sku, discount, coupon, amount, discounted, couponApplied, nil
}
type CreateResourceData struct {
Type m.ResourceType `json:"type" validate:"required"`
Short *CreateShortResourceData `json:"short,omitempty"`
Long *CreateLongResourceData `json:"long,omitempty"`
CouponCode *string `json:"coupon,omitempty"`
Type m.ResourceType `json:"type" validate:"required"`
Short *CreateShortResourceData `json:"short,omitempty"`
Long *CreateLongResourceData `json:"long,omitempty"`
CouponId *int32 `json:"coupon,omitempty"`
}
type CreateShortResourceData struct {
@@ -244,20 +247,20 @@ func (data *CreateResourceData) Code() string {
case data.Type == m.ResourceTypeShort && data.Short != nil:
return fmt.Sprintf(
"mode=%s,live=%d,expire=%d",
"mode=%s&live=%d&expire=%d",
data.Short.Mode.Code(), data.Short.Live, u.Else(data.Short.Expire, 0),
)
case data.Type == m.ResourceTypeLong && data.Long != nil:
return fmt.Sprintf(
"mode=%s,live=%d,expire=%d",
"mode=%s&live=%d&expire=%d",
data.Long.Mode.Code(), data.Long.Live, u.Else(data.Long.Expire, 0),
)
}
}
func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error) {
sku, discount, coupon, amount, actual, err := Resource.CalcPrice(data.Code(), data.Count(), user, data.CouponCode)
sku, discount, coupon, amount, discounted, actual, err := Resource.CalcPrice(data.Code(), data.Count(), user, data.CouponId)
if err != nil {
return nil, err
}
@@ -266,17 +269,16 @@ func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error)
if discount != nil {
discountId = &discount.ID
}
var couponId *int32 = nil
var couponUserId *int32 = nil
if coupon != nil {
couponId = &coupon.ID
couponUserId = &coupon.ID
}
return &TradeDetail{
data,
m.TradeTypePurchase,
sku.Name,
amount, actual,
discountId, discount,
couponId, coupon,
amount, discounted, actual,
discountId, couponUserId,
}, nil
}

View File

@@ -302,17 +302,18 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
switch trade.Type {
case m.TradeTypeRecharge:
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil); err != nil {
return err
}
// 生成账单
err = Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
bill, err := Bill.CreateForBalance(q, user.ID, trade.ID, &detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
// 更新用户余额
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil, &bill.ID); err != nil {
return err
}
case m.TradeTypePurchase:
data, ok := detail.Product.(*CreateResourceData)
if !ok {
@@ -326,14 +327,14 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
}
// 生成账单
err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
_, err = Bill.CreateForResource(q, user.ID, resource.ID, &trade.ID, &detail)
if err != nil {
return core.NewServErr("生成账单失败", err)
}
// 核销优惠券
if detail.CouponId != nil {
err = Coupon.UseCoupon(q, *detail.CouponId)
if detail.CouponUserId != nil {
err = Coupon.UseCoupon(q, *detail.CouponUserId)
if err != nil {
return core.NewServErr("核销优惠券失败", err)
}
@@ -614,15 +615,14 @@ type ProductData interface {
}
type TradeDetail struct {
Product ProductData `json:"product"`
Type m.TradeType `json:"type"`
Subject string `json:"subject"`
Amount decimal.Decimal `json:"amount"`
Actual decimal.Decimal `json:"actual"`
DiscountId *int32 `json:"discount_id,omitempty"`
Discount *m.ProductDiscount `json:"-"` // 不应缓存
CouponId *int32 `json:"coupon_id,omitempty"`
Coupon *m.Coupon `json:"-"` // 不应缓存
Product ProductData `json:"product"`
Type m.TradeType `json:"type"`
Subject string `json:"subject"`
Amount decimal.Decimal `json:"amount"`
Discounted decimal.Decimal `json:"discounted"`
Actual decimal.Decimal `json:"actual"`
DiscountId *int32 `json:"discount_id,omitempty"`
CouponUserId *int32 `json:"coupon_id,omitempty"`
}
type TradeErr string

View File

@@ -39,11 +39,11 @@ func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Deci
}
return q.Q.Transaction(func(q *q.Query) error {
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId, nil)
})
}
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32, billId *int32) error {
balance := user.Balance.Add(amount)
if balance.IsNegative() {
return core.NewServErr("用户余额不足")
@@ -66,6 +66,7 @@ func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Dec
err = q.BalanceActivity.Create(&m.BalanceActivity{
UserID: user.ID,
AdminID: adminId,
BillID: billId,
Amount: amount.StringFixed(2),
BalancePrev: user.Balance.StringFixed(2),
BalanceCurr: balance.StringFixed(2),
@@ -92,8 +93,7 @@ func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
data,
m.TradeTypeRecharge,
fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)),
amount, amount,
nil, nil,
amount, amount, amount,
nil, nil,
}, nil
}
@@ -118,7 +118,6 @@ func (s *userService) CreateByAdmin(data CreateUserByAdminData) error {
Email: data.Email,
Password: hashedPwd,
Source: &source,
Name: data.Name,
Avatar: data.Avatar,
Status: u.Else(data.Status, m.UserStatusEnabled),
ContactQQ: data.ContactQQ,
@@ -141,7 +140,6 @@ type CreateUserByAdminData struct {
Username *string `json:"username"`
Email *string `json:"email"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Status *m.UserStatus `json:"status"`
ContactQQ *string `json:"contact_qq"`

View File

@@ -55,7 +55,7 @@ func (s *verifierService) SendSms(ctx context.Context, phone string, purpose Ver
code := rand.Intn(900000) + 100000 // 6-digit code between 100000-999999
// 发送短信验证码
if env.DebugExternalChange {
if env.RunMode == env.RunModeProd {
params, err := json.Marshal(map[string]string{
"code": strconv.Itoa(code),
})
@@ -89,8 +89,8 @@ func (s *verifierService) SendSms(ctx context.Context, phone string, purpose Ver
return nil
}
func (s *verifierService) VerifySms(ctx context.Context, phone, code string) error {
key := smsKey(phone, VerifierSmsPurposeLogin)
func (s *verifierService) VerifySms(ctx context.Context, phone, code string, purpose VerifierSmsPurpose) error {
key := smsKey(phone, purpose)
keyLock := key + ":lock"
err := g.Redis.Watch(ctx, func(tx *redis.Tx) error {
@@ -146,7 +146,8 @@ func smsKey(phone string, purpose VerifierSmsPurpose) string {
type VerifierSmsPurpose int
const (
VerifierSmsPurposeLogin VerifierSmsPurpose = iota // 登录
VerifierSmsPurposeLogin VerifierSmsPurpose = iota // 登录
VerifierSmsPurposePassword // 修改密码
)
// region 服务异常