diff --git a/api/proto/vas/proto/op.go b/api/proto/vas/proto/op.go index a4a20d4c..ae246c08 100644 --- a/api/proto/vas/proto/op.go +++ b/api/proto/vas/proto/op.go @@ -59,7 +59,23 @@ type RefundOrderData struct { } type RefundOrderOpt struct { - ZoneRefundReq *ZoneRefundReq + zoneRefundReq *ZoneRefundReq +} + +func NewRefundOrderOpt() *RefundOrderOpt { + return new(RefundOrderOpt) +} + +func (p *RefundOrderOpt) SetZoneRefundReq(z *ZoneRefundReq) *RefundOrderOpt { + p.zoneRefundReq = z + return p +} + +func (p *RefundOrderOpt) GetZoneRefundReq() *ZoneRefundReq { + if p != nil && p.zoneRefundReq != nil { + return p.zoneRefundReq + } + return nil } type RefundCoinOrderReq struct { diff --git a/app/mix/dao/mysql_zone.go b/app/mix/dao/mysql_zone.go index f28a15f9..bbdf1fa9 100644 --- a/app/mix/dao/mysql_zone.go +++ b/app/mix/dao/mysql_zone.go @@ -311,3 +311,23 @@ func (m *Mysql) GetZoneMemberList(ctx *gin.Context, tx *sqlx.Tx, zid int64, memT } return } + +// 空间退款记录 +func (m *Mysql) CreateZoneRefundHis(ctx *gin.Context, tx *sqlx.Tx, o *dbstruct.ZoneRefundHis) error { + var err error + sqlStr := "insert into " + TableVasZoneRefundHis + + " (mid, zid, ct, contact_name, contact_phone, note, order_id, product_id) " + + " values (?,?,?,?,?,?,?,?) " + args := []any{o.GetMid(), o.GetZid(), time.Now().Unix(), o.GetContactName(), o.GetContactPhone(), o.GetNote(), o.GetOrderId(), o.GetProductId()} + if tx != nil { + _, err = tx.ExecContext(ctx, sqlStr, args...) + } else { + db := m.getDBVas() + _, err = db.ExecContext(ctx, sqlStr, args...) + } + if err != nil { + logger.Error("CreateZoneRefundHis fail, o: %v, err: %v", util.ToJson(o), err) + return err + } + return nil +} diff --git a/app/mix/service/logic/vas.go b/app/mix/service/logic/vas.go index 8bc3ac67..13ef934b 100644 --- a/app/mix/service/logic/vas.go +++ b/app/mix/service/logic/vas.go @@ -1957,21 +1957,29 @@ func (v *Vas) RefundOrder(ctx *gin.Context, req *vasproto.RefundOrderReq, opt *v switch product.Type { case dbstruct.ProductTypeMoneyMembership: - return v.refundMembership(ctx, order, req) + err = v.refundMembership(ctx, order, req) case dbstruct.ProductTypeCoins: - return v.refundCoins(ctx, order, req) + err = v.refundCoins(ctx, order, req) case dbstruct.ProductTypeMoneyContact: - return v.refundMoneyContactWechat(ctx, order, req) + err = v.refundMoneyContactWechat(ctx, order, req) case dbstruct.ProductTypeZone: switch product.Id { case dbstruct.ProductIdH5ZoneAdmission: - return v.refundZoneAdmission(ctx, order, req, opt) + err = v.refundZoneAdmission(ctx, order, req, opt) default: - return fmt.Errorf("不支持该商品退款: %s", product.Id) + err = fmt.Errorf("不支持该商品退款: %s", product.Id) } default: - return fmt.Errorf("不支持该商品退款: %s", product.Id) + err = fmt.Errorf("不支持该商品退款: %s", product.Id) } + if err != nil { + logger.Error("RefundOrder fail, order: %v, err: %v", util.ToJson(order), err) + return err + } + + // todo 回滚铁粉 + + return nil } // 现金:会员退款 @@ -2466,9 +2474,24 @@ func (v *Vas) refundZoneAdmission(ctx *gin.Context, order *dbstruct.Order, req * } var ( - orderId = order.GetID() - isFinish = order.GetOrderStatus() == dbstruct.VasOrderStatusFinish + mid = order.GetMid() + zid = int64(0) + orderId = order.GetID() + isFinish = order.GetOrderStatus() == dbstruct.VasOrderStatusFinish + zoneRefundReq *vasproto.ZoneRefundReq ) + if opt.GetZoneRefundReq() != nil { + zoneRefundReq = opt.GetZoneRefundReq() + } + if zoneRefundReq == nil { + return fmt.Errorf("zoneRefundReq is nil, order_id: %v", orderId) + } + zid = zoneRefundReq.Zid + // 和订单的zid不匹配 + if order.GetZid() != zid { + err := fmt.Errorf("与订单的zid不匹配,orderZid: %v, reqZid: %v", order.GetZid(), zid) + return err + } // 开启事务 tx, err := v.store.VasBegin(ctx) @@ -2539,20 +2562,36 @@ func (v *Vas) refundZoneAdmission(ctx *gin.Context, order *dbstruct.Order, req * } // 删除空间普通会员 - err = v.store.DeleteZoneAdmission(ctx, tx, order.GetMid(), order.GetZid()) + err = v.store.DeleteZoneAdmission(ctx, tx, mid, zid) if err != nil { - logger.Error("DeleteZoneAdmission fail, mid: %v, zid: %v, err: %v", order.GetMid(), order.GetZid(), err) + logger.Error("DeleteZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err) return err } // 删除空间成员 - err = v.store.DeleteZoneMember(ctx, tx, order.GetMid(), order.GetZid(), dbstruct.ZoneMemberTypeNormal) + err = v.store.DeleteZoneMember(ctx, tx, mid, zid, dbstruct.ZoneMemberTypeNormal) if err != nil { - logger.Error("DeleteZoneMember fail, mid: %v, zid: %v, err: %v", order.GetMid(), order.GetZid(), err) + logger.Error("DeleteZoneMember fail, mid: %v, zid: %v, err: %v", mid, zid, err) return err } - // 获取收入记录 + // 添加退款记录 + zrh := &dbstruct.ZoneRefundHis{ + Mid: goproto.Int64(mid), + Zid: goproto.Int64(zid), + ContactName: goproto.String(zoneRefundReq.ContactName), + ContactPhone: goproto.String(zoneRefundReq.ContactPhone), + Note: goproto.String(zoneRefundReq.Note), + OrderId: goproto.String(orderId), + ProductId: goproto.String(order.GetProductId()), + } + err = v.store.CreateZoneRefundHis(ctx, tx, zrh) + if err != nil { + logger.Error("CreateZoneRefundHis fail, zrh: %v, err: %v", util.ToJson(zrh), err) + return err + } + + // 回滚收入记录 chListTmp, err := v.store.GetIncomeCHList(ctx, tx, orderId) if err != nil { return err diff --git a/app/mix/service/logic/vas_zone.go b/app/mix/service/logic/vas_zone.go index f2a1a131..2f6ca79c 100644 --- a/app/mix/service/logic/vas_zone.go +++ b/app/mix/service/logic/vas_zone.go @@ -814,7 +814,7 @@ func (v *Vas) ZoneRefund(ctx *gin.Context, req *vasproto.ZoneRefundReq) error { err = v.RefundOrder(ctx, &vasproto.RefundOrderReq{ OrderId: order.GetID(), Operator: fmt.Sprintf("%d", req.Mid), - }, &vasproto.RefundOrderOpt{ZoneRefundReq: req}) + }, vasproto.NewRefundOrderOpt().SetZoneRefundReq(req)) if err != nil { logger.Error("RefundOrder fail, mid: %v, zid: %v, orderId: %v, err: %v", req.Mid, req.Zid, order.GetID(), err) return err diff --git a/app/mix/service/utilservice.go b/app/mix/service/utilservice.go index 65eea473..b66e5699 100644 --- a/app/mix/service/utilservice.go +++ b/app/mix/service/utilservice.go @@ -1700,10 +1700,14 @@ func (s *Service) utilFillZoneMomentsWithApiVOInfo(ctx *gin.Context, list []*dbs } // 已消费金额 - vo.Expenditure = zidZuMap[zid].GetConsume() + if zu, ok := zidZuMap[zid]; ok { + vo.Expenditure = zu.GetConsume() + } // 铁粉价格 - vo.IronfanshipPrice = zvMap[zid].IronfanshipPrice + if zv, ok := zvMap[zid]; ok { + vo.IronfanshipPrice = zv.IronfanshipPrice + } } return diff --git a/dbstruct/vas.sql b/dbstruct/vas.sql index b3950ee1..5ae246dd 100644 --- a/dbstruct/vas.sql +++ b/dbstruct/vas.sql @@ -171,4 +171,77 @@ CREATE TABLE `vas_user_membership_unlock` PRIMARY KEY (`id`) ); CREATE INDEX ix_mid_product_id ON vas_user_membership_unlock (mid, product_id); -CREATE INDEX ix_orderid ON vas_user_membership_unlock (order_id); \ No newline at end of file +CREATE INDEX ix_orderid ON vas_user_membership_unlock (order_id); + +-- 用户解锁空间详情 +CREATE TABLE `vas_zone_unlock` +( + `id` bigint AUTO_INCREMENT COMMENT 'id', + `mid` bigint NOT NULL COMMENT '用户id', + `zid` bigint NOT NULL COMMENT '空间id', + `consume` bigint DEFAULT 0 COMMENT '空间总消费', + `admission_ct` bigint DEFAULT 0 COMMENT '普通会员开通时间', + `admission_until` bigint DEFAULT 0 COMMENT '普通会员到期时间,时间戳,-1: 永久', + `admission_order_id` varchar(128) DEFAULT NULL COMMENT '普通会员订单', + `ironfanship_ct` bigint DEFAULT 0 COMMENT '铁粉开通时间', + `ironfanship_until` bigint DEFAULT 0 COMMENT '铁粉到期时间,时间戳,-1: 永久', + `ironfanship_order_id` varchar(128) DEFAULT NULL COMMENT '铁粉订单', + `superfanship_ct` bigint DEFAULT 0 COMMENT '超粉开通时间', + `superfanship_until` bigint DEFAULT 0 COMMENT '超粉到期时间,时间戳,-1: 永久', + `superfanship_order_id` varchar(128) DEFAULT NULL COMMENT '超粉订单id', + PRIMARY KEY (`id`) +); + +-- 空间消费详情 +CREATE TABLE `vas_zone_ch` +( + `id` bigint AUTO_INCREMENT COMMENT 'id', + `mid` bigint NOT NULL COMMENT '用户id', + `zid` bigint NOT NULL COMMENT '空间id', + `consume` bigint DEFAULT 0 COMMENT '单笔消费', + `ct` bigint DEFAULT 0 COMMENT '解锁时间', + `order_id` varchar(128) DEFAULT NULL COMMENT '绑定的订单id', + `product_id` varchar(128) DEFAULT NULL COMMENT '商品id', + PRIMARY KEY (`id`) +); + +-- 空间成员列表 +CREATE TABLE `vas_zone_member` +( + `id` bigint AUTO_INCREMENT COMMENT 'id', + `mid` bigint NOT NULL COMMENT '用户id', + `zid` bigint NOT NULL COMMENT '空间id', + `consume` bigint DEFAULT 0 COMMENT '单笔消费', + `ct` bigint DEFAULT 0 COMMENT '解锁时间', + `order_id` varchar(128) DEFAULT NULL COMMENT '绑定的订单id', + `product_id` varchar(128) DEFAULT NULL COMMENT '商品id', + PRIMARY KEY (`id`) +); + +-- 空间动态解锁 +CREATE TABLE `vas_zone_moment_unlock` +( + `id` bigint AUTO_INCREMENT COMMENT 'id', + `mid` bigint NOT NULL COMMENT '用户id', + `zid` bigint NOT NULL COMMENT '空间id', + `moment_id` bigint NOT NULL COMMENT '动态id', + `status` int NOT NULL COMMENT '状态', + `ct` bigint DEFAULT 0 COMMENT '解锁时间', + `order_id` varchar(128) DEFAULT NULL COMMENT '绑定的订单id', + PRIMARY KEY (`id`) +); + +-- 空间退款记录 +CREATE TABLE `vas_zone_refund_his` +( + `id` bigint AUTO_INCREMENT COMMENT 'id', + `mid` bigint NOT NULL COMMENT '用户id', + `zid` bigint NOT NULL COMMENT '空间id', + `ct` bigint DEFAULT 0 COMMENT '解锁时间', + `contact_name` varchar(128) DEFAULT NULL COMMENT '联系方式', + `contact_phone` varchar(128) DEFAULT NULL COMMENT '联系电话', + `note` varchar(1024) DEFAULT NULL COMMENT '备注', + `order_id` varchar(128) DEFAULT NULL COMMENT '绑定的订单id', + `product_id` varchar(128) DEFAULT NULL COMMENT '商品id', + PRIMARY KEY (`id`) +); diff --git a/dbstruct/vas_mysql.go b/dbstruct/vas_mysql.go index d79a0052..457da872 100644 --- a/dbstruct/vas_mysql.go +++ b/dbstruct/vas_mysql.go @@ -939,10 +939,10 @@ type ZoneUnlock struct { AdmissionCt *int64 `json:"admission_ct" db:"admission_ct"` // 普通会员开通时间 AdmissionUntil *int64 `json:"admission_until" db:"admission_until"` // 普通会员到期时间,时间戳,-1: 永久 AdmissionOrderId *string `json:"admission_order_id" db:"admission_order_id"` // 普通会员订单 - IronfanshipCt *int64 `json:"ironfanship_ct" db:"ironfanship_ct"` // 铁粉开通时间id + IronfanshipCt *int64 `json:"ironfanship_ct" db:"ironfanship_ct"` // 铁粉开通时间 IronfanshipUntil *int64 `json:"ironfanship_until" db:"ironfanship_until"` // 铁粉到期时间,时间戳,-1:永久 IronfanshipOrderId *string `json:"ironfanship_order_id" db:"ironfanship_order_id"` // 铁粉订单 - SuperfanshipCt *int64 `json:"superfanship_ct" db:"superfanship_ct"` // 超粉开通时间id + SuperfanshipCt *int64 `json:"superfanship_ct" db:"superfanship_ct"` // 超粉开通时间 SuperfanshipUntil *int64 `json:"superfanship_until" db:"superfanship_until"` // 超粉到期时间,时间戳,-1:永久 SuperfanshipOrderId *string `json:"superfanship_order_id" db:"superfanship_order_id"` // 超粉订单id } @@ -1073,7 +1073,7 @@ type ZoneConsumeHis struct { Id *int64 `json:"id" db:"id"` Mid *int64 `json:"mid" db:"mid"` // 用户mid Zid *int64 `json:"zid" db:"zid"` // 空间id - Consume *int64 `json:"consume" db:"consume"` // 空间总消费 + Consume *int64 `json:"consume" db:"consume"` // 单笔消费 Ct *int64 `json:"ct" db:"ct"` // 解锁时间 OrderId *string `json:"order_id" db:"order_id"` // 绑定的订单id ProductId *string `json:"product_id" db:"product_id"` // 商品id @@ -1249,3 +1249,79 @@ func (p *ZoneMomentUnlock) GetOrderId() string { } return "" } + +// 空间退款记录 +type ZoneRefundHis struct { + Id *int64 `json:"id" db:"id"` + Mid *int64 `json:"mid" db:"mid"` // 用户id + Zid *int64 `json:"zid" db:"zid"` // 空间id + Ct *int64 `json:"ct" db:"ct"` // 解锁时间 + ContactName *string `json:"contact_name" db:"contact_name"` // 联系方式 + ContactPhone *string `json:"contact_phone" db:"contact_phone"` // 联系电话 + Note *string `json:"note" db:"note"` // 备注 + OrderId *string `json:"order_id" db:"order_id"` // 绑定的订单id + ProductId *string `json:"product_id" db:"product_id"` // 商品id +} + +func (p *ZoneRefundHis) GetId() int64 { + if p != nil && p.Id != nil { + return *p.Id + } + return 0 +} + +func (p *ZoneRefundHis) GetMid() int64 { + if p != nil && p.Mid != nil { + return *p.Mid + } + return 0 +} + +func (p *ZoneRefundHis) GetZid() int64 { + if p != nil && p.Zid != nil { + return *p.Zid + } + return 0 +} + +func (p *ZoneRefundHis) GetCt() int64 { + if p != nil && p.Ct != nil { + return *p.Ct + } + return 0 +} + +func (p *ZoneRefundHis) GetContactName() string { + if p != nil && p.ContactName != nil { + return *p.ContactName + } + return "" +} + +func (p *ZoneRefundHis) GetContactPhone() string { + if p != nil && p.ContactPhone != nil { + return *p.ContactPhone + } + return "" +} + +func (p *ZoneRefundHis) GetNote() string { + if p != nil && p.Note != nil { + return *p.Note + } + return "" +} + +func (p *ZoneRefundHis) GetOrderId() string { + if p != nil && p.OrderId != nil { + return *p.OrderId + } + return "" +} + +func (p *ZoneRefundHis) GetProductId() string { + if p != nil && p.ProductId != nil { + return *p.ProductId + } + return "" +}