From dbfb08ed37d92ccda4f5e4849d7a15de8d183d58 Mon Sep 17 00:00:00 2001 From: Rogee Date: Tue, 30 Dec 2025 17:28:21 +0800 Subject: [PATCH] feat: add coupon support to orders and create user_coupons model - Added CouponID field to Order model to track used coupons. - Updated order query generation to include CouponID. - Introduced UserCoupon model to manage user coupon associations. - Implemented query methods for UserCoupon to facilitate CRUD operations. - Updated query context and default query setup to include UserCoupon. --- backend/app/http/v1/dto/order.go | 7 +- backend/app/services/coupon.go | 84 +++ backend/app/services/coupon_test.go | 111 ++++ backend/app/services/order.go | 74 ++- backend/app/services/provider.gen.go | 13 +- backend/app/services/services.gen.go | 12 +- .../20251230091905_create_coupons.sql | 46 ++ backend/database/models/coupons.gen.go | 64 +++ backend/database/models/coupons.query.gen.go | 511 ++++++++++++++++++ backend/database/models/orders.gen.go | 1 + backend/database/models/orders.query.gen.go | 6 +- backend/database/models/query.gen.go | 16 + backend/database/models/user_coupons.gen.go | 59 ++ .../database/models/user_coupons.query.gen.go | 485 +++++++++++++++++ 14 files changed, 1454 insertions(+), 35 deletions(-) create mode 100644 backend/app/services/coupon.go create mode 100644 backend/app/services/coupon_test.go create mode 100644 backend/database/migrations/20251230091905_create_coupons.sql create mode 100644 backend/database/models/coupons.gen.go create mode 100644 backend/database/models/coupons.query.gen.go create mode 100644 backend/database/models/user_coupons.gen.go create mode 100644 backend/database/models/user_coupons.query.gen.go diff --git a/backend/app/http/v1/dto/order.go b/backend/app/http/v1/dto/order.go index bc10a95..cf0b9ec 100644 --- a/backend/app/http/v1/dto/order.go +++ b/backend/app/http/v1/dto/order.go @@ -1,9 +1,10 @@ package dto type OrderCreateForm struct { - ContentID string `json:"content_id"` - Sku string `json:"sku"` - Quantity int `json:"quantity"` + ContentID string `json:"content_id"` + Sku string `json:"sku"` + Quantity int `json:"quantity"` + UserCouponID string `json:"user_coupon_id"` } type OrderCreateResponse struct { diff --git a/backend/app/services/coupon.go b/backend/app/services/coupon.go new file mode 100644 index 0000000..b3ef7b4 --- /dev/null +++ b/backend/app/services/coupon.go @@ -0,0 +1,84 @@ +package services + +import ( + "context" + "time" + + "quyun/v2/app/errorx" + "quyun/v2/database/models" +) + +// @provider +type coupon struct{} + +// Validate checks if a coupon can be used for an order and returns the discount amount +func (s *coupon) Validate(ctx context.Context, userID, userCouponID, amount int64) (int64, error) { + uc, err := models.UserCouponQuery.WithContext(ctx).Where(models.UserCouponQuery.ID.Eq(userCouponID)).First() + if err != nil { + return 0, errorx.ErrRecordNotFound.WithMsg("优惠券不存在") + } + if uc.UserID != userID { + return 0, errorx.ErrUnauthorized.WithMsg("无权使用该优惠券") + } + if uc.Status != "unused" { + return 0, errorx.ErrBusinessLogic.WithMsg("优惠券已使用或失效") + } + + c, err := models.CouponQuery.WithContext(ctx).Where(models.CouponQuery.ID.Eq(uc.CouponID)).First() + if err != nil { + return 0, errorx.ErrRecordNotFound.WithMsg("优惠券信息缺失") + } + + now := time.Now() + if !c.StartAt.IsZero() && now.Before(c.StartAt) { + return 0, errorx.ErrBusinessLogic.WithMsg("优惠券尚未生效") + } + if !c.EndAt.IsZero() && now.After(c.EndAt) { + return 0, errorx.ErrBusinessLogic.WithMsg("优惠券已过期") + } + + if amount < c.MinOrderAmount { + return 0, errorx.ErrBusinessLogic.WithMsg("未达到优惠券使用门槛") + } + + var discount int64 + if c.Type == "fix_amount" { + discount = c.Value + } else if c.Type == "discount" { + discount = (amount * c.Value) / 100 + if c.MaxDiscount > 0 && discount > c.MaxDiscount { + discount = c.MaxDiscount + } + } + + // Discount cannot exceed order amount + if discount > amount { + discount = amount + } + + return discount, nil +} + +// MarkUsed marks a user coupon as used (intended to be called inside a transaction) +func (s *coupon) MarkUsed(ctx context.Context, tx *models.Query, userCouponID, orderID int64) error { + now := time.Now() + // Update User Coupon + info, err := tx.UserCoupon.WithContext(ctx).Where(tx.UserCoupon.ID.Eq(userCouponID), tx.UserCoupon.Status.Eq("unused")).Updates(&models.UserCoupon{ + Status: "used", + OrderID: orderID, + UsedAt: now, + }) + if err != nil { + return err + } + if info.RowsAffected == 0 { + return errorx.ErrBusinessLogic.WithMsg("优惠券核销失败") + } + + // Update Coupon used quantity (Optional, but good for stats) + // We need CouponID from uc + uc, _ := tx.UserCoupon.WithContext(ctx).Where(tx.UserCoupon.ID.Eq(userCouponID)).First() + _, _ = tx.Coupon.WithContext(ctx).Where(tx.Coupon.ID.Eq(uc.CouponID)).UpdateSimple(tx.Coupon.UsedQuantity.Add(1)) + + return nil +} diff --git a/backend/app/services/coupon_test.go b/backend/app/services/coupon_test.go new file mode 100644 index 0000000..1adc572 --- /dev/null +++ b/backend/app/services/coupon_test.go @@ -0,0 +1,111 @@ +package services + +import ( + "context" + "database/sql" + "testing" + + "quyun/v2/app/commands/testx" + order_dto "quyun/v2/app/http/v1/dto" + "quyun/v2/database" + "quyun/v2/database/models" + "quyun/v2/pkg/consts" + "quyun/v2/providers/storage" + + . "github.com/smartystreets/goconvey/convey" + "github.com/spf13/cast" + "github.com/stretchr/testify/suite" + "go.ipao.vip/atom/contracts" + "go.uber.org/dig" +) + +type CouponTestSuiteInjectParams struct { + dig.In + + DB *sql.DB + Initials []contracts.Initial `group:"initials"` +} + +type CouponTestSuite struct { + suite.Suite + CouponTestSuiteInjectParams +} + +func Test_Coupon(t *testing.T) { + providers := testx.Default().With(Provide).With(storage.Provide) + + testx.Serve(providers, t, func(p CouponTestSuiteInjectParams) { + suite.Run(t, &CouponTestSuite{CouponTestSuiteInjectParams: p}) + }) +} + +func (s *CouponTestSuite) Test_CouponFlow() { + Convey("Coupon Flow", s.T(), func() { + ctx := s.T().Context() + database.Truncate(ctx, s.DB, models.TableNameCoupon, models.TableNameUserCoupon, models.TableNameOrder, models.TableNameUser, models.TableNameContent, models.TableNameContentPrice) + + user := &models.User{Username: "coupon_user", Phone: "13800000001"} + models.UserQuery.WithContext(ctx).Create(user) + + // 1. Create Coupon (Fixed 5.00 CNY, Min 10.00 CNY) + cp := &models.Coupon{ + Title: "Save 5", + Type: "fix_amount", + Value: 500, + MinOrderAmount: 1000, + } + models.CouponQuery.WithContext(ctx).Create(cp) + + // 2. Give to User + uc := &models.UserCoupon{ + UserID: user.ID, + CouponID: cp.ID, + Status: "unused", + } + models.UserCouponQuery.WithContext(ctx).Create(uc) + + Convey("should validate coupon successfully", func() { + discount, err := Coupon.Validate(ctx, user.ID, uc.ID, 1500) + So(err, ShouldBeNil) + So(discount, ShouldEqual, 500) + }) + + Convey("should fail if below min amount", func() { + _, err := Coupon.Validate(ctx, user.ID, uc.ID, 800) + So(err, ShouldNotBeNil) + }) + + Convey("should apply in Order.Create", func() { + // Setup Content + c := &models.Content{UserID: 99, Title: "Test", Status: consts.ContentStatusPublished} + models.ContentQuery.WithContext(ctx).Create(c) + models.ContentPriceQuery.WithContext(ctx).Create(&models.ContentPrice{ + ContentID: c.ID, + PriceAmount: 2000, // 20.00 CNY + Currency: "CNY", + }) + + form := &order_dto.OrderCreateForm{ + ContentID: cast.ToString(c.ID), + UserCouponID: cast.ToString(uc.ID), + } + // Simulate Auth context for Order service + authCtx := context.WithValue(ctx, consts.CtxKeyUser, user.ID) + res, err := Order.Create(authCtx, form) + So(err, ShouldBeNil) + + // Verify Order + oid := cast.ToInt64(res.OrderID) + o, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(oid)).First() + So(o.AmountOriginal, ShouldEqual, 2000) + So(o.AmountDiscount, ShouldEqual, 500) + So(o.AmountPaid, ShouldEqual, 1500) + So(o.CouponID, ShouldEqual, cp.ID) + + // Verify Coupon Status + ucReload, _ := models.UserCouponQuery.WithContext(ctx).Where(models.UserCouponQuery.ID.Eq(uc.ID)).First() + So(ucReload.Status, ShouldEqual, "used") + So(ucReload.OrderID, ShouldEqual, oid) + }) + }) +} \ No newline at end of file diff --git a/backend/app/services/order.go b/backend/app/services/order.go index 5c5dadf..e978e2d 100644 --- a/backend/app/services/order.go +++ b/backend/app/services/order.go @@ -91,11 +91,30 @@ func (s *order) Create(ctx context.Context, form *transaction_dto.OrderCreateFor price, err := models.ContentPriceQuery.WithContext(ctx).Where(models.ContentPriceQuery.ContentID.Eq(cid)).First() if err != nil { - // If price missing, treat as error? Or maybe 0? - // Better to require price record. return nil, errorx.ErrDataCorrupted.WithCause(err).WithMsg("价格信息缺失") } + amountOriginal := price.PriceAmount + var amountDiscount int64 = 0 + var couponID int64 = 0 + + // Validate Coupon + if form.UserCouponID != "" { + ucid := cast.ToInt64(form.UserCouponID) + discount, err := Coupon.Validate(ctx, uid, ucid, amountOriginal) + if err != nil { + return nil, err + } + amountDiscount = discount + + uc, err := models.UserCouponQuery.WithContext(ctx).Where(models.UserCouponQuery.ID.Eq(ucid)).First() + if err == nil { + couponID = uc.CouponID + } + } + + amountPaid := amountOriginal - amountDiscount + // 2. Create Order (Status: Created) order := &models.Order{ TenantID: content.TenantID, @@ -103,27 +122,46 @@ func (s *order) Create(ctx context.Context, form *transaction_dto.OrderCreateFor Type: consts.OrderTypeContentPurchase, Status: consts.OrderStatusCreated, Currency: consts.Currency(price.Currency), - AmountOriginal: price.PriceAmount, - AmountDiscount: 0, - AmountPaid: price.PriceAmount, + AmountOriginal: amountOriginal, + AmountDiscount: amountDiscount, + AmountPaid: amountPaid, + CouponID: couponID, IdempotencyKey: uuid.NewString(), Snapshot: types.NewJSONType(fields.OrdersSnapshot{}), } - if err := models.OrderQuery.WithContext(ctx).Create(order); err != nil { - return nil, errorx.ErrDatabaseError.WithCause(err) - } + err = models.Q.Transaction(func(tx *models.Query) error { + if err := tx.Order.WithContext(ctx).Create(order); err != nil { + return err + } - // 3. Create Order Item - item := &models.OrderItem{ - TenantID: content.TenantID, - UserID: uid, - OrderID: order.ID, - ContentID: cid, - ContentUserID: content.UserID, - AmountPaid: order.AmountPaid, - } - if err := models.OrderItemQuery.WithContext(ctx).Create(item); err != nil { + // 3. Create Order Item + item := &models.OrderItem{ + TenantID: content.TenantID, + UserID: uid, + OrderID: order.ID, + ContentID: cid, + ContentUserID: content.UserID, + AmountPaid: amountPaid, + } + if err := tx.OrderItem.WithContext(ctx).Create(item); err != nil { + return err + } + + // Mark Coupon Used + if form.UserCouponID != "" { + if err := Coupon.MarkUsed(ctx, tx, cast.ToInt64(form.UserCouponID), order.ID); err != nil { + return err + } + } + + return nil + }) + + if err != nil { + if _, ok := err.(*errorx.AppError); ok { + return nil, err + } return nil, errorx.ErrDatabaseError.WithCause(err) } diff --git a/backend/app/services/provider.gen.go b/backend/app/services/provider.gen.go index d9c5620..5874d9c 100755 --- a/backend/app/services/provider.gen.go +++ b/backend/app/services/provider.gen.go @@ -38,6 +38,13 @@ func Provide(opts ...opt.Option) error { }); err != nil { return err } + if err := container.Container.Provide(func() (*coupon, error) { + obj := &coupon{} + + return obj, nil + }); err != nil { + return err + } if err := container.Container.Provide(func() (*creator, error) { obj := &creator{} @@ -67,12 +74,11 @@ func Provide(opts ...opt.Option) error { audit *audit, common *common, content *content, + coupon *coupon, creator *creator, db *gorm.DB, - job *job.Job, notification *notification, order *order, - storage *storage.Storage, super *super, tenant *tenant, user *user, @@ -82,12 +88,11 @@ func Provide(opts ...opt.Option) error { audit: audit, common: common, content: content, + coupon: coupon, creator: creator, db: db, - job: job, notification: notification, order: order, - storage: storage, super: super, tenant: tenant, user: user, diff --git a/backend/app/services/services.gen.go b/backend/app/services/services.gen.go index 8b8850c..236c408 100644 --- a/backend/app/services/services.gen.go +++ b/backend/app/services/services.gen.go @@ -1,9 +1,6 @@ package services import ( - "quyun/v2/providers/job" - "quyun/v2/providers/storage" - "gorm.io/gorm" ) @@ -14,11 +11,10 @@ var ( Audit *audit Common *common Content *content + Coupon *coupon Creator *creator - Job *job.Job Notification *notification Order *order - Storage *storage.Storage Super *super Tenant *tenant User *user @@ -32,11 +28,10 @@ type services struct { audit *audit common *common content *content + coupon *coupon creator *creator - job *job.Job notification *notification order *order - storage *storage.Storage super *super tenant *tenant user *user @@ -50,11 +45,10 @@ func (svc *services) Prepare() error { Audit = svc.audit Common = svc.common Content = svc.content + Coupon = svc.coupon Creator = svc.creator - Job = svc.job Notification = svc.notification Order = svc.order - Storage = svc.storage Super = svc.super Tenant = svc.tenant User = svc.user diff --git a/backend/database/migrations/20251230091905_create_coupons.sql b/backend/database/migrations/20251230091905_create_coupons.sql new file mode 100644 index 0000000..8c3031c --- /dev/null +++ b/backend/database/migrations/20251230091905_create_coupons.sql @@ -0,0 +1,46 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE "coupons" ( + "id" BIGSERIAL PRIMARY KEY, + "tenant_id" BIGINT NOT NULL DEFAULT 0, -- 租户ID + "title" VARCHAR(255) NOT NULL, -- 优惠券标题 + "description" TEXT, -- 优惠券描述 + "type" VARCHAR(32) NOT NULL, -- 优惠券类型: fix_amount(固定金额), discount(折扣比率) + "value" BIGINT NOT NULL, -- 优惠券面值 (固定金额为分,折扣比率为0-100) + "min_order_amount" BIGINT NOT NULL DEFAULT 0, -- 最低订单金额门槛 (分) + "max_discount" BIGINT, -- 最高抵扣金额 (分,针对折扣券) + "total_quantity" INTEGER NOT NULL DEFAULT 0, -- 发行总量 + "used_quantity" INTEGER NOT NULL DEFAULT 0, -- 已使用数量 + "start_at" TIMESTAMPTZ, -- 开始生效时间 + "end_at" TIMESTAMPTZ, -- 过期失效时间 + "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "idx_coupons_tenant_id" ON "coupons" ("tenant_id"); + +CREATE TABLE "user_coupons" ( + "id" BIGSERIAL PRIMARY KEY, + "user_id" BIGINT NOT NULL, -- 用户ID + "coupon_id" BIGINT NOT NULL, -- 优惠券ID + "order_id" BIGINT, -- 使用该优惠券的订单ID + "status" VARCHAR(32) NOT NULL DEFAULT 'unused', -- 状态: unused(未使用), used(已使用), expired(已过期) + "used_at" TIMESTAMPTZ, -- 使用时间 + "created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "idx_user_coupons_user_id" ON "user_coupons" ("user_id"); +CREATE INDEX "idx_user_coupons_coupon_id" ON "user_coupons" ("coupon_id"); + +-- Add CouponID to Orders table +ALTER TABLE "orders" ADD COLUMN "coupon_id" BIGINT DEFAULT 0; -- 关联优惠券ID (0表示未使用) +COMMENT ON COLUMN "orders"."coupon_id" IS '使用的优惠券ID (0表示未使用)'; + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE "orders" DROP COLUMN IF EXISTS "coupon_id"; +DROP TABLE IF EXISTS "user_coupons"; +DROP TABLE IF EXISTS "coupons"; +-- +goose StatementEnd \ No newline at end of file diff --git a/backend/database/models/coupons.gen.go b/backend/database/models/coupons.gen.go new file mode 100644 index 0000000..6c020a8 --- /dev/null +++ b/backend/database/models/coupons.gen.go @@ -0,0 +1,64 @@ +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. + +package models + +import ( + "context" + "time" + + "go.ipao.vip/gen" +) + +const TableNameCoupon = "coupons" + +// Coupon mapped from table +type Coupon struct { + ID int64 `gorm:"column:id;type:bigint;primaryKey;autoIncrement:true" json:"id"` + TenantID int64 `gorm:"column:tenant_id;type:bigint;not null" json:"tenant_id"` + Title string `gorm:"column:title;type:character varying(255);not null" json:"title"` + Description string `gorm:"column:description;type:text" json:"description"` + Type string `gorm:"column:type;type:character varying(32);not null" json:"type"` + Value int64 `gorm:"column:value;type:bigint;not null" json:"value"` + MinOrderAmount int64 `gorm:"column:min_order_amount;type:bigint;not null" json:"min_order_amount"` + MaxDiscount int64 `gorm:"column:max_discount;type:bigint" json:"max_discount"` + TotalQuantity int32 `gorm:"column:total_quantity;type:integer;not null" json:"total_quantity"` + UsedQuantity int32 `gorm:"column:used_quantity;type:integer;not null" json:"used_quantity"` + StartAt time.Time `gorm:"column:start_at;type:timestamp with time zone" json:"start_at"` + EndAt time.Time `gorm:"column:end_at;type:timestamp with time zone" json:"end_at"` + CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;not null;default:now()" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp with time zone;not null;default:now()" json:"updated_at"` +} + +// Quick operations without importing query package +// Update applies changed fields to the database using the default DB. +func (m *Coupon) Update(ctx context.Context) (gen.ResultInfo, error) { + return Q.Coupon.WithContext(ctx).Updates(m) +} + +// Save upserts the model using the default DB. +func (m *Coupon) Save(ctx context.Context) error { return Q.Coupon.WithContext(ctx).Save(m) } + +// Create inserts the model using the default DB. +func (m *Coupon) Create(ctx context.Context) error { return Q.Coupon.WithContext(ctx).Create(m) } + +// Delete removes the row represented by the model using the default DB. +func (m *Coupon) Delete(ctx context.Context) (gen.ResultInfo, error) { + return Q.Coupon.WithContext(ctx).Delete(m) +} + +// ForceDelete permanently deletes the row (ignores soft delete) using the default DB. +func (m *Coupon) ForceDelete(ctx context.Context) (gen.ResultInfo, error) { + return Q.Coupon.WithContext(ctx).Unscoped().Delete(m) +} + +// Reload reloads the model from database by its primary key and overwrites current fields. +func (m *Coupon) Reload(ctx context.Context) error { + fresh, err := Q.Coupon.WithContext(ctx).GetByID(m.ID) + if err != nil { + return err + } + *m = *fresh + return nil +} diff --git a/backend/database/models/coupons.query.gen.go b/backend/database/models/coupons.query.gen.go new file mode 100644 index 0000000..842edaf --- /dev/null +++ b/backend/database/models/coupons.query.gen.go @@ -0,0 +1,511 @@ +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. + +package models + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "go.ipao.vip/gen" + "go.ipao.vip/gen/field" + + "gorm.io/plugin/dbresolver" +) + +func newCoupon(db *gorm.DB, opts ...gen.DOOption) couponQuery { + _couponQuery := couponQuery{} + + _couponQuery.couponQueryDo.UseDB(db, opts...) + _couponQuery.couponQueryDo.UseModel(&Coupon{}) + + tableName := _couponQuery.couponQueryDo.TableName() + _couponQuery.ALL = field.NewAsterisk(tableName) + _couponQuery.ID = field.NewInt64(tableName, "id") + _couponQuery.TenantID = field.NewInt64(tableName, "tenant_id") + _couponQuery.Title = field.NewString(tableName, "title") + _couponQuery.Description = field.NewString(tableName, "description") + _couponQuery.Type = field.NewString(tableName, "type") + _couponQuery.Value = field.NewInt64(tableName, "value") + _couponQuery.MinOrderAmount = field.NewInt64(tableName, "min_order_amount") + _couponQuery.MaxDiscount = field.NewInt64(tableName, "max_discount") + _couponQuery.TotalQuantity = field.NewInt32(tableName, "total_quantity") + _couponQuery.UsedQuantity = field.NewInt32(tableName, "used_quantity") + _couponQuery.StartAt = field.NewTime(tableName, "start_at") + _couponQuery.EndAt = field.NewTime(tableName, "end_at") + _couponQuery.CreatedAt = field.NewTime(tableName, "created_at") + _couponQuery.UpdatedAt = field.NewTime(tableName, "updated_at") + + _couponQuery.fillFieldMap() + + return _couponQuery +} + +type couponQuery struct { + couponQueryDo couponQueryDo + + ALL field.Asterisk + ID field.Int64 + TenantID field.Int64 + Title field.String + Description field.String + Type field.String + Value field.Int64 + MinOrderAmount field.Int64 + MaxDiscount field.Int64 + TotalQuantity field.Int32 + UsedQuantity field.Int32 + StartAt field.Time + EndAt field.Time + CreatedAt field.Time + UpdatedAt field.Time + + fieldMap map[string]field.Expr +} + +func (c couponQuery) Table(newTableName string) *couponQuery { + c.couponQueryDo.UseTable(newTableName) + return c.updateTableName(newTableName) +} + +func (c couponQuery) As(alias string) *couponQuery { + c.couponQueryDo.DO = *(c.couponQueryDo.As(alias).(*gen.DO)) + return c.updateTableName(alias) +} + +func (c *couponQuery) updateTableName(table string) *couponQuery { + c.ALL = field.NewAsterisk(table) + c.ID = field.NewInt64(table, "id") + c.TenantID = field.NewInt64(table, "tenant_id") + c.Title = field.NewString(table, "title") + c.Description = field.NewString(table, "description") + c.Type = field.NewString(table, "type") + c.Value = field.NewInt64(table, "value") + c.MinOrderAmount = field.NewInt64(table, "min_order_amount") + c.MaxDiscount = field.NewInt64(table, "max_discount") + c.TotalQuantity = field.NewInt32(table, "total_quantity") + c.UsedQuantity = field.NewInt32(table, "used_quantity") + c.StartAt = field.NewTime(table, "start_at") + c.EndAt = field.NewTime(table, "end_at") + c.CreatedAt = field.NewTime(table, "created_at") + c.UpdatedAt = field.NewTime(table, "updated_at") + + c.fillFieldMap() + + return c +} + +func (c *couponQuery) QueryContext(ctx context.Context) (*couponQuery, *couponQueryDo) { + return c, c.couponQueryDo.WithContext(ctx) +} + +func (c *couponQuery) WithContext(ctx context.Context) *couponQueryDo { + return c.couponQueryDo.WithContext(ctx) +} + +func (c couponQuery) TableName() string { return c.couponQueryDo.TableName() } + +func (c couponQuery) Alias() string { return c.couponQueryDo.Alias() } + +func (c couponQuery) Columns(cols ...field.Expr) gen.Columns { return c.couponQueryDo.Columns(cols...) } + +func (c *couponQuery) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := c.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (c *couponQuery) fillFieldMap() { + c.fieldMap = make(map[string]field.Expr, 14) + c.fieldMap["id"] = c.ID + c.fieldMap["tenant_id"] = c.TenantID + c.fieldMap["title"] = c.Title + c.fieldMap["description"] = c.Description + c.fieldMap["type"] = c.Type + c.fieldMap["value"] = c.Value + c.fieldMap["min_order_amount"] = c.MinOrderAmount + c.fieldMap["max_discount"] = c.MaxDiscount + c.fieldMap["total_quantity"] = c.TotalQuantity + c.fieldMap["used_quantity"] = c.UsedQuantity + c.fieldMap["start_at"] = c.StartAt + c.fieldMap["end_at"] = c.EndAt + c.fieldMap["created_at"] = c.CreatedAt + c.fieldMap["updated_at"] = c.UpdatedAt +} + +func (c couponQuery) clone(db *gorm.DB) couponQuery { + c.couponQueryDo.ReplaceConnPool(db.Statement.ConnPool) + return c +} + +func (c couponQuery) replaceDB(db *gorm.DB) couponQuery { + c.couponQueryDo.ReplaceDB(db) + return c +} + +type couponQueryDo struct{ gen.DO } + +func (c couponQueryDo) Debug() *couponQueryDo { + return c.withDO(c.DO.Debug()) +} + +func (c couponQueryDo) WithContext(ctx context.Context) *couponQueryDo { + return c.withDO(c.DO.WithContext(ctx)) +} + +func (c couponQueryDo) ReadDB() *couponQueryDo { + return c.Clauses(dbresolver.Read) +} + +func (c couponQueryDo) WriteDB() *couponQueryDo { + return c.Clauses(dbresolver.Write) +} + +func (c couponQueryDo) Session(config *gorm.Session) *couponQueryDo { + return c.withDO(c.DO.Session(config)) +} + +func (c couponQueryDo) Clauses(conds ...clause.Expression) *couponQueryDo { + return c.withDO(c.DO.Clauses(conds...)) +} + +func (c couponQueryDo) Returning(value interface{}, columns ...string) *couponQueryDo { + return c.withDO(c.DO.Returning(value, columns...)) +} + +func (c couponQueryDo) Not(conds ...gen.Condition) *couponQueryDo { + return c.withDO(c.DO.Not(conds...)) +} + +func (c couponQueryDo) Or(conds ...gen.Condition) *couponQueryDo { + return c.withDO(c.DO.Or(conds...)) +} + +func (c couponQueryDo) Select(conds ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Select(conds...)) +} + +func (c couponQueryDo) Where(conds ...gen.Condition) *couponQueryDo { + return c.withDO(c.DO.Where(conds...)) +} + +func (c couponQueryDo) Order(conds ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Order(conds...)) +} + +func (c couponQueryDo) Distinct(cols ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Distinct(cols...)) +} + +func (c couponQueryDo) Omit(cols ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Omit(cols...)) +} + +func (c couponQueryDo) Join(table schema.Tabler, on ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Join(table, on...)) +} + +func (c couponQueryDo) LeftJoin(table schema.Tabler, on ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.LeftJoin(table, on...)) +} + +func (c couponQueryDo) RightJoin(table schema.Tabler, on ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.RightJoin(table, on...)) +} + +func (c couponQueryDo) Group(cols ...field.Expr) *couponQueryDo { + return c.withDO(c.DO.Group(cols...)) +} + +func (c couponQueryDo) Having(conds ...gen.Condition) *couponQueryDo { + return c.withDO(c.DO.Having(conds...)) +} + +func (c couponQueryDo) Limit(limit int) *couponQueryDo { + return c.withDO(c.DO.Limit(limit)) +} + +func (c couponQueryDo) Offset(offset int) *couponQueryDo { + return c.withDO(c.DO.Offset(offset)) +} + +func (c couponQueryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *couponQueryDo { + return c.withDO(c.DO.Scopes(funcs...)) +} + +func (c couponQueryDo) Unscoped() *couponQueryDo { + return c.withDO(c.DO.Unscoped()) +} + +func (c couponQueryDo) Create(values ...*Coupon) error { + if len(values) == 0 { + return nil + } + return c.DO.Create(values) +} + +func (c couponQueryDo) CreateInBatches(values []*Coupon, batchSize int) error { + return c.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (c couponQueryDo) Save(values ...*Coupon) error { + if len(values) == 0 { + return nil + } + return c.DO.Save(values) +} + +func (c couponQueryDo) First() (*Coupon, error) { + if result, err := c.DO.First(); err != nil { + return nil, err + } else { + return result.(*Coupon), nil + } +} + +func (c couponQueryDo) Take() (*Coupon, error) { + if result, err := c.DO.Take(); err != nil { + return nil, err + } else { + return result.(*Coupon), nil + } +} + +func (c couponQueryDo) Last() (*Coupon, error) { + if result, err := c.DO.Last(); err != nil { + return nil, err + } else { + return result.(*Coupon), nil + } +} + +func (c couponQueryDo) Find() ([]*Coupon, error) { + result, err := c.DO.Find() + return result.([]*Coupon), err +} + +func (c couponQueryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*Coupon, err error) { + buf := make([]*Coupon, 0, batchSize) + err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (c couponQueryDo) FindInBatches(result *[]*Coupon, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return c.DO.FindInBatches(result, batchSize, fc) +} + +func (c couponQueryDo) Attrs(attrs ...field.AssignExpr) *couponQueryDo { + return c.withDO(c.DO.Attrs(attrs...)) +} + +func (c couponQueryDo) Assign(attrs ...field.AssignExpr) *couponQueryDo { + return c.withDO(c.DO.Assign(attrs...)) +} + +func (c couponQueryDo) Joins(fields ...field.RelationField) *couponQueryDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Joins(_f)) + } + return &c +} + +func (c couponQueryDo) Preload(fields ...field.RelationField) *couponQueryDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Preload(_f)) + } + return &c +} + +func (c couponQueryDo) FirstOrInit() (*Coupon, error) { + if result, err := c.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*Coupon), nil + } +} + +func (c couponQueryDo) FirstOrCreate() (*Coupon, error) { + if result, err := c.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*Coupon), nil + } +} + +func (c couponQueryDo) FindByPage(offset int, limit int) (result []*Coupon, count int64, err error) { + result, err = c.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = c.Offset(-1).Limit(-1).Count() + return +} + +func (c couponQueryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = c.Count() + if err != nil { + return + } + + err = c.Offset(offset).Limit(limit).Scan(result) + return +} + +func (c couponQueryDo) Scan(result interface{}) (err error) { + return c.DO.Scan(result) +} + +func (c couponQueryDo) Delete(models ...*Coupon) (result gen.ResultInfo, err error) { + return c.DO.Delete(models) +} + +// ForceDelete performs a permanent delete (ignores soft-delete) for current scope. +func (c couponQueryDo) ForceDelete() (gen.ResultInfo, error) { + return c.Unscoped().Delete() +} + +// Inc increases the given column by step for current scope. +func (c couponQueryDo) Inc(column field.Expr, step int64) (gen.ResultInfo, error) { + // column = column + step + e := field.NewUnsafeFieldRaw("?+?", column.RawExpr(), step) + return c.DO.UpdateColumn(column, e) +} + +// Dec decreases the given column by step for current scope. +func (c couponQueryDo) Dec(column field.Expr, step int64) (gen.ResultInfo, error) { + // column = column - step + e := field.NewUnsafeFieldRaw("?-?", column.RawExpr(), step) + return c.DO.UpdateColumn(column, e) +} + +// Sum returns SUM(column) for current scope. +func (c couponQueryDo) Sum(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("SUM(?)", column.RawExpr()) + if err := c.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Avg returns AVG(column) for current scope. +func (c couponQueryDo) Avg(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("AVG(?)", column.RawExpr()) + if err := c.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Min returns MIN(column) for current scope. +func (c couponQueryDo) Min(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("MIN(?)", column.RawExpr()) + if err := c.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Max returns MAX(column) for current scope. +func (c couponQueryDo) Max(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("MAX(?)", column.RawExpr()) + if err := c.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// PluckMap returns a map[key]value for selected key/value expressions within current scope. +func (c couponQueryDo) PluckMap(key, val field.Expr) (map[interface{}]interface{}, error) { + do := c.Select(key, val) + rows, err := do.DO.Rows() + if err != nil { + return nil, err + } + defer rows.Close() + mm := make(map[interface{}]interface{}) + for rows.Next() { + var k interface{} + var v interface{} + if err := rows.Scan(&k, &v); err != nil { + return nil, err + } + mm[k] = v + } + return mm, rows.Err() +} + +// Exists returns true if any record matches the given conditions. +func (c couponQueryDo) Exists(conds ...gen.Condition) (bool, error) { + cnt, err := c.Where(conds...).Count() + if err != nil { + return false, err + } + return cnt > 0, nil +} + +// PluckIDs returns all primary key values under current scope. +func (c couponQueryDo) PluckIDs() ([]int64, error) { + ids := make([]int64, 0, 16) + pk := field.NewInt64(c.TableName(), "id") + if err := c.DO.Pluck(pk, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// GetByID finds a single record by primary key. +func (c couponQueryDo) GetByID(id int64) (*Coupon, error) { + pk := field.NewInt64(c.TableName(), "id") + return c.Where(pk.Eq(id)).First() +} + +// GetByIDs finds records by primary key list. +func (c couponQueryDo) GetByIDs(ids ...int64) ([]*Coupon, error) { + if len(ids) == 0 { + return []*Coupon{}, nil + } + pk := field.NewInt64(c.TableName(), "id") + return c.Where(pk.In(ids...)).Find() +} + +// DeleteByID deletes records by primary key. +func (c couponQueryDo) DeleteByID(id int64) (gen.ResultInfo, error) { + pk := field.NewInt64(c.TableName(), "id") + return c.Where(pk.Eq(id)).Delete() +} + +// DeleteByIDs deletes records by a list of primary keys. +func (c couponQueryDo) DeleteByIDs(ids ...int64) (gen.ResultInfo, error) { + if len(ids) == 0 { + return gen.ResultInfo{RowsAffected: 0, Error: nil}, nil + } + pk := field.NewInt64(c.TableName(), "id") + return c.Where(pk.In(ids...)).Delete() +} + +func (c *couponQueryDo) withDO(do gen.Dao) *couponQueryDo { + c.DO = *do.(*gen.DO) + return c +} diff --git a/backend/database/models/orders.gen.go b/backend/database/models/orders.gen.go index 384b174..5c73b77 100644 --- a/backend/database/models/orders.gen.go +++ b/backend/database/models/orders.gen.go @@ -37,6 +37,7 @@ type Order struct { RefundReason string `gorm:"column:refund_reason;type:character varying(255)" json:"refund_reason"` CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;default:now()" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp with time zone;default:now()" json:"updated_at"` + CouponID int64 `gorm:"column:coupon_id;type:bigint;comment:使用的优惠券ID (0表示未使用)" json:"coupon_id"` // 使用的优惠券ID (0表示未使用) } // Quick operations without importing query package diff --git a/backend/database/models/orders.query.gen.go b/backend/database/models/orders.query.gen.go index d453628..a0da79c 100644 --- a/backend/database/models/orders.query.gen.go +++ b/backend/database/models/orders.query.gen.go @@ -43,6 +43,7 @@ func newOrder(db *gorm.DB, opts ...gen.DOOption) orderQuery { _orderQuery.RefundReason = field.NewString(tableName, "refund_reason") _orderQuery.CreatedAt = field.NewTime(tableName, "created_at") _orderQuery.UpdatedAt = field.NewTime(tableName, "updated_at") + _orderQuery.CouponID = field.NewInt64(tableName, "coupon_id") _orderQuery.fillFieldMap() @@ -71,6 +72,7 @@ type orderQuery struct { RefundReason field.String CreatedAt field.Time UpdatedAt field.Time + CouponID field.Int64 // 使用的优惠券ID (0表示未使用) fieldMap map[string]field.Expr } @@ -105,6 +107,7 @@ func (o *orderQuery) updateTableName(table string) *orderQuery { o.RefundReason = field.NewString(table, "refund_reason") o.CreatedAt = field.NewTime(table, "created_at") o.UpdatedAt = field.NewTime(table, "updated_at") + o.CouponID = field.NewInt64(table, "coupon_id") o.fillFieldMap() @@ -135,7 +138,7 @@ func (o *orderQuery) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (o *orderQuery) fillFieldMap() { - o.fieldMap = make(map[string]field.Expr, 18) + o.fieldMap = make(map[string]field.Expr, 19) o.fieldMap["id"] = o.ID o.fieldMap["tenant_id"] = o.TenantID o.fieldMap["user_id"] = o.UserID @@ -154,6 +157,7 @@ func (o *orderQuery) fillFieldMap() { o.fieldMap["refund_reason"] = o.RefundReason o.fieldMap["created_at"] = o.CreatedAt o.fieldMap["updated_at"] = o.UpdatedAt + o.fieldMap["coupon_id"] = o.CouponID } func (o orderQuery) clone(db *gorm.DB) orderQuery { diff --git a/backend/database/models/query.gen.go b/backend/database/models/query.gen.go index e3e8b48..ea2ab64 100644 --- a/backend/database/models/query.gen.go +++ b/backend/database/models/query.gen.go @@ -22,6 +22,7 @@ var ( ContentAccessQuery *contentAccessQuery ContentAssetQuery *contentAssetQuery ContentPriceQuery *contentPriceQuery + CouponQuery *couponQuery MediaAssetQuery *mediaAssetQuery NotificationQuery *notificationQuery OrderQuery *orderQuery @@ -35,6 +36,7 @@ var ( UserQuery *userQuery UserCommentActionQuery *userCommentActionQuery UserContentActionQuery *userContentActionQuery + UserCouponQuery *userCouponQuery ) func SetDefault(db *gorm.DB, opts ...gen.DOOption) { @@ -44,6 +46,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { ContentAccessQuery = &Q.ContentAccess ContentAssetQuery = &Q.ContentAsset ContentPriceQuery = &Q.ContentPrice + CouponQuery = &Q.Coupon MediaAssetQuery = &Q.MediaAsset NotificationQuery = &Q.Notification OrderQuery = &Q.Order @@ -57,6 +60,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { UserQuery = &Q.User UserCommentActionQuery = &Q.UserCommentAction UserContentActionQuery = &Q.UserContentAction + UserCouponQuery = &Q.UserCoupon } func Use(db *gorm.DB, opts ...gen.DOOption) *Query { @@ -67,6 +71,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { ContentAccess: newContentAccess(db, opts...), ContentAsset: newContentAsset(db, opts...), ContentPrice: newContentPrice(db, opts...), + Coupon: newCoupon(db, opts...), MediaAsset: newMediaAsset(db, opts...), Notification: newNotification(db, opts...), Order: newOrder(db, opts...), @@ -80,6 +85,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { User: newUser(db, opts...), UserCommentAction: newUserCommentAction(db, opts...), UserContentAction: newUserContentAction(db, opts...), + UserCoupon: newUserCoupon(db, opts...), } } @@ -91,6 +97,7 @@ type Query struct { ContentAccess contentAccessQuery ContentAsset contentAssetQuery ContentPrice contentPriceQuery + Coupon couponQuery MediaAsset mediaAssetQuery Notification notificationQuery Order orderQuery @@ -104,6 +111,7 @@ type Query struct { User userQuery UserCommentAction userCommentActionQuery UserContentAction userContentActionQuery + UserCoupon userCouponQuery } func (q *Query) Available() bool { return q.db != nil } @@ -116,6 +124,7 @@ func (q *Query) clone(db *gorm.DB) *Query { ContentAccess: q.ContentAccess.clone(db), ContentAsset: q.ContentAsset.clone(db), ContentPrice: q.ContentPrice.clone(db), + Coupon: q.Coupon.clone(db), MediaAsset: q.MediaAsset.clone(db), Notification: q.Notification.clone(db), Order: q.Order.clone(db), @@ -129,6 +138,7 @@ func (q *Query) clone(db *gorm.DB) *Query { User: q.User.clone(db), UserCommentAction: q.UserCommentAction.clone(db), UserContentAction: q.UserContentAction.clone(db), + UserCoupon: q.UserCoupon.clone(db), } } @@ -148,6 +158,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { ContentAccess: q.ContentAccess.replaceDB(db), ContentAsset: q.ContentAsset.replaceDB(db), ContentPrice: q.ContentPrice.replaceDB(db), + Coupon: q.Coupon.replaceDB(db), MediaAsset: q.MediaAsset.replaceDB(db), Notification: q.Notification.replaceDB(db), Order: q.Order.replaceDB(db), @@ -161,6 +172,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { User: q.User.replaceDB(db), UserCommentAction: q.UserCommentAction.replaceDB(db), UserContentAction: q.UserContentAction.replaceDB(db), + UserCoupon: q.UserCoupon.replaceDB(db), } } @@ -170,6 +182,7 @@ type queryCtx struct { ContentAccess *contentAccessQueryDo ContentAsset *contentAssetQueryDo ContentPrice *contentPriceQueryDo + Coupon *couponQueryDo MediaAsset *mediaAssetQueryDo Notification *notificationQueryDo Order *orderQueryDo @@ -183,6 +196,7 @@ type queryCtx struct { User *userQueryDo UserCommentAction *userCommentActionQueryDo UserContentAction *userContentActionQueryDo + UserCoupon *userCouponQueryDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { @@ -192,6 +206,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx { ContentAccess: q.ContentAccess.WithContext(ctx), ContentAsset: q.ContentAsset.WithContext(ctx), ContentPrice: q.ContentPrice.WithContext(ctx), + Coupon: q.Coupon.WithContext(ctx), MediaAsset: q.MediaAsset.WithContext(ctx), Notification: q.Notification.WithContext(ctx), Order: q.Order.WithContext(ctx), @@ -205,6 +220,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx { User: q.User.WithContext(ctx), UserCommentAction: q.UserCommentAction.WithContext(ctx), UserContentAction: q.UserContentAction.WithContext(ctx), + UserCoupon: q.UserCoupon.WithContext(ctx), } } diff --git a/backend/database/models/user_coupons.gen.go b/backend/database/models/user_coupons.gen.go new file mode 100644 index 0000000..a9aa065 --- /dev/null +++ b/backend/database/models/user_coupons.gen.go @@ -0,0 +1,59 @@ +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. + +package models + +import ( + "context" + "time" + + "go.ipao.vip/gen" +) + +const TableNameUserCoupon = "user_coupons" + +// UserCoupon mapped from table +type UserCoupon struct { + ID int64 `gorm:"column:id;type:bigint;primaryKey;autoIncrement:true" json:"id"` + UserID int64 `gorm:"column:user_id;type:bigint;not null" json:"user_id"` + CouponID int64 `gorm:"column:coupon_id;type:bigint;not null" json:"coupon_id"` + OrderID int64 `gorm:"column:order_id;type:bigint" json:"order_id"` + Status string `gorm:"column:status;type:character varying(32);not null;default:unused" json:"status"` + UsedAt time.Time `gorm:"column:used_at;type:timestamp with time zone" json:"used_at"` + CreatedAt time.Time `gorm:"column:created_at;type:timestamp with time zone;not null;default:now()" json:"created_at"` +} + +// Quick operations without importing query package +// Update applies changed fields to the database using the default DB. +func (m *UserCoupon) Update(ctx context.Context) (gen.ResultInfo, error) { + return Q.UserCoupon.WithContext(ctx).Updates(m) +} + +// Save upserts the model using the default DB. +func (m *UserCoupon) Save(ctx context.Context) error { return Q.UserCoupon.WithContext(ctx).Save(m) } + +// Create inserts the model using the default DB. +func (m *UserCoupon) Create(ctx context.Context) error { + return Q.UserCoupon.WithContext(ctx).Create(m) +} + +// Delete removes the row represented by the model using the default DB. +func (m *UserCoupon) Delete(ctx context.Context) (gen.ResultInfo, error) { + return Q.UserCoupon.WithContext(ctx).Delete(m) +} + +// ForceDelete permanently deletes the row (ignores soft delete) using the default DB. +func (m *UserCoupon) ForceDelete(ctx context.Context) (gen.ResultInfo, error) { + return Q.UserCoupon.WithContext(ctx).Unscoped().Delete(m) +} + +// Reload reloads the model from database by its primary key and overwrites current fields. +func (m *UserCoupon) Reload(ctx context.Context) error { + fresh, err := Q.UserCoupon.WithContext(ctx).GetByID(m.ID) + if err != nil { + return err + } + *m = *fresh + return nil +} diff --git a/backend/database/models/user_coupons.query.gen.go b/backend/database/models/user_coupons.query.gen.go new file mode 100644 index 0000000..690d74d --- /dev/null +++ b/backend/database/models/user_coupons.query.gen.go @@ -0,0 +1,485 @@ +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. +// Code generated by go.ipao.vip/gen. DO NOT EDIT. + +package models + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "go.ipao.vip/gen" + "go.ipao.vip/gen/field" + + "gorm.io/plugin/dbresolver" +) + +func newUserCoupon(db *gorm.DB, opts ...gen.DOOption) userCouponQuery { + _userCouponQuery := userCouponQuery{} + + _userCouponQuery.userCouponQueryDo.UseDB(db, opts...) + _userCouponQuery.userCouponQueryDo.UseModel(&UserCoupon{}) + + tableName := _userCouponQuery.userCouponQueryDo.TableName() + _userCouponQuery.ALL = field.NewAsterisk(tableName) + _userCouponQuery.ID = field.NewInt64(tableName, "id") + _userCouponQuery.UserID = field.NewInt64(tableName, "user_id") + _userCouponQuery.CouponID = field.NewInt64(tableName, "coupon_id") + _userCouponQuery.OrderID = field.NewInt64(tableName, "order_id") + _userCouponQuery.Status = field.NewString(tableName, "status") + _userCouponQuery.UsedAt = field.NewTime(tableName, "used_at") + _userCouponQuery.CreatedAt = field.NewTime(tableName, "created_at") + + _userCouponQuery.fillFieldMap() + + return _userCouponQuery +} + +type userCouponQuery struct { + userCouponQueryDo userCouponQueryDo + + ALL field.Asterisk + ID field.Int64 + UserID field.Int64 + CouponID field.Int64 + OrderID field.Int64 + Status field.String + UsedAt field.Time + CreatedAt field.Time + + fieldMap map[string]field.Expr +} + +func (u userCouponQuery) Table(newTableName string) *userCouponQuery { + u.userCouponQueryDo.UseTable(newTableName) + return u.updateTableName(newTableName) +} + +func (u userCouponQuery) As(alias string) *userCouponQuery { + u.userCouponQueryDo.DO = *(u.userCouponQueryDo.As(alias).(*gen.DO)) + return u.updateTableName(alias) +} + +func (u *userCouponQuery) updateTableName(table string) *userCouponQuery { + u.ALL = field.NewAsterisk(table) + u.ID = field.NewInt64(table, "id") + u.UserID = field.NewInt64(table, "user_id") + u.CouponID = field.NewInt64(table, "coupon_id") + u.OrderID = field.NewInt64(table, "order_id") + u.Status = field.NewString(table, "status") + u.UsedAt = field.NewTime(table, "used_at") + u.CreatedAt = field.NewTime(table, "created_at") + + u.fillFieldMap() + + return u +} + +func (u *userCouponQuery) QueryContext(ctx context.Context) (*userCouponQuery, *userCouponQueryDo) { + return u, u.userCouponQueryDo.WithContext(ctx) +} + +func (u *userCouponQuery) WithContext(ctx context.Context) *userCouponQueryDo { + return u.userCouponQueryDo.WithContext(ctx) +} + +func (u userCouponQuery) TableName() string { return u.userCouponQueryDo.TableName() } + +func (u userCouponQuery) Alias() string { return u.userCouponQueryDo.Alias() } + +func (u userCouponQuery) Columns(cols ...field.Expr) gen.Columns { + return u.userCouponQueryDo.Columns(cols...) +} + +func (u *userCouponQuery) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := u.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (u *userCouponQuery) fillFieldMap() { + u.fieldMap = make(map[string]field.Expr, 7) + u.fieldMap["id"] = u.ID + u.fieldMap["user_id"] = u.UserID + u.fieldMap["coupon_id"] = u.CouponID + u.fieldMap["order_id"] = u.OrderID + u.fieldMap["status"] = u.Status + u.fieldMap["used_at"] = u.UsedAt + u.fieldMap["created_at"] = u.CreatedAt +} + +func (u userCouponQuery) clone(db *gorm.DB) userCouponQuery { + u.userCouponQueryDo.ReplaceConnPool(db.Statement.ConnPool) + return u +} + +func (u userCouponQuery) replaceDB(db *gorm.DB) userCouponQuery { + u.userCouponQueryDo.ReplaceDB(db) + return u +} + +type userCouponQueryDo struct{ gen.DO } + +func (u userCouponQueryDo) Debug() *userCouponQueryDo { + return u.withDO(u.DO.Debug()) +} + +func (u userCouponQueryDo) WithContext(ctx context.Context) *userCouponQueryDo { + return u.withDO(u.DO.WithContext(ctx)) +} + +func (u userCouponQueryDo) ReadDB() *userCouponQueryDo { + return u.Clauses(dbresolver.Read) +} + +func (u userCouponQueryDo) WriteDB() *userCouponQueryDo { + return u.Clauses(dbresolver.Write) +} + +func (u userCouponQueryDo) Session(config *gorm.Session) *userCouponQueryDo { + return u.withDO(u.DO.Session(config)) +} + +func (u userCouponQueryDo) Clauses(conds ...clause.Expression) *userCouponQueryDo { + return u.withDO(u.DO.Clauses(conds...)) +} + +func (u userCouponQueryDo) Returning(value interface{}, columns ...string) *userCouponQueryDo { + return u.withDO(u.DO.Returning(value, columns...)) +} + +func (u userCouponQueryDo) Not(conds ...gen.Condition) *userCouponQueryDo { + return u.withDO(u.DO.Not(conds...)) +} + +func (u userCouponQueryDo) Or(conds ...gen.Condition) *userCouponQueryDo { + return u.withDO(u.DO.Or(conds...)) +} + +func (u userCouponQueryDo) Select(conds ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Select(conds...)) +} + +func (u userCouponQueryDo) Where(conds ...gen.Condition) *userCouponQueryDo { + return u.withDO(u.DO.Where(conds...)) +} + +func (u userCouponQueryDo) Order(conds ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Order(conds...)) +} + +func (u userCouponQueryDo) Distinct(cols ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Distinct(cols...)) +} + +func (u userCouponQueryDo) Omit(cols ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Omit(cols...)) +} + +func (u userCouponQueryDo) Join(table schema.Tabler, on ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Join(table, on...)) +} + +func (u userCouponQueryDo) LeftJoin(table schema.Tabler, on ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.LeftJoin(table, on...)) +} + +func (u userCouponQueryDo) RightJoin(table schema.Tabler, on ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.RightJoin(table, on...)) +} + +func (u userCouponQueryDo) Group(cols ...field.Expr) *userCouponQueryDo { + return u.withDO(u.DO.Group(cols...)) +} + +func (u userCouponQueryDo) Having(conds ...gen.Condition) *userCouponQueryDo { + return u.withDO(u.DO.Having(conds...)) +} + +func (u userCouponQueryDo) Limit(limit int) *userCouponQueryDo { + return u.withDO(u.DO.Limit(limit)) +} + +func (u userCouponQueryDo) Offset(offset int) *userCouponQueryDo { + return u.withDO(u.DO.Offset(offset)) +} + +func (u userCouponQueryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *userCouponQueryDo { + return u.withDO(u.DO.Scopes(funcs...)) +} + +func (u userCouponQueryDo) Unscoped() *userCouponQueryDo { + return u.withDO(u.DO.Unscoped()) +} + +func (u userCouponQueryDo) Create(values ...*UserCoupon) error { + if len(values) == 0 { + return nil + } + return u.DO.Create(values) +} + +func (u userCouponQueryDo) CreateInBatches(values []*UserCoupon, batchSize int) error { + return u.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 (u userCouponQueryDo) Save(values ...*UserCoupon) error { + if len(values) == 0 { + return nil + } + return u.DO.Save(values) +} + +func (u userCouponQueryDo) First() (*UserCoupon, error) { + if result, err := u.DO.First(); err != nil { + return nil, err + } else { + return result.(*UserCoupon), nil + } +} + +func (u userCouponQueryDo) Take() (*UserCoupon, error) { + if result, err := u.DO.Take(); err != nil { + return nil, err + } else { + return result.(*UserCoupon), nil + } +} + +func (u userCouponQueryDo) Last() (*UserCoupon, error) { + if result, err := u.DO.Last(); err != nil { + return nil, err + } else { + return result.(*UserCoupon), nil + } +} + +func (u userCouponQueryDo) Find() ([]*UserCoupon, error) { + result, err := u.DO.Find() + return result.([]*UserCoupon), err +} + +func (u userCouponQueryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*UserCoupon, err error) { + buf := make([]*UserCoupon, 0, batchSize) + err = u.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 (u userCouponQueryDo) FindInBatches(result *[]*UserCoupon, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return u.DO.FindInBatches(result, batchSize, fc) +} + +func (u userCouponQueryDo) Attrs(attrs ...field.AssignExpr) *userCouponQueryDo { + return u.withDO(u.DO.Attrs(attrs...)) +} + +func (u userCouponQueryDo) Assign(attrs ...field.AssignExpr) *userCouponQueryDo { + return u.withDO(u.DO.Assign(attrs...)) +} + +func (u userCouponQueryDo) Joins(fields ...field.RelationField) *userCouponQueryDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Joins(_f)) + } + return &u +} + +func (u userCouponQueryDo) Preload(fields ...field.RelationField) *userCouponQueryDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Preload(_f)) + } + return &u +} + +func (u userCouponQueryDo) FirstOrInit() (*UserCoupon, error) { + if result, err := u.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*UserCoupon), nil + } +} + +func (u userCouponQueryDo) FirstOrCreate() (*UserCoupon, error) { + if result, err := u.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*UserCoupon), nil + } +} + +func (u userCouponQueryDo) FindByPage(offset int, limit int) (result []*UserCoupon, count int64, err error) { + result, err = u.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 = u.Offset(-1).Limit(-1).Count() + return +} + +func (u userCouponQueryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = u.Count() + if err != nil { + return + } + + err = u.Offset(offset).Limit(limit).Scan(result) + return +} + +func (u userCouponQueryDo) Scan(result interface{}) (err error) { + return u.DO.Scan(result) +} + +func (u userCouponQueryDo) Delete(models ...*UserCoupon) (result gen.ResultInfo, err error) { + return u.DO.Delete(models) +} + +// ForceDelete performs a permanent delete (ignores soft-delete) for current scope. +func (u userCouponQueryDo) ForceDelete() (gen.ResultInfo, error) { + return u.Unscoped().Delete() +} + +// Inc increases the given column by step for current scope. +func (u userCouponQueryDo) Inc(column field.Expr, step int64) (gen.ResultInfo, error) { + // column = column + step + e := field.NewUnsafeFieldRaw("?+?", column.RawExpr(), step) + return u.DO.UpdateColumn(column, e) +} + +// Dec decreases the given column by step for current scope. +func (u userCouponQueryDo) Dec(column field.Expr, step int64) (gen.ResultInfo, error) { + // column = column - step + e := field.NewUnsafeFieldRaw("?-?", column.RawExpr(), step) + return u.DO.UpdateColumn(column, e) +} + +// Sum returns SUM(column) for current scope. +func (u userCouponQueryDo) Sum(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("SUM(?)", column.RawExpr()) + if err := u.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Avg returns AVG(column) for current scope. +func (u userCouponQueryDo) Avg(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("AVG(?)", column.RawExpr()) + if err := u.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Min returns MIN(column) for current scope. +func (u userCouponQueryDo) Min(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("MIN(?)", column.RawExpr()) + if err := u.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// Max returns MAX(column) for current scope. +func (u userCouponQueryDo) Max(column field.Expr) (float64, error) { + var _v float64 + agg := field.NewUnsafeFieldRaw("MAX(?)", column.RawExpr()) + if err := u.Select(agg).Scan(&_v); err != nil { + return 0, err + } + return _v, nil +} + +// PluckMap returns a map[key]value for selected key/value expressions within current scope. +func (u userCouponQueryDo) PluckMap(key, val field.Expr) (map[interface{}]interface{}, error) { + do := u.Select(key, val) + rows, err := do.DO.Rows() + if err != nil { + return nil, err + } + defer rows.Close() + mm := make(map[interface{}]interface{}) + for rows.Next() { + var k interface{} + var v interface{} + if err := rows.Scan(&k, &v); err != nil { + return nil, err + } + mm[k] = v + } + return mm, rows.Err() +} + +// Exists returns true if any record matches the given conditions. +func (u userCouponQueryDo) Exists(conds ...gen.Condition) (bool, error) { + cnt, err := u.Where(conds...).Count() + if err != nil { + return false, err + } + return cnt > 0, nil +} + +// PluckIDs returns all primary key values under current scope. +func (u userCouponQueryDo) PluckIDs() ([]int64, error) { + ids := make([]int64, 0, 16) + pk := field.NewInt64(u.TableName(), "id") + if err := u.DO.Pluck(pk, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// GetByID finds a single record by primary key. +func (u userCouponQueryDo) GetByID(id int64) (*UserCoupon, error) { + pk := field.NewInt64(u.TableName(), "id") + return u.Where(pk.Eq(id)).First() +} + +// GetByIDs finds records by primary key list. +func (u userCouponQueryDo) GetByIDs(ids ...int64) ([]*UserCoupon, error) { + if len(ids) == 0 { + return []*UserCoupon{}, nil + } + pk := field.NewInt64(u.TableName(), "id") + return u.Where(pk.In(ids...)).Find() +} + +// DeleteByID deletes records by primary key. +func (u userCouponQueryDo) DeleteByID(id int64) (gen.ResultInfo, error) { + pk := field.NewInt64(u.TableName(), "id") + return u.Where(pk.Eq(id)).Delete() +} + +// DeleteByIDs deletes records by a list of primary keys. +func (u userCouponQueryDo) DeleteByIDs(ids ...int64) (gen.ResultInfo, error) { + if len(ids) == 0 { + return gen.ResultInfo{RowsAffected: 0, Error: nil}, nil + } + pk := field.NewInt64(u.TableName(), "id") + return u.Where(pk.In(ids...)).Delete() +} + +func (u *userCouponQueryDo) withDO(do gen.Dao) *userCouponQueryDo { + u.DO = *do.(*gen.DO) + return u +}