新增指定用户查询接口 & 接口权限细分

This commit is contained in:
2026-04-08 13:38:00 +08:00
parent fccb83c0e5
commit 3fb3a8026f
15 changed files with 780 additions and 69 deletions

View File

@@ -9,13 +9,145 @@ 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 权限
-- ====================
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);
-- --------------------------
-- 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);
-- 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);
-- --------------------------
-- 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:read 子权限
insert into permission (parent_id, name, description, sort) values
((select id from permission where name = 'resource:read' and deleted_at is null), 'resource:read:short:of-user', '读取指定用户的短效套餐列表', 1),
((select id from permission where name = 'resource:read' and deleted_at is null), 'resource:read:long:of-user', '读取指定用户的长效套餐列表', 2);
-- 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);
-- --------------------------
-- 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);
-- 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 '删除时间';
@@ -775,7 +777,6 @@ comment on column product_sku.discount_id is '折扣ID';
comment on column product_sku.code is 'SKU 代码:格式为 key=value,key=value,...其中key:value 是 SKU 的属性,多个属性用逗号分隔';
comment on column product_sku.name is 'SKU 可读名称';
comment on column product_sku.price_min is '最低价格';
comment on column product_sku.min is '最小购买量';
comment on column product_sku.status is 'SKU状态0-禁用1-正常';
comment on column product_sku.created_at is '创建时间';
comment on column product_sku.updated_at is '更新时间';

View File

@@ -26,34 +26,43 @@ const (
ScopeDiscountRead = string("discount:read") // 读取折扣列表
ScopeDiscountWrite = string("discount:write") // 写入折扣
ScopeResource = string("resource") // 用户套餐
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
ScopeResourceWrite = string("resource:write") // 写入用户套餐
ScopeResource = string("resource") // 用户套餐
ScopeResourceRead = string("resource:read") // 读取用户套餐列表
ScopeResourceReadShortOfUser = string("resource:read:short:of-user") // 读取指定用户的短效套餐列表
ScopeResourceReadLongOfUser = string("resource:read:long:of-user") // 读取指定用户的长效套餐列表
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") // 用户认领
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") // 写入交易
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") // 写入账单
)

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

@@ -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.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,80 @@ 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))
}
// 查询账单列表
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"`
}
// 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

@@ -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.ScopeResourceReadShortOfUser)
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.ScopeResourceReadLongOfUser)
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 {

View File

@@ -97,6 +97,82 @@ 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 {
// 检查权限

View File

@@ -405,3 +405,118 @@ type UpdatePasswordReq struct {
Code string `json:"code"`
Password string `json:"password"`
}
// PageUserNotBindByAdmin 分页获取未绑定管理员的用户
func PageUserNotBindByAdmin(c *fiber.Ctx) error {
// 检查权限
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadNotBind)
if err != nil {
return err
}
// 解析请求参数
req := new(PageUserNotBindByAdminReq)
if err := g.Validator.ParseBody(c, req); err != nil {
return err
}
// 构建查询条件(强制过滤未绑定管理员的用户)
do := q.User.Where(q.User.AdminID.IsNull())
if req.Phone != nil {
do = do.Where(q.User.Phone.Eq(*req.Phone))
}
// 查询用户列表
users, total, err := q.User.
Omit(q.User.Password, q.User.IDNo).
Where(do).
Order(q.User.CreatedAt.Desc()).
FindByPage(req.GetOffset(), req.GetLimit())
if err != nil {
return err
}
// 返回结果
return c.JSON(core.PageResp{
Total: int(total),
Page: req.GetPage(),
Size: req.GetSize(),
List: users,
})
}
type PageUserNotBindByAdminReq struct {
core.PageReq
Phone *string `json:"phone,omitempty"`
}
// UpdateUserBalanceIncByAdmin 管理员增加用户余额
func UpdateUserBalanceIncByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Add(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
// UpdateUserBalanceDecByAdmin 管理员减少用户余额
func UpdateUserBalanceDecByAdmin(c *fiber.Ctx) error {
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
if err != nil {
return err
}
var req UpdateUserBalanceChangeByAdminData
if err := g.Validator.ParseBody(c, &req); err != nil {
return err
}
amount, err := decimal.NewFromString(req.Amount)
if err != nil {
return err
}
if !amount.IsPositive() {
return core.NewBizErr("金额必须为正数")
}
user, err := s.User.Get(q.Q, req.UserID)
if err != nil {
return err
}
newBalance := user.Balance.Sub(amount)
if err := s.User.UpdateBalanceByAdmin(user, newBalance, &authCtx.Admin.ID); err != nil {
return err
}
return c.JSON(nil)
}
type UpdateUserBalanceChangeByAdminData struct {
UserID int32 `json:"user_id" validate:"required"`
Amount string `json:"amount" validate:"required"`
}

View File

@@ -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

@@ -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

@@ -159,35 +159,44 @@ 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)
// bill 账单
var bill = api.Group("/bill")
bill.Post("/page", handlers.PageBillByAdmin)
bill.Post("/page/of-user", handlers.PageBillOfUserByAdmin)
// product 产品
var product = api.Group("/product")

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

@@ -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"`