diff --git a/README.md b/README.md index f7696b5..5968e42 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ proxy 的删除和更新,锁粒度应该有问题 -最低价格 0.01 - 交易信息持久化 用反射实现环境变量解析,以简化函数签名 diff --git a/scripts/sql/fill.sql b/scripts/sql/fill.sql index be812b9..bb1181c 100644 --- a/scripts/sql/fill.sql +++ b/scripts/sql/fill.sql @@ -136,74 +136,74 @@ insert into permission (name, description, sort) values -- permission 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'permission' and deleted_at is null), 'permission:read', '读取权限列表', 1), - ((select id from permission where name = 'permission' and deleted_at is null), 'permission:write', '写入权限', 2); + ((select id from permission where name = 'permission' and deleted_at is null), 'permission:write', '编辑权限', 2); -- admin_role 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:read', '读取管理员角色列表', 1), - ((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:write', '写入管理员角色', 2); + ((select id from permission where name = 'admin_role' and deleted_at is null), 'admin_role:write', '编辑管理员角色', 2); -- admin 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'admin' and deleted_at is null), 'admin:read', '读取管理员列表', 1), - ((select id from permission where name = 'admin' and deleted_at is null), 'admin:write', '写入管理员', 2); + ((select id from permission where name = 'admin' and deleted_at is null), 'admin:write', '编辑管理员', 2); -- product 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'product' and deleted_at is null), 'product:read', '读取产品列表', 1), - ((select id from permission where name = 'product' and deleted_at is null), 'product:write', '写入产品', 2); + ((select id from permission where name = 'product' and deleted_at is null), 'product:write', '编辑产品', 2); -- product_sku 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:read', '读取产品套餐列表', 1), - ((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:write', '写入产品套餐', 2); + ((select id from permission where name = 'product_sku' and deleted_at is null), 'product_sku:write', '编辑产品套餐', 2); -- discount 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'discount' and deleted_at is null), 'discount:read', '读取折扣列表', 1), - ((select id from permission where name = 'discount' and deleted_at is null), 'discount:write', '写入折扣', 2); + ((select id from permission where name = 'discount' and deleted_at is null), 'discount:write', '编辑折扣', 2); -- resource 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'resource' and deleted_at is null), 'resource:read', '读取用户套餐列表', 1), - ((select id from permission where name = 'resource' and deleted_at is null), 'resource:write', '写入用户套餐', 2), + ((select id from permission where name = 'resource' and deleted_at is null), 'resource:write', '编辑用户套餐', 2), ((select id from permission where name = 'resource' and deleted_at is null), 'resource:short', '短效动态套餐', 3), ((select id from permission where name = 'resource' and deleted_at is null), 'resource:long', '长效动态套餐', 4); -- user 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'user' and deleted_at is null), 'user:read', '读取用户列表', 1), - ((select id from permission where name = 'user' and deleted_at is null), 'user:write', '写入用户', 2); + ((select id from permission where name = 'user' and deleted_at is null), 'user:write', '编辑用户', 2); -- coupon 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:read', '读取优惠券列表', 1), - ((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:write', '写入优惠券', 2); + ((select id from permission where name = 'coupon' and deleted_at is null), 'coupon:write', '编辑优惠券', 2); -- batch 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'batch' and deleted_at is null), 'batch:read', '读取批次列表', 1), - ((select id from permission where name = 'batch' and deleted_at is null), 'batch:write', '写入批次', 2); + ((select id from permission where name = 'batch' and deleted_at is null), 'batch:write', '编辑批次', 2); -- channel 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'channel' and deleted_at is null), 'channel:read', '读取 IP 列表', 1), - ((select id from permission where name = 'channel' and deleted_at is null), 'channel:write', '写入 IP', 2); + ((select id from permission where name = 'channel' and deleted_at is null), 'channel:write', '编辑 IP', 2); -- proxy 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'proxy' and deleted_at is null), 'proxy:read', '读取代理列表', 1), - ((select id from permission where name = 'proxy' and deleted_at is null), 'proxy:write', '写入代理', 2); + ((select id from permission where name = 'proxy' and deleted_at is null), 'proxy:write', '编辑代理', 2); -- trade 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'trade' and deleted_at is null), 'trade:read', '读取交易列表', 1), - ((select id from permission where name = 'trade' and deleted_at is null), 'trade:write', '写入交易', 2); + ((select id from permission where name = 'trade' and deleted_at is null), 'trade:write', '编辑交易', 2); -- bill 子权限 insert into permission (parent_id, name, description, sort) values ((select id from permission where name = 'bill' and deleted_at is null), 'bill:read', '读取账单列表', 1), - ((select id from permission where name = 'bill' and deleted_at is null), 'bill:write', '写入账单', 2); + ((select id from permission where name = 'bill' and deleted_at is null), 'bill:write', '编辑账单', 2); -- balance_activity 子权限 insert into permission (parent_id, name, description, sort) values @@ -236,7 +236,7 @@ insert into permission (parent_id, name, description, sort) values -- user:write 子权限 insert into permission (parent_id, name, description, sort) values - ((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:balance', '写入用户余额', 1), + ((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:balance', '编辑用户余额', 1), ((select id from permission where name = 'user:write' and deleted_at is null), 'user:write:bind', '用户认领', 2); -- batch:read 子权限 diff --git a/scripts/sql/init.sql b/scripts/sql/init.sql index 21c58a9..b29b906 100644 --- a/scripts/sql/init.sql +++ b/scripts/sql/init.sql @@ -1111,7 +1111,7 @@ comment on table coupon_user is '优惠券发放表'; comment on column coupon_user.id is '记录ID'; comment on column coupon_user.coupon_id is '优惠券ID'; comment on column coupon_user.user_id is '用户ID'; -comment on column coupon_user.status is '使用状态:0-未使用,1-已使用'; +comment on column coupon_user.status is '使用状态:0-未使用,1-已使用,2-已禁用'; comment on column coupon_user.expire_at is '过期时间'; comment on column coupon_user.used_at is '使用时间'; comment on column coupon_user.created_at is '创建时间'; diff --git a/web/handlers/balance_activity.go b/web/handlers/balance_activity.go index 5afa8c0..319579e 100644 --- a/web/handlers/balance_activity.go +++ b/web/handlers/balance_activity.go @@ -11,6 +11,65 @@ import ( "github.com/gofiber/fiber/v2" ) +// PageBalanceActivity 分页查询当前用户的余额变动记录 +func PageBalanceActivity(c *fiber.Ctx) error { + // 获取当前用户ID + authCtx, err := auth.GetAuthCtx(c).PermitUser() + if err != nil { + return err + } + + // 解析请求参数 + req := new(PageBalanceActivityByUserReq) + if err := g.Validator.ParseBody(c, req); err != nil { + return err + } + + // 构造查询条件 + do := q.BalanceActivity.Where(q.BalanceActivity.UserID.Eq(authCtx.User.ID)) + if req.BillNo != nil { + do = do.Where(q.Bill.As("Bill").BillNo.Eq(*req.BillNo)) + } + if req.CreatedAtStart != nil { + t := u.DateHead(*req.CreatedAtStart) + do = do.Where(q.BalanceActivity.CreatedAt.Gte(t)) + } + if req.CreatedAtEnd != nil { + t := u.DateTail(*req.CreatedAtEnd) + do = do.Where(q.BalanceActivity.CreatedAt.Lte(t)) + } + + // 查询余额变动列表 + list, total, err := q.BalanceActivity. + Joins(q.BalanceActivity.Bill). + Select( + q.BalanceActivity.ALL, + q.Bill.As("Bill").ID.As("Bill__id"), + q.Bill.As("Bill").BillNo.As("Bill__bill_no"), + ). + Where(do). + Order(q.BalanceActivity.CreatedAt.Desc()). + FindByPage(req.GetOffset(), req.GetLimit()) + if err != nil { + return core.NewBizErr("获取数据失败", err) + } + + // 返回结果 + return c.JSON(core.PageResp{ + List: list, + Total: int(total), + Page: req.GetPage(), + Size: req.GetSize(), + }) +} + +type PageBalanceActivityByUserReq struct { + core.PageReq + BillNo *string `json:"bill_no,omitempty"` + CreatedAtStart *time.Time `json:"created_at_start,omitempty"` + CreatedAtEnd *time.Time `json:"created_at_end,omitempty"` +} + // PageBalanceActivityByAdmin 分页查询所有余额变动记录 func PageBalanceActivityByAdmin(c *fiber.Ctx) error { // 检查权限 diff --git a/web/handlers/user.go b/web/handlers/user.go index aa52e94..c9bf505 100644 --- a/web/handlers/user.go +++ b/web/handlers/user.go @@ -260,7 +260,7 @@ type UpdateUserBalanceByAdminData struct { // 绑定管理员 func BindAdmin(c *fiber.Ctx) error { // 检查权限 - authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWrite) + authCtx, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeUserWriteBind) if err != nil { return err } diff --git a/web/models/coupon_user.go b/web/models/coupon_user.go index 582d052..db4dc08 100644 --- a/web/models/coupon_user.go +++ b/web/models/coupon_user.go @@ -4,13 +4,13 @@ import "time" // CouponUser 优惠券发放表 type CouponUser struct { - ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID - CouponID int32 `json:"coupon_id" gorm:"column:coupon_id"` // 优惠券ID - UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID - Status CouponStatus `json:"status" gorm:"column:status"` // 使用状态:0-未使用,1-已使用 - ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间 - UsedAt *time.Time `json:"used_at,omitempty" gorm:"column:used_at"` // 使用时间 - CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间 + ID int32 `json:"id" gorm:"column:id;primaryKey"` // 记录ID + CouponID int32 `json:"coupon_id" gorm:"column:coupon_id"` // 优惠券ID + UserID int32 `json:"user_id" gorm:"column:user_id"` // 用户ID + Status CouponUserStatus `json:"status" gorm:"column:status"` // 使用状态:0-未使用,1-已使用,2-已禁用 + ExpireAt *time.Time `json:"expire_at,omitempty" gorm:"column:expire_at"` // 过期时间 + UsedAt *time.Time `json:"used_at,omitempty" gorm:"column:used_at"` // 使用时间 + CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` // 创建时间 Coupon *Coupon `json:"coupon,omitempty" gorm:"foreignKey:CouponID"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"` diff --git a/web/routes.go b/web/routes.go index 1adf7c7..eb15579 100644 --- a/web/routes.go +++ b/web/routes.go @@ -91,6 +91,10 @@ func userRouter(api fiber.Router) { bill := api.Group("/bill") bill.Post("/list", handlers.ListBill) + // 余额变动 + balance := api.Group("/balance") + balance.Post("/page", handlers.PageBalanceActivity) + // 公告 announcement := api.Group("/announcement") announcement.Post("/list", handlers.ListAnnouncements) diff --git a/web/services/coupon.go b/web/services/coupon.go index b0c38ed..ca34d88 100644 --- a/web/services/coupon.go +++ b/web/services/coupon.go @@ -68,14 +68,24 @@ func (s *couponService) Update(data UpdateCouponData) error { do = append(do, q.Coupon.Status.Value(int(*data.Status))) } if data.ExpireType != nil { + switch *data.ExpireType { + case m.CouponExpireTypeNever: + do = append(do, q.Coupon.ExpireAt.Null(), q.Coupon.ExpireIn.Null()) + + case m.CouponExpireTypeFixed: + if data.ExpireAt == nil { + return core.NewBizErr("expire_at 不能为空") + } + do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt), q.Coupon.ExpireIn.Null()) + + case m.CouponExpireTypeRelative: + if data.ExpireIn == nil { + return core.NewBizErr("expire_in 不能为空") + } + do = append(do, q.Coupon.ExpireAt.Null(), q.Coupon.ExpireIn.Value(*data.ExpireIn)) + } do = append(do, q.Coupon.ExpireType.Value(int(*data.ExpireType))) } - if data.ExpireAt != nil { - do = append(do, q.Coupon.ExpireAt.Value(*data.ExpireAt)) - } - if data.ExpireIn != nil { - do = append(do, q.Coupon.ExpireIn.Value(*data.ExpireIn)) - } _, err := q.Coupon.Where(q.Coupon.ID.Eq(data.ID)).UpdateSimple(do...) return err @@ -98,6 +108,56 @@ func (s *couponService) Delete(id int32) error { return err } +// 发放优惠券 +func (s *couponService) Assign(couponID int32, userID int32) error { + // 获取优惠券信息 + coupon, err := q.Coupon.Where(q.Coupon.ID.Eq(couponID)).Take() + if errors.Is(err, gorm.ErrRecordNotFound) { + return core.NewBizErr("优惠券不存在") + } + if err != nil { + return core.NewBizErr("获取优惠券数据失败", err) + } + + // 检查优惠券是否可用 + // 1. 状态必须为正常 + if coupon.Status != m.CouponStatusEnabled { + return core.NewBizErr("优惠券不可用") + } + // 2. 数量必须大于0 + if coupon.Count <= 0 { + return core.NewBizErr("优惠券已发放完") + } + + // 发放优惠券 + err = q.Q.Transaction(func(q *q.Query) error { + // 创建用户优惠券记录 + err := q.CouponUser.Create(&m.CouponUser{ + UserID: userID, + CouponID: couponID, + Status: m.CouponUserStatusUnused, + }) + if err != nil { + return err + } + + // 扣减优惠券数量 + _, err = q.Coupon. + Where(q.Coupon.ID.Eq(couponID), q.Coupon.Count_.Eq(coupon.Count)). + UpdateSimple(q.Coupon.Count_.Value(coupon.Count - 1)) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return core.NewBizErr("发放优惠券失败", err) + } + + return nil +} + // GetUserCoupon 获取用户的指定优惠券 func (s *couponService) GetUserCoupon(uid int32, cuid int32, amount decimal.Decimal) (*m.CouponUser, error) { // 获取优惠券