From 5d00111887d267f1c2e96223dacdb4a2c5aca324 Mon Sep 17 00:00:00 2001 From: luorijun Date: Mon, 6 Jul 2026 18:09:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=95=86=E7=A6=8F=E9=80=9A?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=97=AE=E9=A2=98=20&=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=A5=97=E9=A4=90=E7=BB=9F=E4=B8=80=E6=9F=A5=E8=AF=A2=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 +- docs/api.yaml | 177 +++++++++++++++ go.mod | 2 +- scripts/sql/fill.sql | 4 + web/core/scopes.go | 7 +- web/globals/shangfutong.go | 2 +- web/handlers/resource.go | 451 +++++++++++++++++++++++++++++++++++++ web/routes.go | 3 + web/services/trade.go | 5 +- 9 files changed, 664 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b29f6ac..a1a88cd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,26 @@ ## TODO +长短效合并 + +现在我需要提供一套首页接口,用来展示一些统计信息。检查 [README.md](README.md) 的后台首页接口部分,确保你正确理解了每个接口的含义,如何进行统计。如果有不清除的接口,先提出问题,我会补充相关信息。在没有问题后,给出实施计划 + +后台首页接口: +- 待认领客户数量 +- 活跃代理数 +- 今日交易额 +- 今日提取数量 +- 今日新增客户 +- 每日活跃代理 +- 每日交易额 +- 每日新增客户 +- 每日提取量 +- 全局可提取统计(包时套餐计算最大可提取,每日提取上限 * 剩余可提取天数) + - 长效套餐 + - 短效套餐 +- 最近活跃用户(七日内 ip 使用量) + +--- + 关闭支付弹窗 后台限制 ip @@ -10,7 +31,7 @@ 错误提示增强,展示整链路信息 -交易信息持久化 +交易信息持久化 订单关闭问题,在前端关闭窗口后直接调用了全部订单接口,应改成先确认再关闭 diff --git a/docs/api.yaml b/docs/api.yaml index 87aa6c6..c949de3 100644 --- a/docs/api.yaml +++ b/docs/api.yaml @@ -506,6 +506,37 @@ components: - mode - quota + PageResourceRequest: + allOf: + - $ref: "#/components/schemas/PageRequest" + - type: object + properties: + resource_no: + type: string + active: + type: boolean + resource_type: + type: integer + description: 1=短效动态,2=长效动态 + type: + type: integer + description: 1=包时,2=包量 + create_after: + type: string + format: date-time + create_before: + type: string + format: date-time + expire_after: + type: string + format: date-time + expire_before: + type: string + format: date-time + status: + type: integer + description: 0=全部,1=有效,2=过期 + PageResourceShortRequest: allOf: - $ref: "#/components/schemas/PageRequest" @@ -1440,6 +1471,32 @@ components: required: - id + PageResourceByAdminRequest: + allOf: + - $ref: "#/components/schemas/PageRequest" + - type: object + properties: + user_phone: + type: string + resource_no: + type: string + active: + type: boolean + resource_type: + type: integer + description: 1=短效动态,2=长效动态 + mode: + type: integer + description: 1=包时,2=包量 + created_at_start: + type: string + format: date-time + created_at_end: + type: string + format: date-time + expired: + type: boolean + PageResourceShortByAdminRequest: allOf: - $ref: "#/components/schemas/PageRequest" @@ -1484,6 +1541,34 @@ components: expired: type: boolean + PageResourceOfUserByAdminRequest: + allOf: + - $ref: "#/components/schemas/PageRequest" + - type: object + properties: + user_id: + type: integer + resource_no: + type: string + active: + type: boolean + resource_type: + type: integer + description: 1=短效动态,2=长效动态 + mode: + type: integer + description: 1=包时,2=包量 + created_at_start: + type: string + format: date-time + created_at_end: + type: string + format: date-time + expired: + type: boolean + required: + - user_id + PageResourceShortOfUserByAdminRequest: allOf: - $ref: "#/components/schemas/PageRequest" @@ -2589,6 +2674,11 @@ components: long: $ref: "#/components/schemas/ResourceLongAdminInfo" + ResourceAdminItem: + oneOf: + - $ref: "#/components/schemas/ResourceShortAdminItem" + - $ref: "#/components/schemas/ResourceLongAdminItem" + ChannelUserItem: allOf: - $ref: "#/components/schemas/BaseModel" @@ -3064,6 +3154,18 @@ components: required: - list + PageResourceResponse: + allOf: + - $ref: "#/components/schemas/PageMeta" + - type: object + properties: + list: + type: array + items: + $ref: "#/components/schemas/ResourceAllItem" + required: + - list + PageResourceShortResponse: allOf: - $ref: "#/components/schemas/PageMeta" @@ -3220,6 +3322,18 @@ components: required: - list + PageResourceAdminResponse: + allOf: + - $ref: "#/components/schemas/PageMeta" + - type: object + properties: + list: + type: array + items: + $ref: "#/components/schemas/ResourceAdminItem" + required: + - list + PageResourceShortAdminResponse: allOf: - $ref: "#/components/schemas/PageMeta" @@ -3911,6 +4025,27 @@ paths: default: $ref: "#/components/responses/PlainTextError" + /api/resource/list: + post: + tags: [user/resource] + summary: 分页查询当前用户长短效套餐 + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceRequest" + responses: + "200": + description: 分页结果 + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceResponse" + default: + $ref: "#/components/responses/PlainTextError" + /api/resource/list/short: post: tags: [user/resource] @@ -4709,6 +4844,48 @@ paths: default: $ref: "#/components/responses/PlainTextError" + /api/admin/resource/page: + post: + tags: [admin/resource] + summary: 分页查询全部长短效套餐 + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceByAdminRequest" + responses: + "200": + description: 分页结果 + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceAdminResponse" + default: + $ref: "#/components/responses/PlainTextError" + + /api/admin/resource/page/of-user: + post: + tags: [admin/resource] + summary: 分页查询指定用户长短效套餐 + security: + - bearerAuth: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceOfUserByAdminRequest" + responses: + "200": + description: 分页结果 + content: + application/json: + schema: + $ref: "#/components/schemas/PageResourceAdminResponse" + default: + $ref: "#/components/responses/PlainTextError" + /api/admin/resource/short/page: post: tags: [admin/resource] diff --git a/go.mod b/go.mod index 4d76fb6..2668132 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/redis/go-redis/v9 v9.17.2 github.com/shopspring/decimal v1.4.0 github.com/smartwalle/alipay/v3 v3.2.28 - github.com/valyala/fasthttp v1.68.0 github.com/wechatpay-apiv3/wechatpay-go v0.2.21 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 @@ -85,6 +84,7 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.68.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect diff --git a/scripts/sql/fill.sql b/scripts/sql/fill.sql index d51ed40..8633282 100644 --- a/scripts/sql/fill.sql +++ b/scripts/sql/fill.sql @@ -251,6 +251,10 @@ insert into permission (parent_id, name, description, sort) values insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'resource:long' and deleted_at is null), 'resource:long:read', '读取用户长效动态套餐列表', 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:of_user', '读取指定用户的用户套餐列表', 1); + -- user:read 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'user:read' and deleted_at is null), 'user:read:one', '读取单个用户', 1), diff --git a/web/core/scopes.go b/web/core/scopes.go index 94f084f..b982ce9 100644 --- a/web/core/scopes.go +++ b/web/core/scopes.go @@ -26,9 +26,10 @@ 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") // 读取用户套餐列表 + ScopeResourceWrite = string("resource:write") // 写入用户套餐 + ScopeResourceReadOfUser = string("resource:read:of_user") // 读取指定用户的用户套餐列表 ScopeResourceShort = string("resource:short") // 短效动态套餐 ScopeResourceShortRead = string("resource:short:read") // 读取用户短效动态套餐列表 diff --git a/web/globals/shangfutong.go b/web/globals/shangfutong.go index 88b3b4e..c0dfcab 100644 --- a/web/globals/shangfutong.go +++ b/web/globals/shangfutong.go @@ -233,7 +233,7 @@ type QueryTradeResp struct { RefundState int32 `json:"refundState"` DrType *string `json:"drType"` ExtParam *string `json:"extParam"` - PayTime *string `json:"payTime"` + PayTime *int64 `json:"payTime"` Subject string `json:"subject"` TradeFee *int64 `json:"tradeFee"` CashFee *int64 `json:"cashFee"` diff --git a/web/handlers/resource.go b/web/handlers/resource.go index 9b51020..717738a 100644 --- a/web/handlers/resource.go +++ b/web/handlers/resource.go @@ -15,6 +15,176 @@ import ( "github.com/gofiber/fiber/v2" ) +// PageResource 分页查询当前用户长短效套餐 +func PageResource(c *fiber.Ctx) error { + authCtx, err := auth.GetAuthCtx(c).PermitUser() + if err != nil { + return err + } + + req := new(PageResourceReq) + if err := c.BodyParser(req); err != nil { + return err + } + + short := q.ResourceShort.As(q.Resource.Short.Name()) + long := q.ResourceLong.As(q.Resource.Long.Name()) + do := q.Resource.Where(q.Resource.UserID.Eq(authCtx.User.ID)) + if req.ResourceNo != nil && *req.ResourceNo != "" { + do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) + } + if req.Active != nil { + do = do.Where(q.Resource.Active.Is(*req.Active)) + } + if req.ResourceType != nil { + do = do.Where(q.Resource.Type.Eq(*req.ResourceType)) + } + if req.Type != nil { + if req.ResourceType != nil { + switch m.ResourceType(*req.ResourceType) { + case m.ResourceTypeShort: + do = do.Where(short.Type.Eq(*req.Type)) + case m.ResourceTypeLong: + do = do.Where(long.Type.Eq(*req.Type)) + } + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(*req.Type), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(*req.Type), + )) + } + } + if req.CreateAfter != nil { + do = do.Where(q.Resource.CreatedAt.Gte(req.CreateAfter.UTC())) + } + if req.CreateBefore != nil { + do = do.Where(q.Resource.CreatedAt.Lte(req.CreateBefore.UTC())) + } + if req.ExpireAfter != nil { + if req.ResourceType != nil { + switch m.ResourceType(*req.ResourceType) { + case m.ResourceTypeShort: + do = do.Where(short.ExpireAt.Gte(req.ExpireAfter.UTC())) + case m.ResourceTypeLong: + do = do.Where(long.ExpireAt.Gte(req.ExpireAfter.UTC())) + } + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.ExpireAt.Gte(req.ExpireAfter.UTC()), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.ExpireAt.Gte(req.ExpireAfter.UTC()), + )) + } + } + if req.ExpireBefore != nil { + if req.ResourceType != nil { + switch m.ResourceType(*req.ResourceType) { + case m.ResourceTypeShort: + do = do.Where(short.ExpireAt.Lte(req.ExpireBefore.UTC())) + case m.ResourceTypeLong: + do = do.Where(long.ExpireAt.Lte(req.ExpireBefore.UTC())) + } + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.ExpireAt.Lte(req.ExpireBefore.UTC()), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.ExpireAt.Lte(req.ExpireBefore.UTC()), + )) + } + } + if req.Status != nil { + now := time.Now().UTC() + switch *req.Status { + case 1: + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Gte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.GtCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Gte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.GtCol(long.Used), + )) + case 2: + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.LteCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.LteCol(long.Used), + )) + } + } + + resource, err := q.Resource.Where(do). + Joins(q.Resource.Short, q.Resource.Long). + Order(q.Resource.CreatedAt.Desc()). + Offset(req.GetOffset()). + Limit(req.GetLimit()). + Find() + if err != nil { + return err + } + + var total int64 + if len(resource) < req.GetLimit() { + total = int64(len(resource) + req.GetOffset()) + } else { + total, err = q.Resource. + Joins(q.Resource.Short, q.Resource.Long). + Where(do). + Count() + if err != nil { + return err + } + } + + return c.JSON(core.PageResp{ + Total: int(total), + Page: req.GetPage(), + Size: req.GetSize(), + List: resource, + }) +} + +type PageResourceReq struct { + core.PageReq + ResourceNo *string `json:"resource_no"` + Active *bool `json:"active"` + ResourceType *int `json:"resource_type"` // 1 - 短效动态,2 - 长效动态 + Type *int `json:"type"` // 1 - 包时,2 - 包量 + CreateAfter *time.Time `json:"create_after"` + CreateBefore *time.Time `json:"create_before"` + ExpireAfter *time.Time `json:"expire_after"` + ExpireBefore *time.Time `json:"expire_before"` + Status *int `json:"status"` // 0 - 全部,1 - 有效,2 - 过期 +} + // PageResourceShort 分页查询当前用户短效套餐 func PageResourceShort(c *fiber.Ctx) error { // 检查权限 @@ -209,6 +379,148 @@ type PageResourceLongReq struct { Status *int `json:"status"` // 0 - 全部,1 - 有效,2 - 过期 } +// PageResourceByAdmin 分页查询全部长短效套餐 +func PageResourceByAdmin(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceRead) + if err != nil { + return err + } + + var req PageResourceByAdminReq + if err = g.Validator.ParseBody(c, &req); err != nil { + return err + } + + short := q.ResourceShort.As("Short") + long := q.ResourceLong.As("Long") + do := q.Resource.Where() + if req.UserPhone != nil { + do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone)) + } + if req.ResourceNo != nil { + do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) + } + if req.Active != nil { + do = do.Where(q.Resource.Active.Is(*req.Active)) + } + if req.ResourceType != nil { + do = do.Where(q.Resource.Type.Eq(*req.ResourceType)) + } + if req.Mode != nil { + if req.ResourceType != nil { + switch m.ResourceType(*req.ResourceType) { + case m.ResourceTypeShort: + do = do.Where(short.Type.Eq(*req.Mode)) + case m.ResourceTypeLong: + do = do.Where(long.Type.Eq(*req.Mode)) + } + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(*req.Mode), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(*req.Mode), + )) + } + } + if req.CreatedAtStart != nil { + do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC())) + } + if req.CreatedAtEnd != nil { + do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC())) + } + if req.Expired != nil { + now := time.Now().UTC() + if *req.Expired { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.LteCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.LteCol(long.Used), + )) + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Gt(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.GtCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Gt(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.GtCol(long.Used), + )) + } + } + + list, total, err := q.Resource. + Joins(q.Resource.User, q.Resource.Short, q.Resource.Long, q.Resource.Short.Sku, q.Resource.Long.Sku). + Select( + q.Resource.ALL, + q.User.As("User").Phone.As("User__phone"), + q.User.As("User").Name.As("User__name"), + short.Type.As("Short__type"), + short.Live.As("Short__live"), + short.Quota.As("Short__quota"), + short.Used.As("Short__used"), + short.Daily.As("Short__daily"), + short.LastAt.As("Short__last_at"), + short.ExpireAt.As("Short__expire_at"), + q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"), + long.Type.As("Long__type"), + long.Live.As("Long__live"), + long.Quota.As("Long__quota"), + long.Used.As("Long__used"), + long.Daily.As("Long__daily"), + long.LastAt.As("Long__last_at"), + long.ExpireAt.As("Long__expire_at"), + q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"), + ). + Where(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 PageResourceByAdminReq struct { + core.PageReq + UserPhone *string `json:"user_phone" form:"user_phone"` + ResourceNo *string `json:"resource_no" form:"resource_no"` + Active *bool `json:"active" form:"active"` + ResourceType *int `json:"resource_type" form:"resource_type"` + Mode *int `json:"mode" form:"mode"` + CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"` + CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"` + Expired *bool `json:"expired" form:"expired"` +} + // PageResourceShortByAdmin 分页查询全部短效套餐 func PageResourceShortByAdmin(c *fiber.Ctx) error { _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortRead) @@ -393,6 +705,145 @@ type PageResourceLongByAdminReq struct { Expired *bool `json:"expired" form:"expired"` } +// PageResourceOfUserByAdmin 分页查询指定用户的长短效套餐 +func PageResourceOfUserByAdmin(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceReadOfUser) + if err != nil { + return err + } + + var req PageResourceOfUserByAdminReq + if err = g.Validator.ParseBody(c, &req); err != nil { + return err + } + + short := q.ResourceShort.As("Short") + long := q.ResourceLong.As("Long") + 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.ResourceType != nil { + do = do.Where(q.Resource.Type.Eq(*req.ResourceType)) + } + if req.Mode != nil { + if req.ResourceType != nil { + switch m.ResourceType(*req.ResourceType) { + case m.ResourceTypeShort: + do = do.Where(short.Type.Eq(*req.Mode)) + case m.ResourceTypeLong: + do = do.Where(long.Type.Eq(*req.Mode)) + } + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(*req.Mode), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(*req.Mode), + )) + } + } + if req.CreatedAtStart != nil { + do = do.Where(q.Resource.CreatedAt.Gte(req.CreatedAtStart.UTC())) + } + if req.CreatedAtEnd != nil { + do = do.Where(q.Resource.CreatedAt.Lte(req.CreatedAtEnd.UTC())) + } + if req.Expired != nil { + now := time.Now().UTC() + if *req.Expired { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.LteCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Lte(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.LteCol(long.Used), + )) + } else { + do = do.Where(q.Resource.Where( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeTime)), + short.ExpireAt.Gt(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeShort)), + short.Type.Eq(int(m.ResourceModeQuota)), + short.Quota.GtCol(short.Used), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeTime)), + long.ExpireAt.Gt(now), + ).Or( + q.Resource.Type.Eq(int(m.ResourceTypeLong)), + long.Type.Eq(int(m.ResourceModeQuota)), + long.Quota.GtCol(long.Used), + )) + } + } + + list, total, err := q.Resource. + Joins(q.Resource.User, q.Resource.Short, q.Resource.Long, q.Resource.Short.Sku, q.Resource.Long.Sku). + Select( + q.Resource.ALL, + q.User.As("User").Phone.As("User__phone"), + q.User.As("User").Name.As("User__name"), + short.Type.As("Short__type"), + short.Live.As("Short__live"), + short.Quota.As("Short__quota"), + short.Used.As("Short__used"), + short.Daily.As("Short__daily"), + short.LastAt.As("Short__last_at"), + short.ExpireAt.As("Short__expire_at"), + q.ProductSku.As("Short__Sku").Name.As("Short__Sku__name"), + long.Type.As("Long__type"), + long.Live.As("Long__live"), + long.Quota.As("Long__quota"), + long.Used.As("Long__used"), + long.Daily.As("Long__daily"), + long.LastAt.As("Long__last_at"), + long.ExpireAt.As("Long__expire_at"), + q.ProductSku.As("Long__Sku").Name.As("Long__Sku__name"), + ). + Where(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 PageResourceOfUserByAdminReq struct { + core.PageReq + UserID int32 `json:"user_id" validate:"required"` + ResourceNo *string `json:"resource_no"` + Active *bool `json:"active"` + ResourceType *int `json:"resource_type"` + Mode *int `json:"mode"` + CreatedAtStart *time.Time `json:"created_at_start"` + CreatedAtEnd *time.Time `json:"created_at_end"` + Expired *bool `json:"expired"` +} + // PageResourceShortOfUserByAdmin 分页查询指定用户的短效套餐 func PageResourceShortOfUserByAdmin(c *fiber.Ctx) error { _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeResourceShortReadOfUser) diff --git a/web/routes.go b/web/routes.go index c1bb9d3..45174ba 100644 --- a/web/routes.go +++ b/web/routes.go @@ -112,6 +112,7 @@ func userRouter(api fiber.Router) { // 套餐 resource := api.Group("/resource") resource.Post("/all", handlers.AllActiveResource) + resource.Post("/list", handlers.PageResource) resource.Post("/list/short", handlers.PageResourceShort) resource.Post("/list/long", handlers.PageResourceLong) resource.Post("/create", handlers.CreateResource) @@ -204,6 +205,8 @@ func adminRouter(api fiber.Router) { // resource 套餐 var resource = api.Group("/resource") + resource.Post("/page", handlers.PageResourceByAdmin) + resource.Post("/page/of-user", handlers.PageResourceOfUserByAdmin) resource.Post("/short/page", handlers.PageResourceShortByAdmin) resource.Post("/short/page/of-user", handlers.PageResourceShortOfUserByAdmin) resource.Post("/long/page", handlers.PageResourceLongByAdmin) diff --git a/web/services/trade.go b/web/services/trade.go index 7cbb8ee..5283931 100644 --- a/web/services/trade.go +++ b/web/services/trade.go @@ -646,10 +646,7 @@ func (s *tradeService) CheckTrade(ref *TradeRef) (*CheckTradeResult, error) { result.Success.Acquirer = m.TradeAcquirerUnionPay } result.Success.Actual = decimal.NewFromInt(resp.Amount).Div(decimal.NewFromInt(100)) - result.Success.Time, err = time.Parse("2006-01-02 15:04:05", *resp.PayTime) - if err != nil { - return nil, err - } + result.Success.Time = time.UnixMilli(*resp.PayTime) } // 不支持的支付方式