chore: update auth and portal

This commit is contained in:
2026-01-14 11:29:17 +08:00
parent fb0a1c2f84
commit 3bcee7efc2
42 changed files with 5969 additions and 3014 deletions

View File

@@ -64,9 +64,6 @@ func (m *Middlewares) authenticate(ctx fiber.Ctx, requireToken bool) error {
if user.Status == consts.UserStatusBanned {
return errorx.ErrAccountDisabled
}
if user.Status == consts.UserStatusBanned {
return errorx.ErrAccountDisabled
}
// Set Context
ctx.Locals(consts.CtxKeyUser, user)
@@ -104,6 +101,9 @@ func (m *Middlewares) SuperAuth(ctx fiber.Ctx) error {
if err != nil {
return errorx.ErrUnauthorized.WithCause(err).WithMsg("UserNotFound")
}
if user.Status == consts.UserStatusBanned {
return errorx.ErrAccountDisabled
}
if !hasRole(user.Roles, consts.RoleSuperAdmin) {
return errorx.ErrForbidden.WithMsg("无权限访问")

View File

@@ -27,12 +27,22 @@ type super struct {
}
func (s *super) Login(ctx context.Context, form *super_dto.LoginForm) (*super_dto.LoginResponse, error) {
tbl, q := models.UserQuery.QueryContext(ctx)
u, err := q.Where(tbl.Username.Eq(form.Username)).First()
if err != nil {
return nil, errorx.ErrInvalidCredentials.WithMsg("账号或密码错误")
if form == nil {
return nil, errorx.ErrInvalidParameter.WithMsg("登录参数不能为空")
}
if u.Password != form.Password {
username := strings.TrimSpace(form.Username)
password := strings.TrimSpace(form.Password)
if username == "" || password == "" {
return nil, errorx.ErrInvalidParameter.WithMsg("账号或密码不能为空")
}
// 校验账号与权限。
tbl, q := models.UserQuery.QueryContext(ctx)
u, err := q.Where(tbl.Username.Eq(username)).First()
if err != nil {
return nil, errorx.ErrInvalidCredentials.WithCause(err).WithMsg("账号或密码错误")
}
if u.Password != password {
return nil, errorx.ErrInvalidCredentials.WithMsg("账号或密码错误")
}
if u.Status == consts.UserStatusBanned {
@@ -42,11 +52,12 @@ func (s *super) Login(ctx context.Context, form *super_dto.LoginForm) (*super_dt
return nil, errorx.ErrForbidden.WithMsg("无权限访问")
}
// 生成登录令牌。
token, err := s.jwt.CreateToken(s.jwt.CreateClaims(jwt_provider.BaseClaims{
UserID: u.ID,
}))
if err != nil {
return nil, errorx.ErrInternalError.WithMsg("生成令牌失败")
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("生成令牌失败")
}
return &super_dto.LoginResponse{
@@ -68,7 +79,10 @@ func (s *super) CheckToken(ctx context.Context, token string) (*super_dto.LoginR
tbl, q := models.UserQuery.QueryContext(ctx)
u, err := q.Where(tbl.ID.Eq(claims.UserID)).First()
if err != nil {
return nil, errorx.ErrUnauthorized.WithMsg("UserNotFound")
return nil, errorx.ErrUnauthorized.WithCause(err).WithMsg("UserNotFound")
}
if u.Status == consts.UserStatusBanned {
return nil, errorx.ErrAccountDisabled
}
if !hasRole(u.Roles, consts.RoleSuperAdmin) {
return nil, errorx.ErrForbidden.WithMsg("无权限访问")
@@ -78,7 +92,7 @@ func (s *super) CheckToken(ctx context.Context, token string) (*super_dto.LoginR
UserID: u.ID,
}))
if err != nil {
return nil, errorx.ErrInternalError.WithMsg("生成令牌失败")
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("生成令牌失败")
}
return &super_dto.LoginResponse{

View File

@@ -16,6 +16,7 @@ import (
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/suite"
"go.ipao.vip/atom/contracts"
"go.ipao.vip/gen/types"
"go.uber.org/dig"
)
@@ -74,6 +75,60 @@ func (s *SuperTestSuite) Test_ListUsers() {
})
}
func (s *SuperTestSuite) Test_LoginAndCheckToken() {
Convey("Login and CheckToken", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
admin := &models.User{
Username: "super_admin",
Password: "pass123",
Roles: types.Array[consts.Role]{consts.RoleSuperAdmin},
Status: consts.UserStatusVerified,
}
normal := &models.User{
Username: "normal_user",
Password: "pass123",
Status: consts.UserStatusVerified,
}
models.UserQuery.WithContext(ctx).Create(admin, normal)
Convey("should login as super admin", func() {
res, err := Super.Login(ctx, &super_dto.LoginForm{
Username: admin.Username,
Password: admin.Password,
})
So(err, ShouldBeNil)
So(res, ShouldNotBeNil)
So(res.Token, ShouldNotBeBlank)
So(res.User.ID, ShouldEqual, admin.ID)
})
Convey("should reject non-super admin", func() {
_, err := Super.Login(ctx, &super_dto.LoginForm{
Username: normal.Username,
Password: normal.Password,
})
So(err, ShouldNotBeNil)
})
Convey("should refresh token", func() {
loginRes, err := Super.Login(ctx, &super_dto.LoginForm{
Username: admin.Username,
Password: admin.Password,
})
So(err, ShouldBeNil)
token := "Bearer " + loginRes.Token
checkRes, err := Super.CheckToken(ctx, token)
So(err, ShouldBeNil)
So(checkRes, ShouldNotBeNil)
So(checkRes.Token, ShouldNotBeBlank)
So(checkRes.User.ID, ShouldEqual, admin.ID)
})
})
}
func (s *SuperTestSuite) Test_CreateTenant() {
Convey("CreateTenant", s.T(), func() {
ctx := s.T().Context()

View File

@@ -1708,6 +1708,208 @@ const docTemplate = `{
}
}
},
"/t/{tenantCode}/v1/creator/coupons": {
"get": {
"description": "List coupon templates",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "List coupons",
"parameters": [
{
"type": "integer",
"description": "Page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Limit",
"name": "limit",
"in": "query"
},
{
"type": "string",
"description": "Type (fix_amount/discount)",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Status (active/expired)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Keyword",
"name": "keyword",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/requests.Pager"
}
}
}
},
"post": {
"description": "Create coupon template",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Create coupon",
"parameters": [
{
"description": "Coupon form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponCreateForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/coupons/{id}": {
"get": {
"description": "Get coupon template detail",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Get coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
},
"put": {
"description": "Update coupon template",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Update coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Coupon form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponUpdateForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/coupons/{id}/grant": {
"post": {
"description": "Grant coupon to users",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Grant coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Grant form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponGrantForm"
}
}
],
"responses": {
"200": {
"description": "Granted",
"schema": {
"type": "string"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/dashboard": {
"get": {
"description": "Get creator dashboard stats",
@@ -2278,6 +2480,76 @@ const docTemplate = `{
}
}
},
"/t/{tenantCode}/v1/me/coupons/available": {
"get": {
"description": "List coupons available for the given order amount",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"UserCenter"
],
"summary": "List available coupons",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Order amount (cents)",
"name": "amount",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.UserCouponItem"
}
}
}
}
}
},
"/t/{tenantCode}/v1/me/coupons/receive": {
"post": {
"description": "Receive a coupon by coupon_id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"UserCenter"
],
"summary": "Receive coupon",
"parameters": [
{
"description": "Receive form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponReceiveForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.UserCouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/me/favorites": {
"get": {
"description": "Get favorites",
@@ -4187,6 +4459,166 @@ const docTemplate = `{
}
}
},
"dto.CouponCreateForm": {
"type": "object",
"properties": {
"description": {
"description": "Description 优惠券描述。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339可为空。",
"type": "string"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339可为空。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量0 表示不限量)。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.CouponGrantForm": {
"type": "object",
"properties": {
"user_ids": {
"description": "UserIDs 领取用户ID集合。",
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"dto.CouponItem": {
"type": "object",
"properties": {
"created_at": {
"description": "CreatedAt 创建时间RFC3339。",
"type": "string"
},
"description": {
"description": "Description 优惠券描述。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339。",
"type": "string"
},
"id": {
"description": "ID 券模板ID。",
"type": "integer"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"updated_at": {
"description": "UpdatedAt 更新时间RFC3339。",
"type": "string"
},
"used_quantity": {
"description": "UsedQuantity 已使用数量。",
"type": "integer"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.CouponReceiveForm": {
"type": "object",
"properties": {
"coupon_id": {
"description": "CouponID 券模板ID。",
"type": "integer"
}
}
},
"dto.CouponUpdateForm": {
"type": "object",
"properties": {
"description": {
"description": "Description 优惠券描述(为空表示不修改)。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339可为空。",
"type": "string"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339可为空。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题(为空表示不修改)。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量0 表示不限量)。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.DashboardStats": {
"type": "object",
"properties": {

View File

@@ -1702,6 +1702,208 @@
}
}
},
"/t/{tenantCode}/v1/creator/coupons": {
"get": {
"description": "List coupon templates",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "List coupons",
"parameters": [
{
"type": "integer",
"description": "Page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Limit",
"name": "limit",
"in": "query"
},
{
"type": "string",
"description": "Type (fix_amount/discount)",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Status (active/expired)",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Keyword",
"name": "keyword",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/requests.Pager"
}
}
}
},
"post": {
"description": "Create coupon template",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Create coupon",
"parameters": [
{
"description": "Coupon form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponCreateForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/coupons/{id}": {
"get": {
"description": "Get coupon template detail",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Get coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
},
"put": {
"description": "Update coupon template",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Update coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Coupon form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponUpdateForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.CouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/coupons/{id}/grant": {
"post": {
"description": "Grant coupon to users",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CreatorCenter"
],
"summary": "Grant coupon",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Coupon ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Grant form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponGrantForm"
}
}
],
"responses": {
"200": {
"description": "Granted",
"schema": {
"type": "string"
}
}
}
}
},
"/t/{tenantCode}/v1/creator/dashboard": {
"get": {
"description": "Get creator dashboard stats",
@@ -2272,6 +2474,76 @@
}
}
},
"/t/{tenantCode}/v1/me/coupons/available": {
"get": {
"description": "List coupons available for the given order amount",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"UserCenter"
],
"summary": "List available coupons",
"parameters": [
{
"type": "integer",
"format": "int64",
"description": "Order amount (cents)",
"name": "amount",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.UserCouponItem"
}
}
}
}
}
},
"/t/{tenantCode}/v1/me/coupons/receive": {
"post": {
"description": "Receive a coupon by coupon_id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"UserCenter"
],
"summary": "Receive coupon",
"parameters": [
{
"description": "Receive form",
"name": "form",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.CouponReceiveForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.UserCouponItem"
}
}
}
}
},
"/t/{tenantCode}/v1/me/favorites": {
"get": {
"description": "Get favorites",
@@ -4181,6 +4453,166 @@
}
}
},
"dto.CouponCreateForm": {
"type": "object",
"properties": {
"description": {
"description": "Description 优惠券描述。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339可为空。",
"type": "string"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339可为空。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量0 表示不限量)。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.CouponGrantForm": {
"type": "object",
"properties": {
"user_ids": {
"description": "UserIDs 领取用户ID集合。",
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"dto.CouponItem": {
"type": "object",
"properties": {
"created_at": {
"description": "CreatedAt 创建时间RFC3339。",
"type": "string"
},
"description": {
"description": "Description 优惠券描述。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339。",
"type": "string"
},
"id": {
"description": "ID 券模板ID。",
"type": "integer"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"updated_at": {
"description": "UpdatedAt 更新时间RFC3339。",
"type": "string"
},
"used_quantity": {
"description": "UsedQuantity 已使用数量。",
"type": "integer"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.CouponReceiveForm": {
"type": "object",
"properties": {
"coupon_id": {
"description": "CouponID 券模板ID。",
"type": "integer"
}
}
},
"dto.CouponUpdateForm": {
"type": "object",
"properties": {
"description": {
"description": "Description 优惠券描述(为空表示不修改)。",
"type": "string"
},
"end_at": {
"description": "EndAt 过期时间RFC3339可为空。",
"type": "string"
},
"max_discount": {
"description": "MaxDiscount 折扣券最高抵扣金额(分)。",
"type": "integer"
},
"min_order_amount": {
"description": "MinOrderAmount 使用门槛金额(分)。",
"type": "integer"
},
"start_at": {
"description": "StartAt 生效时间RFC3339可为空。",
"type": "string"
},
"title": {
"description": "Title 优惠券标题(为空表示不修改)。",
"type": "string"
},
"total_quantity": {
"description": "TotalQuantity 发行总量0 表示不限量)。",
"type": "integer"
},
"type": {
"description": "Type 优惠券类型fix_amount/discount。",
"type": "string"
},
"value": {
"description": "Value 优惠券面值(分/折扣百分比)。",
"type": "integer"
}
}
},
"dto.DashboardStats": {
"type": "object",
"properties": {

View File

@@ -472,6 +472,122 @@ definitions:
description: Title 内容标题(为空表示不修改)。
type: string
type: object
dto.CouponCreateForm:
properties:
description:
description: Description 优惠券描述。
type: string
end_at:
description: EndAt 过期时间RFC3339可为空
type: string
max_discount:
description: MaxDiscount 折扣券最高抵扣金额(分)。
type: integer
min_order_amount:
description: MinOrderAmount 使用门槛金额(分)。
type: integer
start_at:
description: StartAt 生效时间RFC3339可为空
type: string
title:
description: Title 优惠券标题。
type: string
total_quantity:
description: TotalQuantity 发行总量0 表示不限量)。
type: integer
type:
description: Type 优惠券类型fix_amount/discount
type: string
value:
description: Value 优惠券面值(分/折扣百分比)。
type: integer
type: object
dto.CouponGrantForm:
properties:
user_ids:
description: UserIDs 领取用户ID集合。
items:
type: integer
type: array
type: object
dto.CouponItem:
properties:
created_at:
description: CreatedAt 创建时间RFC3339
type: string
description:
description: Description 优惠券描述。
type: string
end_at:
description: EndAt 过期时间RFC3339
type: string
id:
description: ID 券模板ID。
type: integer
max_discount:
description: MaxDiscount 折扣券最高抵扣金额(分)。
type: integer
min_order_amount:
description: MinOrderAmount 使用门槛金额(分)。
type: integer
start_at:
description: StartAt 生效时间RFC3339
type: string
title:
description: Title 优惠券标题。
type: string
total_quantity:
description: TotalQuantity 发行总量。
type: integer
type:
description: Type 优惠券类型fix_amount/discount
type: string
updated_at:
description: UpdatedAt 更新时间RFC3339
type: string
used_quantity:
description: UsedQuantity 已使用数量。
type: integer
value:
description: Value 优惠券面值(分/折扣百分比)。
type: integer
type: object
dto.CouponReceiveForm:
properties:
coupon_id:
description: CouponID 券模板ID。
type: integer
type: object
dto.CouponUpdateForm:
properties:
description:
description: Description 优惠券描述(为空表示不修改)。
type: string
end_at:
description: EndAt 过期时间RFC3339可为空
type: string
max_discount:
description: MaxDiscount 折扣券最高抵扣金额(分)。
type: integer
min_order_amount:
description: MinOrderAmount 使用门槛金额(分)。
type: integer
start_at:
description: StartAt 生效时间RFC3339可为空
type: string
title:
description: Title 优惠券标题(为空表示不修改)。
type: string
total_quantity:
description: TotalQuantity 发行总量0 表示不限量)。
type: integer
type:
description: Type 优惠券类型fix_amount/discount
type: string
value:
description: Value 优惠券面值(分/折扣百分比)。
type: integer
type: object
dto.DashboardStats:
properties:
new_messages:
@@ -2916,6 +3032,140 @@ paths:
summary: Update content
tags:
- CreatorCenter
/t/{tenantCode}/v1/creator/coupons:
get:
consumes:
- application/json
description: List coupon templates
parameters:
- description: Page
in: query
name: page
type: integer
- description: Limit
in: query
name: limit
type: integer
- description: Type (fix_amount/discount)
in: query
name: type
type: string
- description: Status (active/expired)
in: query
name: status
type: string
- description: Keyword
in: query
name: keyword
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/requests.Pager'
summary: List coupons
tags:
- CreatorCenter
post:
consumes:
- application/json
description: Create coupon template
parameters:
- description: Coupon form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.CouponCreateForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.CouponItem'
summary: Create coupon
tags:
- CreatorCenter
/t/{tenantCode}/v1/creator/coupons/{id}:
get:
consumes:
- application/json
description: Get coupon template detail
parameters:
- description: Coupon ID
format: int64
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.CouponItem'
summary: Get coupon
tags:
- CreatorCenter
put:
consumes:
- application/json
description: Update coupon template
parameters:
- description: Coupon ID
format: int64
in: path
name: id
required: true
type: integer
- description: Coupon form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.CouponUpdateForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.CouponItem'
summary: Update coupon
tags:
- CreatorCenter
/t/{tenantCode}/v1/creator/coupons/{id}/grant:
post:
consumes:
- application/json
description: Grant coupon to users
parameters:
- description: Coupon ID
format: int64
in: path
name: id
required: true
type: integer
- description: Grant form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.CouponGrantForm'
produces:
- application/json
responses:
"200":
description: Granted
schema:
type: string
summary: Grant coupon
tags:
- CreatorCenter
/t/{tenantCode}/v1/creator/dashboard:
get:
consumes:
@@ -3290,6 +3540,52 @@ paths:
summary: List coupons
tags:
- UserCenter
/t/{tenantCode}/v1/me/coupons/available:
get:
consumes:
- application/json
description: List coupons available for the given order amount
parameters:
- description: Order amount (cents)
format: int64
in: query
name: amount
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/dto.UserCouponItem'
type: array
summary: List available coupons
tags:
- UserCenter
/t/{tenantCode}/v1/me/coupons/receive:
post:
consumes:
- application/json
description: Receive a coupon by coupon_id
parameters:
- description: Receive form
in: body
name: form
required: true
schema:
$ref: '#/definitions/dto.CouponReceiveForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.UserCouponItem'
summary: Receive coupon
tags:
- UserCenter
/t/{tenantCode}/v1/me/favorites:
get:
consumes: