恢复余额功能 & 管理员修改余额功能
This commit is contained in:
47
README.md
47
README.md
@@ -2,54 +2,19 @@
|
||||
|
||||
用户请求需要检查数据权限
|
||||
|
||||
管理页面查询统一加排序
|
||||
|
||||
后端默认用户名不能是完整手机号
|
||||
|
||||
前端需要 token 化改造,以避免每次 basic 认证流程中 bcrypt 对比导致的性能对比
|
||||
|
||||
优化中间件,配置通用限速
|
||||
|
||||
observe 部署,蓝狐部署
|
||||
|
||||
---
|
||||
|
||||
用反射实现环境变量解析,以简化函数签名
|
||||
|
||||
分离 task 的客户端,支持多进程(prefork 必要!)
|
||||
|
||||
调整目录结构:
|
||||
|
||||
```
|
||||
- /util 工具函数
|
||||
|
||||
- /models 模型
|
||||
- /queries 数据库层
|
||||
- /clients 三方依赖的客户端实例
|
||||
|
||||
- /services 服务层
|
||||
- /auth 认证相关,特化服务
|
||||
|
||||
- /app 应用相关,初始化日志,环境变量,错误类型等
|
||||
- /http 协议层,http 服务
|
||||
- /cmd 主函数
|
||||
|
||||
逐层向上依赖
|
||||
cmd 调用 app, http 的初始化函数
|
||||
http 调用 clients 的初始化函数
|
||||
```
|
||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
||||
|
||||
---
|
||||
|
||||
数据库转模型文件
|
||||
|
||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
||||
|
||||
慢速请求底层调用埋点监控
|
||||
|
||||
- redis
|
||||
- gorm
|
||||
- 三方接口
|
||||
数据库转模型文件
|
||||
|
||||
冷数据迁移方案
|
||||
|
||||
@@ -67,6 +32,14 @@ jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
||||
3. 创建 model 文件,并将其添加到 gen 代码中
|
||||
4. 生成查询文件
|
||||
|
||||
### 权限管理
|
||||
|
||||
在 `web/core/scopes.go` 下定义了系统所有静态权限
|
||||
|
||||
新增系统权限需要在数据库中配套添加权限
|
||||
|
||||
前端也需要新增配套权限定义
|
||||
|
||||
## 业务逻辑
|
||||
|
||||
### 订单关闭的几种方式
|
||||
|
||||
@@ -65,6 +65,7 @@ func main() {
|
||||
m.Whitelist{},
|
||||
m.Inquiry{},
|
||||
m.ProductDiscount{},
|
||||
m.BalanceActivity{},
|
||||
)
|
||||
g.Execute()
|
||||
}
|
||||
|
||||
@@ -17,14 +17,6 @@ func Else[T any](v *T, or T) T {
|
||||
}
|
||||
}
|
||||
|
||||
func ElseTo[A any, B any](a *A, f func(A) B) *B {
|
||||
if a == nil {
|
||||
return nil
|
||||
} else {
|
||||
return P(f(*a))
|
||||
}
|
||||
}
|
||||
|
||||
// 三元表达式
|
||||
func Ternary[T any](condition bool, trueValue T, falseValue T) T {
|
||||
if condition {
|
||||
|
||||
@@ -200,7 +200,7 @@ create table admin (
|
||||
updated_at timestamptz default current_timestamp,
|
||||
deleted_at timestamptz
|
||||
);
|
||||
create unique index udx_admin_username on admin (username);
|
||||
create unique index udx_admin_username on admin (username) where deleted_at is null;
|
||||
create index idx_admin_status on admin (status) where deleted_at is null;
|
||||
create index idx_admin_created_at on admin (created_at) where deleted_at is null;
|
||||
|
||||
@@ -1018,6 +1018,36 @@ comment on column bill.created_at is '创建时间';
|
||||
comment on column bill.updated_at is '更新时间';
|
||||
comment on column bill.deleted_at is '删除时间';
|
||||
|
||||
-- balance_activity 余额变动记录
|
||||
drop table if exists balance_activity cascade;
|
||||
create table balance_activity (
|
||||
id int generated by default as identity primary key,
|
||||
user_id int not null,
|
||||
bill_id int,
|
||||
admin_id int,
|
||||
amount text not null,
|
||||
balance_prev text not null,
|
||||
balance_curr text not null,
|
||||
remark text,
|
||||
created_at timestamptz default current_timestamp
|
||||
);
|
||||
create index idx_balance_activity_user_id on balance_activity (user_id);
|
||||
create index idx_balance_activity_bill_id on balance_activity (bill_id);
|
||||
create index idx_balance_activity_admin_id on balance_activity (admin_id);
|
||||
create index idx_balance_activity_created_at on balance_activity (created_at);
|
||||
|
||||
-- balance_activity表字段注释
|
||||
comment on table balance_activity is '余额变动记录表';
|
||||
comment on column balance_activity.id is '记录ID';
|
||||
comment on column balance_activity.user_id is '用户ID';
|
||||
comment on column balance_activity.bill_id is '账单ID';
|
||||
comment on column balance_activity.admin_id is '管理员ID';
|
||||
comment on column balance_activity.amount is '变动金额';
|
||||
comment on column balance_activity.balance_prev is '变动前余额';
|
||||
comment on column balance_activity.balance_curr is '变动后余额';
|
||||
comment on column balance_activity.remark is '备注';
|
||||
comment on column balance_activity.created_at is '创建时间';
|
||||
|
||||
-- coupon 优惠券
|
||||
drop table if exists coupon cascade;
|
||||
create table coupon (
|
||||
@@ -1175,4 +1205,10 @@ alter table product_sku_user
|
||||
alter table product_sku_user
|
||||
add constraint fk_product_sku_user_discount_id foreign key (discount_id) references product_discount (id) on delete restrict;
|
||||
|
||||
--balance_activity表外键
|
||||
alter table balance_activity
|
||||
add constraint fk_balance_activity_user_id foreign key (user_id) references "user" (id) on delete cascade;
|
||||
alter table balance_activity
|
||||
add constraint fk_balance_activity_bill_id foreign key (bill_id) references bill (id) on delete set null;
|
||||
|
||||
-- endregion
|
||||
|
||||
@@ -155,22 +155,8 @@ func authAdminByPassword(tx *q.Query, username, password string) (*m.Admin, erro
|
||||
}
|
||||
|
||||
func adminScopes(admin *m.Admin) ([]string, error) {
|
||||
count, err := q.Admin.
|
||||
LeftJoin(q.LinkAdminRole, q.LinkAdminRole.AdminID.EqCol(q.Admin.ID)).
|
||||
LeftJoin(q.LinkAdminRolePermission, q.LinkAdminRolePermission.RoleID.EqCol(q.LinkAdminRole.RoleID)).
|
||||
LeftJoin(q.Permission, q.Permission.ID.EqCol(q.LinkAdminRolePermission.PermissionID)).
|
||||
Where(q.Admin.ID.Eq(admin.ID)).
|
||||
Select(q.Permission.Name).
|
||||
Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scopes := make([]string, 0, count)
|
||||
err = q.Admin.
|
||||
var scopes []struct{ Name string }
|
||||
err := q.Admin.
|
||||
LeftJoin(q.LinkAdminRole, q.LinkAdminRole.AdminID.EqCol(q.Admin.ID)).
|
||||
LeftJoin(q.LinkAdminRolePermission, q.LinkAdminRolePermission.RoleID.EqCol(q.LinkAdminRole.RoleID)).
|
||||
LeftJoin(q.Permission, q.Permission.ID.EqCol(q.LinkAdminRolePermission.PermissionID)).
|
||||
@@ -181,5 +167,9 @@ func adminScopes(admin *m.Admin) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return scopes, nil
|
||||
scopeNames := make([]string, 0, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
scopeNames = append(scopeNames, scope.Name)
|
||||
}
|
||||
return scopeNames, nil
|
||||
}
|
||||
|
||||
@@ -337,7 +337,6 @@ func authPassword(c *fiber.Ctx, auth *AuthCtx, req *TokenReq, now time.Time) (*m
|
||||
// 手机号首次登录的自动创建用户
|
||||
user = &m.User{
|
||||
Phone: req.Username,
|
||||
Username: u.P(req.Username),
|
||||
Status: m.UserStatusEnabled,
|
||||
}
|
||||
}
|
||||
@@ -549,22 +548,30 @@ func introspectUser(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
||||
func introspectAdmin(ctx *fiber.Ctx, authCtx *AuthCtx) error {
|
||||
// 获取管理员信息
|
||||
profile, err := q.Admin.
|
||||
Preload(q.Admin.Roles, q.Admin.Roles.Permissions).
|
||||
Where(q.Admin.ID.Eq(authCtx.Admin.ID)).
|
||||
Omit(q.Admin.DeletedAt).
|
||||
Omit(q.Admin.DeletedAt, q.Admin.Password).
|
||||
Take()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 不返回密码
|
||||
profile.Password = ""
|
||||
|
||||
// 掩码敏感信息
|
||||
if profile.Phone != nil && *profile.Phone != "" {
|
||||
profile.Phone = u.P(maskPhone(*profile.Phone))
|
||||
// 整理权限列表
|
||||
scopes := make(map[string]struct{}, 0)
|
||||
for _, role := range profile.Roles {
|
||||
for _, permission := range role.Permissions {
|
||||
scopes[permission.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
list := make([]string, 0, len(scopes))
|
||||
for scope := range scopes {
|
||||
list = append(list, scope)
|
||||
}
|
||||
|
||||
return ctx.JSON(profile)
|
||||
return ctx.JSON(struct {
|
||||
*m.Admin
|
||||
Scopes []string `json:"scopes"`
|
||||
}{profile, list})
|
||||
}
|
||||
|
||||
func maskPhone(phone string) string {
|
||||
|
||||
@@ -31,7 +31,10 @@ const (
|
||||
|
||||
ScopeUser = string("user") // 用户
|
||||
ScopeUserRead = string("user:read") // 读取用户列表
|
||||
ScopeUserReadOne = string("user:read:one") // 读取单个用户
|
||||
ScopeUserWrite = string("user:write") // 写入用户
|
||||
ScopeUserWriteBalance = string("user:write:balance") // 写入用户余额
|
||||
ScopeUserWriteBind = string("user:write:bind") // 用户认领
|
||||
|
||||
ScopeCoupon = string("coupon") // 优惠券
|
||||
ScopeCouponRead = string("coupon:read") // 读取优惠券列表
|
||||
|
||||
11
web/error.go
11
web/error.go
@@ -22,8 +22,9 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
|
||||
var authErr auth.AuthErr
|
||||
var bizErr *core.BizErr
|
||||
var servErr *core.ServErr
|
||||
var jsonErr *json.UnmarshalTypeError
|
||||
var timeErr *time.ParseError
|
||||
var jsonErr *json.UnmarshalTypeError
|
||||
var jsonSyntaxErr *json.SyntaxError
|
||||
|
||||
switch {
|
||||
|
||||
@@ -53,13 +54,17 @@ func ErrorHandler(c *fiber.Ctx, err error) error {
|
||||
code = fiber.StatusInternalServerError
|
||||
message = err.Error()
|
||||
|
||||
case errors.As(err, &timeErr):
|
||||
code = fiber.StatusBadRequest
|
||||
message = fmt.Sprintf("时间格式不正确,传入值为 %s,检查传参是否为时间类型", timeErr.Value)
|
||||
|
||||
case errors.As(err, &jsonErr):
|
||||
code = fiber.StatusBadRequest
|
||||
message = fmt.Sprintf("参数 %s 类型不正确,传入类型为 %s,正确类型应该为 %s", jsonErr.Field, jsonErr.Value, jsonErr.Type.Name())
|
||||
|
||||
case errors.As(err, &timeErr):
|
||||
case errors.As(err, &jsonSyntaxErr):
|
||||
code = fiber.StatusBadRequest
|
||||
message = fmt.Sprintf("时间格式不正确,传入值为 %s,检查传参是否为时间类型", timeErr.Value)
|
||||
message = "参数格式不正确,检查传参是否为 JSON 格式"
|
||||
|
||||
// 所有未手动声明的错误类型
|
||||
default:
|
||||
|
||||
@@ -106,6 +106,7 @@ func PageBatchByAdmin(c *fiber.Ctx) error {
|
||||
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
||||
).
|
||||
Where(do).
|
||||
Order(q.LogsUserUsage.Time.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
|
||||
return c.JSON(core.PageResp{
|
||||
|
||||
@@ -57,7 +57,7 @@ func PageBillByAdmin(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 查询用户列表
|
||||
list, total, err := q.Bill.Debug().
|
||||
list, total, err := q.Bill.
|
||||
Joins(
|
||||
q.Bill.User,
|
||||
q.Bill.Resource,
|
||||
@@ -70,6 +70,7 @@ func PageBillByAdmin(c *fiber.Ctx) error {
|
||||
q.User.As("User").Phone.As("User__phone"),
|
||||
q.User.As("User").Name.As("User__name"),
|
||||
q.Trade.As("Trade").InnerNo.As("Trade__inner_no"),
|
||||
q.Trade.As("Trade").Acquirer.As("Trade__acquirer"),
|
||||
q.Resource.As("Resource").ResourceNo.As("Resource__resource_no"),
|
||||
).
|
||||
Where(do).
|
||||
|
||||
@@ -72,6 +72,7 @@ func PageChannelByAdmin(c *fiber.Ctx) error {
|
||||
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
|
||||
@@ -190,6 +191,10 @@ func CreateChannel(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 创建通道
|
||||
var isp *m.EdgeISP
|
||||
if req.Isp != nil {
|
||||
isp = u.X(m.ToEdgeISP(*req.Isp))
|
||||
}
|
||||
result, err := s.Channel.CreateChannels(
|
||||
ip,
|
||||
req.ResourceId,
|
||||
@@ -197,7 +202,7 @@ func CreateChannel(c *fiber.Ctx) error {
|
||||
req.AuthType == s.ChannelAuthTypePass,
|
||||
req.Count,
|
||||
s.EdgeFilter{
|
||||
Isp: u.ElseTo(req.Isp, m.ToEdgeISP),
|
||||
Isp: isp,
|
||||
Prov: req.Prov,
|
||||
City: req.City,
|
||||
},
|
||||
|
||||
@@ -260,7 +260,7 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
list, total, err := q.Resource.Debug().
|
||||
list, total, err := q.Resource.
|
||||
Joins(q.Resource.User, q.Resource.Short, q.Resource.Short.Sku).
|
||||
Select(
|
||||
q.Resource.ALL,
|
||||
@@ -352,7 +352,7 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
list, total, err := q.Resource.Debug().
|
||||
list, total, err := q.Resource.
|
||||
Joins(q.Resource.User, q.Resource.Long, q.Resource.Long.Sku).
|
||||
Select(
|
||||
q.Resource.ALL,
|
||||
|
||||
@@ -70,6 +70,7 @@ func PageTradeByAdmin(c *fiber.Ctx) error {
|
||||
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
|
||||
@@ -121,7 +122,13 @@ func TradeCreate(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 处理订单
|
||||
result, err := s.Trade.Create(authCtx.User, req.CreateTradeData, req.Resource)
|
||||
var result *s.CreateTradeResult
|
||||
switch req.Type {
|
||||
case m.TradeTypePurchase:
|
||||
result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Resource)
|
||||
case m.TradeTypeRecharge:
|
||||
result, err = s.Trade.Create(authCtx.User, req.CreateTradeData, req.Recharge)
|
||||
}
|
||||
if err != nil {
|
||||
return core.NewServErr("处理购买产品信息失败", err)
|
||||
}
|
||||
@@ -193,11 +200,7 @@ type TradeCancelReq struct {
|
||||
|
||||
// 检查订单
|
||||
func TradeCheck(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitUser()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 检查权限:sse 接口暂时不检查权限
|
||||
|
||||
// 解析请求参数
|
||||
req := new(TradeCheckReq)
|
||||
|
||||
@@ -9,9 +9,157 @@ import (
|
||||
s "platform/web/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/shopspring/decimal"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 分页获取用户
|
||||
func PageUserByAdmin(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(PageUserByAdminReq)
|
||||
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
do := q.User.Where()
|
||||
if req.Account != nil {
|
||||
do = do.Where(q.User.Where(
|
||||
q.User.Username.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Phone.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Email.Like("%" + *req.Account + "%"),
|
||||
))
|
||||
}
|
||||
if req.Name != nil {
|
||||
do = do.Where(q.User.Name.Eq(*req.Name))
|
||||
}
|
||||
if req.Identified != nil {
|
||||
if *req.Identified {
|
||||
do = do.Where(q.User.IDType.Gt(0))
|
||||
} else {
|
||||
do = do.Where(q.User.IDType.Eq(0))
|
||||
}
|
||||
}
|
||||
if req.Enabled != nil {
|
||||
if *req.Enabled {
|
||||
do = do.Where(q.User.Status.Eq(1))
|
||||
} else {
|
||||
do = do.Where(q.User.Status.Eq(0))
|
||||
}
|
||||
}
|
||||
if req.Assigned != nil {
|
||||
if *req.Assigned {
|
||||
do = do.Where(q.User.AdminID.IsNotNull())
|
||||
} else {
|
||||
do = do.Where(q.User.AdminID.IsNull())
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户列表
|
||||
users, total, err := q.User.
|
||||
Preload(q.User.Admin, q.User.Discount).
|
||||
Omit(q.User.Password).
|
||||
Where(do).
|
||||
Order(q.User.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Admin != nil {
|
||||
user.Admin = &m.Admin{
|
||||
Name: user.Admin.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(core.PageResp{
|
||||
Total: int(total),
|
||||
Page: req.GetPage(),
|
||||
Size: req.GetSize(),
|
||||
List: users,
|
||||
})
|
||||
}
|
||||
|
||||
type PageUserByAdminReq struct {
|
||||
core.PageReq
|
||||
Account *string `json:"account,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Identified *bool `json:"identified,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Assigned *bool `json:"assigned,omitempty"`
|
||||
}
|
||||
|
||||
// 管理员获取单个用户
|
||||
func GetUserByAdmin(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserReadOne)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
var req GetUserByAdminReq
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
do := q.User.Where()
|
||||
if req.Account != nil {
|
||||
do = do.Where(q.User.Where(
|
||||
q.User.Username.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Phone.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Email.Like("%" + *req.Account + "%"),
|
||||
))
|
||||
}
|
||||
if req.Name != nil {
|
||||
do = do.Where(q.User.Name.Eq(*req.Name))
|
||||
}
|
||||
|
||||
// 查询用户
|
||||
user, err := q.User.
|
||||
Preload(q.User.Admin, q.User.Discount).
|
||||
Omit(q.User.Password).
|
||||
Where(do).
|
||||
Order(q.User.CreatedAt.Desc()).
|
||||
First()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return core.NewBizErr("找不到用户")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 仅保留管理员名称
|
||||
if user.Admin != nil {
|
||||
user.Admin = &m.Admin{
|
||||
Name: user.Admin.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
type GetUserByAdminReq struct {
|
||||
Account *string `json:"account,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// 管理员创建用户
|
||||
func CreateUserByAdmin(c *fiber.Ctx) error {
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite)
|
||||
@@ -69,91 +217,36 @@ func RemoveUserByAdmin(c *fiber.Ctx) error {
|
||||
return c.JSON(nil)
|
||||
}
|
||||
|
||||
// 分页获取用户
|
||||
func PageUserByAdmin(c *fiber.Ctx) error {
|
||||
// 检查权限
|
||||
_, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserRead)
|
||||
// 管理员更新用户余额
|
||||
func UpdateUserBalanceByAdmin(c *fiber.Ctx) error {
|
||||
authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBalance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析请求参数
|
||||
req := new(PageUserByAdminReq)
|
||||
if err := g.Validator.ParseBody(c, req); err != nil {
|
||||
var req UpdateUserBalanceByAdminData
|
||||
if err := g.Validator.ParseBody(c, &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
do := q.User.Where()
|
||||
if req.Account != nil {
|
||||
do = do.Where(q.User.Where(
|
||||
q.User.Username.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Phone.Like("%" + *req.Account + "%"),
|
||||
).Or(
|
||||
q.User.Email.Like("%" + *req.Account + "%"),
|
||||
))
|
||||
}
|
||||
if req.Name != nil {
|
||||
do = do.Where(q.User.Name.Eq(*req.Name))
|
||||
}
|
||||
if req.Identified != nil {
|
||||
if *req.Identified {
|
||||
do = do.Where(q.User.IDType.Gt(0))
|
||||
} else {
|
||||
do = do.Where(q.User.IDType.Eq(0))
|
||||
}
|
||||
}
|
||||
if req.Enabled != nil {
|
||||
if *req.Enabled {
|
||||
do = do.Where(q.User.Status.Eq(1))
|
||||
} else {
|
||||
do = do.Where(q.User.Status.Eq(0))
|
||||
}
|
||||
}
|
||||
if req.Assigned != nil {
|
||||
if *req.Assigned {
|
||||
do = do.Where(q.User.AdminID.IsNotNull())
|
||||
} else {
|
||||
do = do.Where(q.User.AdminID.IsNull())
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户列表
|
||||
users, total, err := q.User.Debug().
|
||||
Preload(q.User.Admin, q.User.Discount).
|
||||
Omit(q.User.Password).
|
||||
Where(do).
|
||||
Order(q.User.CreatedAt).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
user, err := s.User.Get(q.Q, req.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if user.Admin != nil {
|
||||
user.Admin = &m.Admin{
|
||||
Name: user.Admin.Name,
|
||||
}
|
||||
balance, err := decimal.NewFromString(req.Balance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.User.UpdateBalanceByAdmin(user, balance, &authCtx.Admin.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
return c.JSON(core.PageResp{
|
||||
Total: int(total),
|
||||
Page: req.GetPage(),
|
||||
Size: req.GetSize(),
|
||||
List: users,
|
||||
})
|
||||
return c.JSON(nil)
|
||||
}
|
||||
|
||||
type PageUserByAdminReq struct {
|
||||
core.PageReq
|
||||
Account *string `json:"account,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Identified *bool `json:"identified,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Assigned *bool `json:"assigned,omitempty"`
|
||||
type UpdateUserBalanceByAdminData struct {
|
||||
UserID int32 `json:"user_id" validate:"required"`
|
||||
Balance string `json:"balance" validate:"required"`
|
||||
}
|
||||
|
||||
// 绑定管理员
|
||||
|
||||
22
web/models/balance_activity.go
Normal file
22
web/models/balance_activity.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// BalanceActivity 余额变动记录表
|
||||
type BalanceActivity struct {
|
||||
ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID
|
||||
UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID
|
||||
BillID *int32 `json:"bill_id,omitempty" gorm:"column:bill_id"` // 账单ID
|
||||
AdminID *int32 `json:"admin_id,omitempty" gorm:"column:admin_id"` // 管理员ID
|
||||
Amount string `json:"amount" gorm:"column:amount"` // 变动金额
|
||||
BalancePrev string `json:"balance_prev" gorm:"column:balance_prev"` // 变动前余额
|
||||
BalanceCurr string `json:"balance_curr" gorm:"column:balance_curr"` // 变动后余额
|
||||
Remark *string `json:"remark,omitempty" gorm:"column:remark"` // 备注
|
||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间
|
||||
|
||||
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
||||
Bill *Bill `json:"bill,omitempty" gorm:"foreignKey:BillID"`
|
||||
Admin *User `json:"admin,omitempty" gorm:"foreignKey:AdminID"`
|
||||
}
|
||||
871
web/queries/balance_activity.gen.go
Normal file
871
web/queries/balance_activity.gen.go
Normal file
@@ -0,0 +1,871 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"platform/web/models"
|
||||
)
|
||||
|
||||
func newBalanceActivity(db *gorm.DB, opts ...gen.DOOption) balanceActivity {
|
||||
_balanceActivity := balanceActivity{}
|
||||
|
||||
_balanceActivity.balanceActivityDo.UseDB(db, opts...)
|
||||
_balanceActivity.balanceActivityDo.UseModel(&models.BalanceActivity{})
|
||||
|
||||
tableName := _balanceActivity.balanceActivityDo.TableName()
|
||||
_balanceActivity.ALL = field.NewAsterisk(tableName)
|
||||
_balanceActivity.ID = field.NewInt32(tableName, "id")
|
||||
_balanceActivity.UserID = field.NewInt32(tableName, "user_id")
|
||||
_balanceActivity.BillID = field.NewInt32(tableName, "bill_id")
|
||||
_balanceActivity.AdminID = field.NewInt32(tableName, "admin_id")
|
||||
_balanceActivity.Amount = field.NewString(tableName, "amount")
|
||||
_balanceActivity.BalancePrev = field.NewString(tableName, "balance_prev")
|
||||
_balanceActivity.BalanceCurr = field.NewString(tableName, "balance_curr")
|
||||
_balanceActivity.Remark = field.NewString(tableName, "remark")
|
||||
_balanceActivity.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_balanceActivity.Admin = balanceActivityHasOneAdmin{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("Admin", "models.User"),
|
||||
Admin: struct {
|
||||
field.RelationField
|
||||
Roles struct {
|
||||
field.RelationField
|
||||
Permissions struct {
|
||||
field.RelationField
|
||||
Parent struct {
|
||||
field.RelationField
|
||||
}
|
||||
Children struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Admin", "models.Admin"),
|
||||
Roles: struct {
|
||||
field.RelationField
|
||||
Permissions struct {
|
||||
field.RelationField
|
||||
Parent struct {
|
||||
field.RelationField
|
||||
}
|
||||
Children struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Admin.Roles", "models.AdminRole"),
|
||||
Permissions: struct {
|
||||
field.RelationField
|
||||
Parent struct {
|
||||
field.RelationField
|
||||
}
|
||||
Children struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions", "models.Permission"),
|
||||
Parent: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Parent", "models.Permission"),
|
||||
},
|
||||
Children: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Admin.Roles.Permissions.Children", "models.Permission"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Discount: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Discount", "models.ProductDiscount"),
|
||||
},
|
||||
Roles: struct {
|
||||
field.RelationField
|
||||
Permissions struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Roles", "models.UserRole"),
|
||||
Permissions: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Admin.Roles.Permissions", "models.Permission"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_balanceActivity.User = balanceActivityBelongsToUser{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("User", "models.User"),
|
||||
}
|
||||
|
||||
_balanceActivity.Bill = balanceActivityBelongsToBill{
|
||||
db: db.Session(&gorm.Session{}),
|
||||
|
||||
RelationField: field.NewRelation("Bill", "models.Bill"),
|
||||
User: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.User", "models.User"),
|
||||
},
|
||||
Trade: struct {
|
||||
field.RelationField
|
||||
User struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Trade", "models.Trade"),
|
||||
User: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Trade.User", "models.User"),
|
||||
},
|
||||
},
|
||||
Resource: struct {
|
||||
field.RelationField
|
||||
User struct {
|
||||
field.RelationField
|
||||
}
|
||||
Short struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
Discount struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}
|
||||
Long struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource", "models.Resource"),
|
||||
User: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.User", "models.User"),
|
||||
},
|
||||
Short: struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
Discount struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Short", "models.ResourceShort"),
|
||||
Sku: struct {
|
||||
field.RelationField
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
Discount struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Short.Sku", "models.ProductSku"),
|
||||
Product: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Product", "models.Product"),
|
||||
},
|
||||
Discount: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Short.Sku.Discount", "models.ProductDiscount"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Long: struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
}
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Long", "models.ResourceLong"),
|
||||
Sku: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Long.Sku", "models.ProductSku"),
|
||||
},
|
||||
},
|
||||
Product: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Resource.Product", "models.Product"),
|
||||
},
|
||||
},
|
||||
Refund: struct {
|
||||
field.RelationField
|
||||
}{
|
||||
RelationField: field.NewRelation("Bill.Refund", "models.Refund"),
|
||||
},
|
||||
}
|
||||
|
||||
_balanceActivity.fillFieldMap()
|
||||
|
||||
return _balanceActivity
|
||||
}
|
||||
|
||||
type balanceActivity struct {
|
||||
balanceActivityDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int32
|
||||
UserID field.Int32
|
||||
BillID field.Int32
|
||||
AdminID field.Int32
|
||||
Amount field.String
|
||||
BalancePrev field.String
|
||||
BalanceCurr field.String
|
||||
Remark field.String
|
||||
CreatedAt field.Time
|
||||
Admin balanceActivityHasOneAdmin
|
||||
|
||||
User balanceActivityBelongsToUser
|
||||
|
||||
Bill balanceActivityBelongsToBill
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (b balanceActivity) Table(newTableName string) *balanceActivity {
|
||||
b.balanceActivityDo.UseTable(newTableName)
|
||||
return b.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (b balanceActivity) As(alias string) *balanceActivity {
|
||||
b.balanceActivityDo.DO = *(b.balanceActivityDo.As(alias).(*gen.DO))
|
||||
return b.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (b *balanceActivity) updateTableName(table string) *balanceActivity {
|
||||
b.ALL = field.NewAsterisk(table)
|
||||
b.ID = field.NewInt32(table, "id")
|
||||
b.UserID = field.NewInt32(table, "user_id")
|
||||
b.BillID = field.NewInt32(table, "bill_id")
|
||||
b.AdminID = field.NewInt32(table, "admin_id")
|
||||
b.Amount = field.NewString(table, "amount")
|
||||
b.BalancePrev = field.NewString(table, "balance_prev")
|
||||
b.BalanceCurr = field.NewString(table, "balance_curr")
|
||||
b.Remark = field.NewString(table, "remark")
|
||||
b.CreatedAt = field.NewTime(table, "created_at")
|
||||
|
||||
b.fillFieldMap()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *balanceActivity) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := b.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (b *balanceActivity) fillFieldMap() {
|
||||
b.fieldMap = make(map[string]field.Expr, 12)
|
||||
b.fieldMap["id"] = b.ID
|
||||
b.fieldMap["user_id"] = b.UserID
|
||||
b.fieldMap["bill_id"] = b.BillID
|
||||
b.fieldMap["admin_id"] = b.AdminID
|
||||
b.fieldMap["amount"] = b.Amount
|
||||
b.fieldMap["balance_prev"] = b.BalancePrev
|
||||
b.fieldMap["balance_curr"] = b.BalanceCurr
|
||||
b.fieldMap["remark"] = b.Remark
|
||||
b.fieldMap["created_at"] = b.CreatedAt
|
||||
|
||||
}
|
||||
|
||||
func (b balanceActivity) clone(db *gorm.DB) balanceActivity {
|
||||
b.balanceActivityDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
b.Admin.db = db.Session(&gorm.Session{Initialized: true})
|
||||
b.Admin.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
b.User.db = db.Session(&gorm.Session{Initialized: true})
|
||||
b.User.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
b.Bill.db = db.Session(&gorm.Session{Initialized: true})
|
||||
b.Bill.db.Statement.ConnPool = db.Statement.ConnPool
|
||||
return b
|
||||
}
|
||||
|
||||
func (b balanceActivity) replaceDB(db *gorm.DB) balanceActivity {
|
||||
b.balanceActivityDo.ReplaceDB(db)
|
||||
b.Admin.db = db.Session(&gorm.Session{})
|
||||
b.User.db = db.Session(&gorm.Session{})
|
||||
b.Bill.db = db.Session(&gorm.Session{})
|
||||
return b
|
||||
}
|
||||
|
||||
type balanceActivityHasOneAdmin struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
|
||||
Admin struct {
|
||||
field.RelationField
|
||||
Roles struct {
|
||||
field.RelationField
|
||||
Permissions struct {
|
||||
field.RelationField
|
||||
Parent struct {
|
||||
field.RelationField
|
||||
}
|
||||
Children struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Discount struct {
|
||||
field.RelationField
|
||||
}
|
||||
Roles struct {
|
||||
field.RelationField
|
||||
Permissions struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdmin) Where(conds ...field.Expr) *balanceActivityHasOneAdmin {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdmin) WithContext(ctx context.Context) *balanceActivityHasOneAdmin {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdmin) Session(session *gorm.Session) *balanceActivityHasOneAdmin {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdmin) Model(m *models.BalanceActivity) *balanceActivityHasOneAdminTx {
|
||||
return &balanceActivityHasOneAdminTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdmin) Unscoped() *balanceActivityHasOneAdmin {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityHasOneAdminTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Find() (result *models.User, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Append(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Replace(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Delete(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a balanceActivityHasOneAdminTx) Unscoped() *balanceActivityHasOneAdminTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityBelongsToUser struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUser) Where(conds ...field.Expr) *balanceActivityBelongsToUser {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUser) WithContext(ctx context.Context) *balanceActivityBelongsToUser {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUser) Session(session *gorm.Session) *balanceActivityBelongsToUser {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUser) Model(m *models.BalanceActivity) *balanceActivityBelongsToUserTx {
|
||||
return &balanceActivityBelongsToUserTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUser) Unscoped() *balanceActivityBelongsToUser {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityBelongsToUserTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Find() (result *models.User, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Append(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Replace(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Delete(values ...*models.User) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToUserTx) Unscoped() *balanceActivityBelongsToUserTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityBelongsToBill struct {
|
||||
db *gorm.DB
|
||||
|
||||
field.RelationField
|
||||
|
||||
User struct {
|
||||
field.RelationField
|
||||
}
|
||||
Trade struct {
|
||||
field.RelationField
|
||||
User struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
Resource struct {
|
||||
field.RelationField
|
||||
User struct {
|
||||
field.RelationField
|
||||
}
|
||||
Short struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
Discount struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
}
|
||||
Long struct {
|
||||
field.RelationField
|
||||
Sku struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
Product struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
Refund struct {
|
||||
field.RelationField
|
||||
}
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBill) Where(conds ...field.Expr) *balanceActivityBelongsToBill {
|
||||
if len(conds) == 0 {
|
||||
return &a
|
||||
}
|
||||
|
||||
exprs := make([]clause.Expression, 0, len(conds))
|
||||
for _, cond := range conds {
|
||||
exprs = append(exprs, cond.BeCond().(clause.Expression))
|
||||
}
|
||||
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBill) WithContext(ctx context.Context) *balanceActivityBelongsToBill {
|
||||
a.db = a.db.WithContext(ctx)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBill) Session(session *gorm.Session) *balanceActivityBelongsToBill {
|
||||
a.db = a.db.Session(session)
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBill) Model(m *models.BalanceActivity) *balanceActivityBelongsToBillTx {
|
||||
return &balanceActivityBelongsToBillTx{a.db.Model(m).Association(a.Name())}
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBill) Unscoped() *balanceActivityBelongsToBill {
|
||||
a.db = a.db.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityBelongsToBillTx struct{ tx *gorm.Association }
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Find() (result *models.Bill, err error) {
|
||||
return result, a.tx.Find(&result)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Append(values ...*models.Bill) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Append(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Replace(values ...*models.Bill) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Replace(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Delete(values ...*models.Bill) (err error) {
|
||||
targetValues := make([]interface{}, len(values))
|
||||
for i, v := range values {
|
||||
targetValues[i] = v
|
||||
}
|
||||
return a.tx.Delete(targetValues...)
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Clear() error {
|
||||
return a.tx.Clear()
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Count() int64 {
|
||||
return a.tx.Count()
|
||||
}
|
||||
|
||||
func (a balanceActivityBelongsToBillTx) Unscoped() *balanceActivityBelongsToBillTx {
|
||||
a.tx = a.tx.Unscoped()
|
||||
return &a
|
||||
}
|
||||
|
||||
type balanceActivityDo struct{ gen.DO }
|
||||
|
||||
func (b balanceActivityDo) Debug() *balanceActivityDo {
|
||||
return b.withDO(b.DO.Debug())
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) WithContext(ctx context.Context) *balanceActivityDo {
|
||||
return b.withDO(b.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) ReadDB() *balanceActivityDo {
|
||||
return b.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) WriteDB() *balanceActivityDo {
|
||||
return b.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Session(config *gorm.Session) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Session(config))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Clauses(conds ...clause.Expression) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Returning(value interface{}, columns ...string) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Not(conds ...gen.Condition) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Or(conds ...gen.Condition) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Select(conds ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Where(conds ...gen.Condition) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Order(conds ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Distinct(cols ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Omit(cols ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Join(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) LeftJoin(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) RightJoin(table schema.Tabler, on ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Group(cols ...field.Expr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Having(conds ...gen.Condition) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Limit(limit int) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Offset(offset int) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Unscoped() *balanceActivityDo {
|
||||
return b.withDO(b.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Create(values ...*models.BalanceActivity) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return b.DO.Create(values)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) CreateInBatches(values []*models.BalanceActivity, batchSize int) error {
|
||||
return b.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (b balanceActivityDo) Save(values ...*models.BalanceActivity) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return b.DO.Save(values)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) First() (*models.BalanceActivity, error) {
|
||||
if result, err := b.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.BalanceActivity), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Take() (*models.BalanceActivity, error) {
|
||||
if result, err := b.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.BalanceActivity), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Last() (*models.BalanceActivity, error) {
|
||||
if result, err := b.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.BalanceActivity), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Find() ([]*models.BalanceActivity, error) {
|
||||
result, err := b.DO.Find()
|
||||
return result.([]*models.BalanceActivity), err
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.BalanceActivity, err error) {
|
||||
buf := make([]*models.BalanceActivity, 0, batchSize)
|
||||
err = b.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) FindInBatches(result *[]*models.BalanceActivity, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return b.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Attrs(attrs ...field.AssignExpr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Assign(attrs ...field.AssignExpr) *balanceActivityDo {
|
||||
return b.withDO(b.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Joins(fields ...field.RelationField) *balanceActivityDo {
|
||||
for _, _f := range fields {
|
||||
b = *b.withDO(b.DO.Joins(_f))
|
||||
}
|
||||
return &b
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Preload(fields ...field.RelationField) *balanceActivityDo {
|
||||
for _, _f := range fields {
|
||||
b = *b.withDO(b.DO.Preload(_f))
|
||||
}
|
||||
return &b
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) FirstOrInit() (*models.BalanceActivity, error) {
|
||||
if result, err := b.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.BalanceActivity), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) FirstOrCreate() (*models.BalanceActivity, error) {
|
||||
if result, err := b.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*models.BalanceActivity), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) FindByPage(offset int, limit int) (result []*models.BalanceActivity, count int64, err error) {
|
||||
result, err = b.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = b.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = b.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = b.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Scan(result interface{}) (err error) {
|
||||
return b.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (b balanceActivityDo) Delete(models ...*models.BalanceActivity) (result gen.ResultInfo, err error) {
|
||||
return b.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (b *balanceActivityDo) withDO(do gen.Dao) *balanceActivityDo {
|
||||
b.DO = *do.(*gen.DO)
|
||||
return b
|
||||
}
|
||||
@@ -20,6 +20,7 @@ var (
|
||||
Admin *admin
|
||||
AdminRole *adminRole
|
||||
Announcement *announcement
|
||||
BalanceActivity *balanceActivity
|
||||
Bill *bill
|
||||
Channel *channel
|
||||
Client *client
|
||||
@@ -57,6 +58,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
Admin = &Q.Admin
|
||||
AdminRole = &Q.AdminRole
|
||||
Announcement = &Q.Announcement
|
||||
BalanceActivity = &Q.BalanceActivity
|
||||
Bill = &Q.Bill
|
||||
Channel = &Q.Channel
|
||||
Client = &Q.Client
|
||||
@@ -95,6 +97,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
Admin: newAdmin(db, opts...),
|
||||
AdminRole: newAdminRole(db, opts...),
|
||||
Announcement: newAnnouncement(db, opts...),
|
||||
BalanceActivity: newBalanceActivity(db, opts...),
|
||||
Bill: newBill(db, opts...),
|
||||
Channel: newChannel(db, opts...),
|
||||
Client: newClient(db, opts...),
|
||||
@@ -134,6 +137,7 @@ type Query struct {
|
||||
Admin admin
|
||||
AdminRole adminRole
|
||||
Announcement announcement
|
||||
BalanceActivity balanceActivity
|
||||
Bill bill
|
||||
Channel channel
|
||||
Client client
|
||||
@@ -174,6 +178,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
|
||||
Admin: q.Admin.clone(db),
|
||||
AdminRole: q.AdminRole.clone(db),
|
||||
Announcement: q.Announcement.clone(db),
|
||||
BalanceActivity: q.BalanceActivity.clone(db),
|
||||
Bill: q.Bill.clone(db),
|
||||
Channel: q.Channel.clone(db),
|
||||
Client: q.Client.clone(db),
|
||||
@@ -221,6 +226,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
Admin: q.Admin.replaceDB(db),
|
||||
AdminRole: q.AdminRole.replaceDB(db),
|
||||
Announcement: q.Announcement.replaceDB(db),
|
||||
BalanceActivity: q.BalanceActivity.replaceDB(db),
|
||||
Bill: q.Bill.replaceDB(db),
|
||||
Channel: q.Channel.replaceDB(db),
|
||||
Client: q.Client.replaceDB(db),
|
||||
@@ -258,6 +264,7 @@ type queryCtx struct {
|
||||
Admin *adminDo
|
||||
AdminRole *adminRoleDo
|
||||
Announcement *announcementDo
|
||||
BalanceActivity *balanceActivityDo
|
||||
Bill *billDo
|
||||
Channel *channelDo
|
||||
Client *clientDo
|
||||
@@ -295,6 +302,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
Admin: q.Admin.WithContext(ctx),
|
||||
AdminRole: q.AdminRole.WithContext(ctx),
|
||||
Announcement: q.Announcement.WithContext(ctx),
|
||||
BalanceActivity: q.BalanceActivity.WithContext(ctx),
|
||||
Bill: q.Bill.WithContext(ctx),
|
||||
Channel: q.Channel.WithContext(ctx),
|
||||
Client: q.Client.WithContext(ctx),
|
||||
|
||||
@@ -116,7 +116,7 @@ func clientRouter(api fiber.Router) {
|
||||
client := api
|
||||
|
||||
// 验证短信令牌
|
||||
client.Post("/sms/verify", handlers.SmsCode)
|
||||
client.Post("/verify/sms", handlers.SmsCode)
|
||||
|
||||
// 套餐定价查询
|
||||
resource := client.Group("/resource")
|
||||
@@ -159,11 +159,13 @@ func adminRouter(api fiber.Router) {
|
||||
// user 用户
|
||||
var user = api.Group("/user")
|
||||
user.Post("/page", handlers.PageUserByAdmin)
|
||||
user.Post("/get", handlers.GetUserByAdmin)
|
||||
user.Post("/create", handlers.CreateUserByAdmin)
|
||||
user.Post("/update", handlers.UpdateUserByAdmin)
|
||||
user.Post("/remove", handlers.RemoveUserByAdmin)
|
||||
|
||||
user.Post("/bind", handlers.BindAdmin)
|
||||
user.Post("/update/balance", handlers.UpdateUserBalanceByAdmin)
|
||||
|
||||
// resource 套餐
|
||||
var resource = api.Group("/resource")
|
||||
|
||||
@@ -122,7 +122,7 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
||||
// 更新管理员基本信息
|
||||
if len(simples) > 0 {
|
||||
_, err := tx.Admin.
|
||||
Where(tx.Admin.ID.Eq(update.Id)).
|
||||
Where(tx.Admin.ID.Eq(update.Id), tx.Admin.Username.Neq("admin")).
|
||||
UpdateSimple(simples...)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -154,6 +154,6 @@ func (s *adminService) UpdateAdmin(update *UpdateAdmin) error {
|
||||
}
|
||||
|
||||
func (s *adminService) RemoveAdmin(id int32) error {
|
||||
_, err := q.Admin.Where(q.Admin.ID.Eq(id)).UpdateColumn(q.Admin.DeletedAt, time.Now())
|
||||
_, err := q.Admin.Where(q.Admin.ID.Eq(id), q.Admin.Username.Neq("admin")).UpdateColumn(q.Admin.DeletedAt, time.Now())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ var Coupon = &couponService{}
|
||||
type couponService struct{}
|
||||
|
||||
func (s *couponService) All() (result []*m.Coupon, err error) {
|
||||
return q.Coupon.Find()
|
||||
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).Find()
|
||||
}
|
||||
|
||||
func (s *couponService) Page(req *core.PageReq) (result []*m.Coupon, count int64, err error) {
|
||||
return q.Coupon.FindByPage(req.GetOffset(), req.GetLimit())
|
||||
return q.Coupon.Order(q.Coupon.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
|
||||
}
|
||||
|
||||
func (s *couponService) Create(data CreateCouponData) error {
|
||||
|
||||
@@ -15,5 +15,5 @@ func (r *permissionService) ListPermissions() (result []*m.Permission, err error
|
||||
}
|
||||
|
||||
func (p *permissionService) PagePermissions(req core.PageReq) (result []*m.Permission, count int64, err error) {
|
||||
return q.Permission.FindByPage(req.GetOffset(), req.GetLimit())
|
||||
return q.Permission.Order(q.Permission.Sort).FindByPage(req.GetOffset(), req.GetLimit())
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ func (s *productService) GetPrice(code string) {
|
||||
|
||||
// 获取所有产品
|
||||
func (s *productService) AllProducts() ([]*m.Product, error) {
|
||||
return q.Product.Find()
|
||||
return q.Product.
|
||||
Order(q.Product.Sort.Asc(), q.Product.CreatedAt.Desc()).
|
||||
Find()
|
||||
}
|
||||
|
||||
// 新增产品
|
||||
|
||||
@@ -14,11 +14,11 @@ var ProductDiscount = &productDiscountService{}
|
||||
type productDiscountService struct{}
|
||||
|
||||
func (s *productDiscountService) All() (result []*m.ProductDiscount, err error) {
|
||||
return q.ProductDiscount.Find()
|
||||
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).Find()
|
||||
}
|
||||
|
||||
func (s *productDiscountService) Page(req *core.PageReq) (result []*m.ProductDiscount, count int64, err error) {
|
||||
return q.ProductDiscount.FindByPage(req.GetOffset(), req.GetLimit())
|
||||
return q.ProductDiscount.Order(q.ProductDiscount.CreatedAt.Desc()).FindByPage(req.GetOffset(), req.GetLimit())
|
||||
}
|
||||
|
||||
func (s *productDiscountService) Create(data CreateProductDiscountData) (err error) {
|
||||
|
||||
@@ -20,6 +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()).
|
||||
Find()
|
||||
}
|
||||
|
||||
@@ -31,6 +32,7 @@ func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []
|
||||
return q.ProductSku.
|
||||
Joins(q.ProductSku.Discount).
|
||||
Where(do...).
|
||||
Order(q.ProductSku.CreatedAt.Desc()).
|
||||
FindByPage(req.GetOffset(), req.GetLimit())
|
||||
}
|
||||
|
||||
|
||||
@@ -26,21 +26,10 @@ func (s *resourceService) CreateResourceByBalance(user *m.User, data *CreateReso
|
||||
return core.NewServErr("获取产品支付信息失败", err)
|
||||
}
|
||||
|
||||
newBalance := user.Balance.Sub(detail.Actual)
|
||||
if newBalance.IsNegative() {
|
||||
return ErrBalanceNotEnough
|
||||
}
|
||||
|
||||
return q.Q.Transaction(func(q *q.Query) error {
|
||||
|
||||
// 更新用户余额
|
||||
_, err = q.User.
|
||||
Where(
|
||||
q.User.ID.Eq(user.ID),
|
||||
q.User.Balance.Eq(user.Balance),
|
||||
).
|
||||
UpdateSimple(q.User.Balance.Value(newBalance))
|
||||
if err != nil {
|
||||
if err := User.UpdateBalance(q, user, detail.Actual.Neg(), "余额购买产品", nil); err != nil {
|
||||
return core.NewServErr("更新用户余额失败", err)
|
||||
}
|
||||
|
||||
@@ -273,13 +262,21 @@ func (data *CreateResourceData) TradeDetail(user *m.User) (*TradeDetail, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var discountId *int32 = nil
|
||||
if discount != nil {
|
||||
discountId = &discount.ID
|
||||
}
|
||||
var couponId *int32 = nil
|
||||
if coupon != nil {
|
||||
couponId = &coupon.ID
|
||||
}
|
||||
return &TradeDetail{
|
||||
data,
|
||||
m.TradeTypePurchase,
|
||||
sku.Name,
|
||||
amount, actual,
|
||||
&discount.ID, discount,
|
||||
&coupon.ID, coupon,
|
||||
discountId, discount,
|
||||
couponId, coupon,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -26,13 +27,18 @@ import (
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(&CreateResourceData{})
|
||||
gob.Register(&UpdateBalanceData{})
|
||||
}
|
||||
|
||||
var Trade = &tradeService{}
|
||||
|
||||
type tradeService struct {
|
||||
}
|
||||
|
||||
// 创建交易
|
||||
func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productData *CreateResourceData) (*CreateTradeResult, error) {
|
||||
func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productData ProductData) (*CreateTradeResult, error) {
|
||||
if user == nil {
|
||||
return nil, core.NewBizErr("用户未登录")
|
||||
}
|
||||
@@ -196,15 +202,12 @@ func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productD
|
||||
}
|
||||
|
||||
// 缓存产品数据
|
||||
serialized, err := json.Marshal(detail)
|
||||
if err != nil {
|
||||
return nil, core.NewServErr("序列化产品信息失败", err)
|
||||
}
|
||||
|
||||
w := bytes.Buffer{}
|
||||
gob.NewEncoder(&w).Encode(detail)
|
||||
err = g.Redis.Set(
|
||||
context.Background(),
|
||||
tradeProductKey(tradeNo),
|
||||
serialized,
|
||||
w.Bytes(),
|
||||
expireIn,
|
||||
).Err()
|
||||
if err != nil {
|
||||
@@ -267,14 +270,15 @@ func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo str
|
||||
case m.TradeStatusPending:
|
||||
}
|
||||
|
||||
// 恢复购买信息
|
||||
detailStr, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Result()
|
||||
// 恢复购买信息;如果反序列化失败,检查开头 init 函数中是否注册了对应的 struct 类型
|
||||
detailBytes, err := g.Redis.Get(context.Background(), tradeProductKey(interNo)).Bytes()
|
||||
if err != nil {
|
||||
return core.NewServErr("恢复购买信息失败", err)
|
||||
}
|
||||
|
||||
var detail TradeDetail
|
||||
if err := json.Unmarshal([]byte(detailStr), &detail); err != nil {
|
||||
r := bytes.NewReader(detailBytes)
|
||||
if err := gob.NewDecoder(r).Decode(&detail); err != nil {
|
||||
return core.NewServErr("解析购买信息失败", err)
|
||||
}
|
||||
|
||||
@@ -299,7 +303,7 @@ 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); err != nil {
|
||||
if err := User.UpdateBalance(q, user, detail.Actual, "充值余额", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -605,12 +609,12 @@ type OnTradeCompletedData struct {
|
||||
*TradeSuccessResult
|
||||
}
|
||||
|
||||
type ProductInfo interface {
|
||||
type ProductData interface {
|
||||
TradeDetail(user *m.User) (*TradeDetail, error)
|
||||
}
|
||||
|
||||
type TradeDetail struct {
|
||||
Product ProductInfo `json:"product"`
|
||||
Product ProductData `json:"product"`
|
||||
Type m.TradeType `json:"type"`
|
||||
Subject string `json:"subject"`
|
||||
Amount decimal.Decimal `json:"amount"`
|
||||
@@ -621,11 +625,6 @@ type TradeDetail struct {
|
||||
Coupon *m.Coupon `json:"-"` // 不应缓存
|
||||
}
|
||||
|
||||
type CompleteEvent interface {
|
||||
Check(t m.TradeType) (ProductInfo, bool)
|
||||
OnTradeComplete(info ProductInfo, trade *m.Trade) error
|
||||
}
|
||||
|
||||
type TradeErr string
|
||||
|
||||
func (e TradeErr) Error() string {
|
||||
|
||||
@@ -28,12 +28,28 @@ func (s *userService) Get(q *q.Query, uid int32) (*m.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal) error {
|
||||
func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Decimal, adminId *int32) error {
|
||||
if user == nil {
|
||||
return core.NewServErr("用户不存在")
|
||||
}
|
||||
|
||||
amount := newBalance.Sub(user.Balance)
|
||||
if amount.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return q.Q.Transaction(func(q *q.Query) error {
|
||||
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
|
||||
balance := user.Balance.Add(amount)
|
||||
if balance.IsNegative() {
|
||||
return core.NewServErr("用户余额不足")
|
||||
}
|
||||
|
||||
// 更新余额
|
||||
_, err := q.User.
|
||||
Where(
|
||||
q.User.ID.Eq(user.ID),
|
||||
@@ -46,6 +62,19 @@ func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Dec
|
||||
return core.NewServErr("更新用户余额失败", err)
|
||||
}
|
||||
|
||||
// 新增动账记录
|
||||
err = q.BalanceActivity.Create(&m.BalanceActivity{
|
||||
UserID: user.ID,
|
||||
AdminID: adminId,
|
||||
Amount: amount.StringFixed(2),
|
||||
BalancePrev: user.Balance.StringFixed(2),
|
||||
BalanceCurr: balance.StringFixed(2),
|
||||
Remark: &remark,
|
||||
})
|
||||
if err != nil {
|
||||
return core.NewServErr("新增动账记录失败", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user