Merge branch 'feat-IRONFANS-70' into test
This commit is contained in:
commit
5981c7d076
|
@ -120,6 +120,7 @@ type WithdrawApplyData struct {
|
||||||
|
|
||||||
// 空间收银台
|
// 空间收银台
|
||||||
type ZoneGetCashierReq struct {
|
type ZoneGetCashierReq struct {
|
||||||
|
base.BaseRequest
|
||||||
Zid int64 `json:"zid"` // 空间id
|
Zid int64 `json:"zid"` // 空间id
|
||||||
MomentId int64 `json:"moment_id"` // 动态id
|
MomentId int64 `json:"moment_id"` // 动态id
|
||||||
ProductId string `json:"product_id"` // 商品id,ProductIdH5Zone*
|
ProductId string `json:"product_id"` // 商品id,ProductIdH5Zone*
|
||||||
|
@ -130,6 +131,7 @@ type ZoneGetCashierData struct {
|
||||||
Price int64 `json:"price"` // 价格,单位: 分
|
Price int64 `json:"price"` // 价格,单位: 分
|
||||||
Validity string `json:"validity"` // 有效期,直接展示就行,"30天"、"永久"
|
Validity string `json:"validity"` // 有效期,直接展示就行,"30天"、"永久"
|
||||||
IsSuperfanshipGiveWechat int `json:"is_superfanship_give_wechat"` // 是否开启超粉空间赠送微信 0: 不赠送, 1: 赠送
|
IsSuperfanshipGiveWechat int `json:"is_superfanship_give_wechat"` // 是否开启超粉空间赠送微信 0: 不赠送, 1: 赠送
|
||||||
|
HasBought int `json:"has_bought"` // 是否购买过, 0: 没购买过,1: 已经买了
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZoneGetCashierResp struct {
|
type ZoneGetCashierResp struct {
|
||||||
|
|
|
@ -103,11 +103,17 @@ type ApiListByMidReq struct {
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AdmissionRefundStatusRefunding = 1 // 退款中
|
||||||
|
AdmissionRefundStatusFinish = 2 // 已退款
|
||||||
|
)
|
||||||
|
|
||||||
type ApiListByMidData struct {
|
type ApiListByMidData struct {
|
||||||
List []*ApiZoneVO `json:"list"`
|
List []*ApiZoneVO `json:"list"`
|
||||||
Offset int `json:"offset"`
|
Offset int `json:"offset"`
|
||||||
More int `json:"more"`
|
More int `json:"more"`
|
||||||
RefundEnable int `json:"refund_enable"` // 1: 能退款,0: 不能退款
|
RefundEnable int `json:"refund_enable"` // 1: 能退款,0: 不能退款
|
||||||
|
RefundStatus int `json:"refund_status"` // 退款状态, AdmissionRefundStatus*
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiListByMidResp struct {
|
type ApiListByMidResp struct {
|
||||||
|
|
|
@ -151,7 +151,7 @@ func ApiGetZoneListByUserIdFromOutside(ctx *gin.Context) {
|
||||||
req.Limit = consts.DefaultPageSize
|
req.Limit = consts.DefaultPageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
refundEnable, list, ec := service.DefaultService.ApiGetZoneListByUserIdFromOutside(ctx, req)
|
refundEnable, refundStatus, list, ec := service.DefaultService.ApiGetZoneListByUserIdFromOutside(ctx, req)
|
||||||
if ec != errcode.ErrCodeZoneSrvOk {
|
if ec != errcode.ErrCodeZoneSrvOk {
|
||||||
logger.Error("ApiGetZoneListByUserIdFromOutside fail, req: %v, ec: %v", util.ToJson(req), ec)
|
logger.Error("ApiGetZoneListByUserIdFromOutside fail, req: %v, ec: %v", util.ToJson(req), ec)
|
||||||
ReplyErrCodeMsg(ctx, ec)
|
ReplyErrCodeMsg(ctx, ec)
|
||||||
|
@ -172,6 +172,7 @@ func ApiGetZoneListByUserIdFromOutside(ctx *gin.Context) {
|
||||||
List: list,
|
List: list,
|
||||||
Offset: req.Offset + len(list),
|
Offset: req.Offset + len(list),
|
||||||
RefundEnable: refundEnable,
|
RefundEnable: refundEnable,
|
||||||
|
RefundStatus: refundStatus,
|
||||||
}
|
}
|
||||||
if len(list) >= req.Limit {
|
if len(list) >= req.Limit {
|
||||||
data.More = 1
|
data.More = 1
|
||||||
|
|
|
@ -266,6 +266,25 @@ func (m *Mysql) DecZoneConsume(ctx *gin.Context, tx *sqlx.Tx, mid, zid, inc int6
|
||||||
// 添加到空间成员
|
// 添加到空间成员
|
||||||
func (m *Mysql) AddZoneMember(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64, memType int32) error {
|
func (m *Mysql) AddZoneMember(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64, memType int32) error {
|
||||||
var err error
|
var err error
|
||||||
|
// 先获取,没有再添加
|
||||||
|
var tmpZm *dbstruct.ZoneMember
|
||||||
|
if tx != nil {
|
||||||
|
err = tx.GetContext(ctx, tmpZm, fmt.Sprintf("select * from %s where mid=? and zid=? and member_type=?", TableVasZoneMember), mid, zid, memType)
|
||||||
|
} else {
|
||||||
|
db := m.getDBVas()
|
||||||
|
err = db.GetContext(ctx, tmpZm, fmt.Sprintf("select * from %s where mid=? and zid=? and member_type=?", TableVasZoneMember), mid, zid, memType)
|
||||||
|
}
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if tmpZm != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再添加
|
||||||
sqlStr := "insert into " + TableVasZoneMember + " (zid, mid, member_type, ct) " + " values (?,?,?,?)"
|
sqlStr := "insert into " + TableVasZoneMember + " (zid, mid, member_type, ct) " + " values (?,?,?,?)"
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
_, err = tx.ExecContext(ctx, sqlStr, zid, mid, memType, time.Now().Unix())
|
_, err = tx.ExecContext(ctx, sqlStr, zid, mid, memType, time.Now().Unix())
|
||||||
|
@ -299,7 +318,7 @@ func (m *Mysql) DeleteZoneMember(ctx *gin.Context, tx *sqlx.Tx, mid, zid int64,
|
||||||
// 获取空间成员列表
|
// 获取空间成员列表
|
||||||
func (m *Mysql) GetZoneMemberList(ctx *gin.Context, tx *sqlx.Tx, zid int64, memType int32) (list []*dbstruct.ZoneMember, err error) {
|
func (m *Mysql) GetZoneMemberList(ctx *gin.Context, tx *sqlx.Tx, zid int64, memType int32) (list []*dbstruct.ZoneMember, err error) {
|
||||||
list = make([]*dbstruct.ZoneMember, 0)
|
list = make([]*dbstruct.ZoneMember, 0)
|
||||||
sqlStr := fmt.Sprintf(fmt.Sprintf("select * from %s where zid=? and member_type=? order by ct desc", TableVasZoneUnlock))
|
sqlStr := fmt.Sprintf(fmt.Sprintf("select * from %s where zid=? and member_type=? order by ct desc", TableVasZoneMember))
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
err = tx.SelectContext(ctx, &list, sqlStr, zid, memType)
|
err = tx.SelectContext(ctx, &list, sqlStr, zid, memType)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2301,7 +2301,7 @@ func (s *Service) ApiGetZoneListByVisitorMid(ctx *gin.Context, req *zoneproto.Ap
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ApiGetZoneListByUserIdFromOutside(ctx *gin.Context, req *zoneproto.ApiListByUserIdFromOutsideReq) (refundEnable int, volist []*zoneproto.ApiZoneVO, ec errcode.ErrCode) {
|
func (s *Service) ApiGetZoneListByUserIdFromOutside(ctx *gin.Context, req *zoneproto.ApiListByUserIdFromOutsideReq) (refundEnable int, refundStatus int, volist []*zoneproto.ApiZoneVO, ec errcode.ErrCode) {
|
||||||
ec = errcode.ErrCodeZoneSrvOk
|
ec = errcode.ErrCodeZoneSrvOk
|
||||||
|
|
||||||
volist = make([]*zoneproto.ApiZoneVO, 0)
|
volist = make([]*zoneproto.ApiZoneVO, 0)
|
||||||
|
@ -2358,6 +2358,16 @@ func (s *Service) ApiGetZoneListByUserIdFromOutside(ctx *gin.Context, req *zonep
|
||||||
refundEnable = 1
|
refundEnable = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 退款状态
|
||||||
|
if _, ok := zidZuMap[zid]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
admissionOrderId := zidZuMap[zid].GetAdmissionOrderId()
|
||||||
|
order, _ := _DefaultVas.GetOrderById(ctx, nil, admissionOrderId)
|
||||||
|
if order.GetOrderStatus() == dbstruct.VasOrderStatusRefund {
|
||||||
|
refundStatus = zoneproto.AdmissionRefundStatusFinish
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1336,6 +1336,10 @@ func (v *Vas) GetCoinOrderById(ctx *gin.Context, id string) (*dbstruct.CoinOrder
|
||||||
return v.store.GetCoinOrderById(ctx, nil, id)
|
return v.store.GetCoinOrderById(ctx, nil, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Vas) GetOrderById(ctx *gin.Context, tx *sqlx.Tx, id string) (*dbstruct.Order, error) {
|
||||||
|
return v.store.GetOrderById(ctx, tx, id)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Vas) GetOrderCountGroupByStatus(ctx *gin.Context, req *vasproto.GetOrderByStatusReq) ([]*dbstruct.VasOrderStatusCount, error) {
|
func (v *Vas) GetOrderCountGroupByStatus(ctx *gin.Context, req *vasproto.GetOrderByStatusReq) ([]*dbstruct.VasOrderStatusCount, error) {
|
||||||
return v.store.GetOrderCountGroupByStatus(ctx, nil, req.OrderStatuses, req.CtStart, req.CtEnd)
|
return v.store.GetOrderCountGroupByStatus(ctx, nil, req.OrderStatuses, req.CtStart, req.CtEnd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ func (v *Vas) UnlockZoneAdmission(ctx *gin.Context, tx *sqlx.Tx, order *dbstruct
|
||||||
)
|
)
|
||||||
|
|
||||||
// 解锁空间会员
|
// 解锁空间会员
|
||||||
err := v.store.UnlockZoneAdmission(ctx, tx, mid, zid, -1, orderId, unlockType)
|
err := v.MustUnlockAdmission(ctx, tx, mid, zid, -1, orderId, unlockType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||||||
return err
|
return err
|
||||||
|
@ -297,9 +297,9 @@ func (v *Vas) UnlockZoneSuperfanship(ctx *gin.Context, tx *sqlx.Tx, order *dbstr
|
||||||
)
|
)
|
||||||
|
|
||||||
// 解锁空间超粉
|
// 解锁空间超粉
|
||||||
err := v.store.UnlockZoneSuperfanship(ctx, tx, mid, zid, order.GetSuperfanshipUntil(), orderId, unlockType)
|
err := v.MustUnlockSuperfanship(ctx, tx, mid, zid, order.GetSuperfanshipUntil(), orderId, unlockType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
logger.Error("MustUnlockSuperfanship fail, mid: %v, zid: %v, orderId: %v, err: %v", mid, zid, orderId, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ func (v *Vas) UnlockZoneIronfanshipReachConsume(ctx *gin.Context, tx *sqlx.Tx, m
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解锁铁粉
|
// 解锁铁粉
|
||||||
err := v.store.UnlockZoneIronfanship(ctx, tx, mid, zid, -1, "ironfan", dbstruct.ZoneUnlockTypeReachConsume)
|
err := v.MustUnlockIronfanship(ctx, tx, mid, zid, -1, "ironfan", dbstruct.ZoneUnlockTypeReachConsume)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||||||
return err
|
return err
|
||||||
|
@ -856,7 +856,7 @@ func (v *Vas) ZoneFreeJoin(ctx *gin.Context, mid, zid int64) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 解锁空间会员
|
// 解锁空间会员
|
||||||
err = v.store.UnlockZoneAdmission(ctx, tx, mid, zid, -1, "", dbstruct.ZoneUnlockTypeFree)
|
err = v.MustUnlockAdmission(ctx, tx, mid, zid, -1, "", dbstruct.ZoneUnlockTypeFree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
logger.Error("UnlockZoneAdmission fail, mid: %v, zid: %v, err: %v", mid, zid, err)
|
||||||
return err
|
return err
|
||||||
|
@ -871,3 +871,39 @@ func (v *Vas) ZoneFreeJoin(ctx *gin.Context, mid, zid int64) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Vas) MustUnlockAdmission(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||||||
|
// 检查解锁
|
||||||
|
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||||||
|
if !exist {
|
||||||
|
logger.Error("MustUnlockAdmission fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||||||
|
return fmt.Errorf("无法解锁,请联系运营")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解锁空间会员
|
||||||
|
return v.store.UnlockZoneAdmission(ctx, tx, mid, zid, until, orderId, unlockType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vas) MustUnlockIronfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||||||
|
// 检查解锁
|
||||||
|
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||||||
|
if !exist {
|
||||||
|
logger.Error("MustUnlockIronfanship fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||||||
|
return fmt.Errorf("无法解锁,请联系运营")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解锁铁粉
|
||||||
|
return v.store.UnlockZoneIronfanship(ctx, tx, mid, zid, until, orderId, unlockType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vas) MustUnlockSuperfanship(ctx *gin.Context, tx *sqlx.Tx, mid, zid, until int64, orderId string, unlockType int32) error {
|
||||||
|
// 检查解锁
|
||||||
|
_, exist := v.CheckZoneUnlockExist(ctx, tx, mid, zid)
|
||||||
|
if !exist {
|
||||||
|
logger.Error("MustUnlockSuperfanship fail, checkZU fail, mid: %v, zid: %v", mid, zid)
|
||||||
|
return fmt.Errorf("无法解锁,请联系运营")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解锁超粉
|
||||||
|
return v.store.UnlockZoneSuperfanship(ctx, tx, mid, zid, until, orderId, unlockType)
|
||||||
|
}
|
||||||
|
|
|
@ -666,13 +666,29 @@ func (s *Service) ZoneCreateOrder(ctx *gin.Context, req *vasproto.ZoneCreateOrde
|
||||||
|
|
||||||
func (s *Service) ZoneGetCashier(ctx *gin.Context, req *vasproto.ZoneGetCashierReq) (data *vasproto.ZoneGetCashierData, ec errcode.ErrCode, err error) {
|
func (s *Service) ZoneGetCashier(ctx *gin.Context, req *vasproto.ZoneGetCashierReq) (data *vasproto.ZoneGetCashierData, ec errcode.ErrCode, err error) {
|
||||||
ec = errcode.ErrCodeVasSrvOk
|
ec = errcode.ErrCodeVasSrvOk
|
||||||
|
|
||||||
data = new(vasproto.ZoneGetCashierData)
|
data = new(vasproto.ZoneGetCashierData)
|
||||||
data.Validity = "永久"
|
data.Validity = "永久"
|
||||||
zv, _ := _DefaultVas.GetZoneVasInfo(ctx, req.Zid)
|
|
||||||
|
mid := req.Mid
|
||||||
|
zid := req.Zid
|
||||||
|
zv, _ := _DefaultVas.GetZoneVasInfo(ctx, zid)
|
||||||
|
|
||||||
switch req.ProductId {
|
switch req.ProductId {
|
||||||
case dbstruct.ProductIdH5ZoneMoment:
|
case dbstruct.ProductIdH5ZoneMoment:
|
||||||
|
// 判断是否已解锁该动态
|
||||||
|
zmuMap, err := _DefaultVas.GetZoneMomentUnlockMapByMidMomentIds(ctx, req.Mid, []int64{req.MomentId})
|
||||||
|
if err != nil {
|
||||||
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
|
logger.Error("GetZoneMomentUnlockMapByMidMomentIds fail, mid: %v, momentId: %v", req.Mid, req.MomentId)
|
||||||
|
return nil, ec, fmt.Errorf("无法获取动态解锁记录")
|
||||||
|
}
|
||||||
|
if _, ok := zmuMap[req.MomentId]; ok {
|
||||||
|
ec = errcode.ErrCodeVasSrvOk
|
||||||
|
data.HasBought = 1
|
||||||
|
return data, ec, nil
|
||||||
|
//ec = errcode.ErrCodeVasSrvFail
|
||||||
|
//return nil, ec, fmt.Errorf("已解锁该动态,请勿重复购买")
|
||||||
|
}
|
||||||
data.Name = "付费动态"
|
data.Name = "付费动态"
|
||||||
// 获取动态价格
|
// 获取动态价格
|
||||||
zmp, _ := _DefaultVas.GetZoneMomentPriceById(ctx, req.MomentId)
|
zmp, _ := _DefaultVas.GetZoneMomentPriceById(ctx, req.MomentId)
|
||||||
|
@ -680,6 +696,21 @@ func (s *Service) ZoneGetCashier(ctx *gin.Context, req *vasproto.ZoneGetCashierR
|
||||||
data.Price = zmp.Price
|
data.Price = zmp.Price
|
||||||
}
|
}
|
||||||
case dbstruct.ProductIdH5ZoneAdmission:
|
case dbstruct.ProductIdH5ZoneAdmission:
|
||||||
|
// 是否解锁了空间普通会员
|
||||||
|
zuMap, _ := _DefaultVas.GetZoneUnlockMapByMidZids(ctx, mid, []int64{zid})
|
||||||
|
if zu, ok := zuMap[mid]; ok && zu.IsUnlockAdmission() {
|
||||||
|
ec = errcode.ErrCodeVasSrvOk
|
||||||
|
data.HasBought = 1
|
||||||
|
return data, ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断主播是否开通空间
|
||||||
|
zone, err := _DefaultZone.GetById(ctx, zid)
|
||||||
|
if err != nil || zone == nil {
|
||||||
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
|
logger.Error("GetById fail, mid: %v, zid: %v, erR: %v", req.Mid, zid, err)
|
||||||
|
return nil, ec, fmt.Errorf("无法获取主播动态")
|
||||||
|
}
|
||||||
data.Name = "空间会员"
|
data.Name = "空间会员"
|
||||||
if zv == nil {
|
if zv == nil {
|
||||||
ec = errcode.ErrCodeVasSrvFail
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
|
@ -692,14 +723,27 @@ func (s *Service) ZoneGetCashier(ctx *gin.Context, req *vasproto.ZoneGetCashierR
|
||||||
ec = errcode.ErrCodeVasSrvFail
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
return nil, ec, fmt.Errorf("该主播未设置空间价格")
|
return nil, ec, fmt.Errorf("该主播未设置空间价格")
|
||||||
}
|
}
|
||||||
data.Price = zv.AdmissionPrice
|
data.Price = zv.IronfanshipPrice
|
||||||
case dbstruct.ProductIdH5ZoneSuperfanship:
|
case dbstruct.ProductIdH5ZoneSuperfanship:
|
||||||
|
// 判断主播是否开启超粉
|
||||||
|
if zv.IsSuperfanshipEnabled != 1 {
|
||||||
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
|
return nil, ec, fmt.Errorf("该主播未开启超粉空间")
|
||||||
|
}
|
||||||
|
// 用户是否解锁了空间超粉
|
||||||
|
zuMap, _ := _DefaultVas.GetZoneUnlockMapByMidZids(ctx, mid, []int64{zid})
|
||||||
|
if zu, ok := zuMap[mid]; ok && zu.IsUnlockSuperfanship() {
|
||||||
|
ec = errcode.ErrCodeVasSrvOk
|
||||||
|
data.HasBought = 1
|
||||||
|
return data, ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
data.Name = "超粉"
|
data.Name = "超粉"
|
||||||
if zv == nil {
|
if zv == nil {
|
||||||
ec = errcode.ErrCodeVasSrvFail
|
ec = errcode.ErrCodeVasSrvFail
|
||||||
return nil, ec, fmt.Errorf("该主播未设置空间价格")
|
return nil, ec, fmt.Errorf("该主播未设置空间价格")
|
||||||
}
|
}
|
||||||
data.Price = zv.AdmissionPrice
|
data.Price = zv.SuperfanshipPrice
|
||||||
data.Validity = zv.GetSuperfanshipDurationDesc()
|
data.Validity = zv.GetSuperfanshipDurationDesc()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue