@@ -0,0 +1,468 @@
|
||||
package ai_changeface_img_ser
|
||||
|
||||
import (
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/httputil"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/aichangefaceimgmod"
|
||||
"91porn-server/models/v/aichangefacevidmod"
|
||||
"91porn-server/models/v/aiplazamod"
|
||||
"91porn-server/models/v/txnmod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
"91porn-server/web/proto"
|
||||
"91porn-server/web/webg"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func List(req *aichangefaceimgmod.WebListRequest) (interface{}, stderr.Code) {
|
||||
var data = map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"count": 0,
|
||||
}
|
||||
count, err := aichangefaceimgmod.CountDocument(req.Filter())
|
||||
if err != nil {
|
||||
return nil, stderr.ErrDbQueryError
|
||||
}
|
||||
if count == 0 {
|
||||
return data, stderr.Success
|
||||
}
|
||||
list, err := aichangefaceimgmod.QueryAllDocument(req.Filter(), req.Options())
|
||||
if err != nil {
|
||||
return data, stderr.ErrDbQueryError
|
||||
}
|
||||
data["count"] = count
|
||||
data["list"] = list
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
func Update(act string, req *aichangefaceimgmod.EditCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefaceimgmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
if data.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
if (*req.Status == aichangefaceimgmod.FAILURE || *req.Status == aichangefaceimgmod.REFUND) &&
|
||||
(data.Status == aichangefaceimgmod.FAILURE || data.Status == aichangefaceimgmod.REFUND) {
|
||||
return stderr.Success
|
||||
}
|
||||
// 根据状态判断
|
||||
switch *req.Status {
|
||||
case aichangefaceimgmod.Processing:
|
||||
if data.Status != aichangefaceimgmod.SUCCESS {
|
||||
return stderr.AiChangeFaceImgStatusIsErr
|
||||
}
|
||||
err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.SUCCESS:
|
||||
if err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.FAILURE, aichangefaceimgmod.REFUND:
|
||||
// 失败处理 退金币
|
||||
deAmt := -data.DebitAmountCoin
|
||||
deIncome := -data.DebitIncomeCoin
|
||||
uid := data.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, data.ID, data.Status, *req.Status, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if data.Count > 0 || data.IsFreeTimes {
|
||||
deCount := data.Count
|
||||
if data.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if data.DebitAmountCoin > 0 || data.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func UpdateNew(act string, req *aichangefaceimgmod.EditCond) stderr.Code {
|
||||
// 获取Ai记录
|
||||
data, err := aichangefaceimgmod.FindDataByID(req.ID)
|
||||
if err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.ID.IsZero() {
|
||||
return stderr.CodeEmptyData
|
||||
}
|
||||
if *req.Status == aichangefaceimgmod.StatusSubmit && data.Status == aichangefaceimgmod.StatusSubmit {
|
||||
return stderr.Success
|
||||
}
|
||||
if (*req.Status == aichangefaceimgmod.FAILURE || *req.Status == aichangefaceimgmod.REFUND) &&
|
||||
(data.Status == aichangefaceimgmod.FAILURE || data.Status == aichangefaceimgmod.REFUND) {
|
||||
return stderr.Success
|
||||
}
|
||||
// 根据状态判断
|
||||
switch *req.Status {
|
||||
case aichangefaceimgmod.Processing:
|
||||
err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act))
|
||||
if err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.StatusSubmit:
|
||||
//提交订单到第三方
|
||||
notifyURL, notifyErr := webg.AICallbackURL("/api/web/admin/ai/change_face_img/callback")
|
||||
if notifyErr != nil {
|
||||
log.Error("AI图片换脸回调地址配置异常", log.E(notifyErr))
|
||||
return stderr.Failure
|
||||
}
|
||||
httpReq := proto.AiChangeFaceReq{
|
||||
AppId: int(commod.KFK_APPID),
|
||||
OrderNumAPP: data.ID.Hex(),
|
||||
Title: data.ModTitle,
|
||||
SourceImageUrl: data.OriginPic,
|
||||
NotifyUrl: notifyURL,
|
||||
}
|
||||
url := webg.Conf.URL.AiServer + "/api/changeface/prd/changeFaceByVideo"
|
||||
//第三方请求地址
|
||||
var resp proto.AiChangeFaceResp
|
||||
codeJ, err := httputil.DefaultClientPostJsonWithResp(&resp, url, nil, httpReq)
|
||||
if err != nil {
|
||||
log.Error("CreateChangeFace httputil.DefaultClientPostJsonWithResp err", log.E(err))
|
||||
return stderr.Failure
|
||||
}
|
||||
if codeJ != 200 {
|
||||
log.Error("CreateChangeFace req http code error", log.Any("code", codeJ))
|
||||
return stderr.Failure
|
||||
}
|
||||
if resp.Code == 40000 {
|
||||
return stderr.ChangeFaceNoModule
|
||||
}
|
||||
if err = aichangefaceimgmod.Edit(nil, req.Filter(), req.Update(act)); err != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
case aichangefaceimgmod.FAILURE, aichangefaceimgmod.REFUND:
|
||||
// 失败处理 退金币
|
||||
deAmt := -data.DebitAmountCoin
|
||||
deIncome := -data.DebitIncomeCoin
|
||||
uid := data.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
updateSet, _ := req.Update(act)["$set"].(bson.M)
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, data.ID, data.Status, *req.Status, updateSet)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if data.Count > 0 || data.IsFreeTimes {
|
||||
deCount := data.Count
|
||||
if data.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if data.DebitAmountCoin > 0 || data.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if data.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if data.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
func CallBackOrder(req *aichangefacevidmod.CallbackReq) stderr.Code {
|
||||
//根据订单号更新ai换脸订单
|
||||
id, _ := primitive.ObjectIDFromHex(req.AppOrderNum)
|
||||
//查询该笔订单是否存在
|
||||
aichangeFaceInfo, err := aichangefaceimgmod.FindByID(nil, id)
|
||||
if aichangeFaceInfo == nil || err != nil {
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
oldOrderTime := time.Date(2025, 9, 15, 0, 0, 0, 0, time.Local)
|
||||
if aichangeFaceInfo.CreatedAt.Before(oldOrderTime) &&
|
||||
(aichangeFaceInfo.Status == aichangefaceimgmod.SUCCESS || aichangeFaceInfo.Status == aichangefaceimgmod.FAILURE || aichangeFaceInfo.Status == aichangefaceimgmod.REFUND) {
|
||||
// 以前的单子如果处理过了就新下资源和状态
|
||||
set := bson.M{"newPic": []string{strings.TrimSpace(req.ImgUrl)}}
|
||||
if req.Msg == "操作成功" {
|
||||
set["status"] = aichangefaceimgmod.SUCCESS
|
||||
} else {
|
||||
set["remark"] = req.Msg
|
||||
set["status"] = aichangefaceimgmod.REFUND
|
||||
}
|
||||
_ = aichangefaceimgmod.Update(nil, id, set)
|
||||
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
if aichangeFaceInfo.Status != aichangefaceimgmod.StatusSubmit {
|
||||
log.Error("AI图片换脸-重复回调或者状态不正确", log.Any("id", aichangeFaceInfo.ID.Hex()), log.Any("status", aichangeFaceInfo.Status))
|
||||
return stderr.Success
|
||||
}
|
||||
|
||||
var status = aichangefaceimgmod.SUCCESS
|
||||
//处理金币退款以及后续操作
|
||||
set := bson.M{}
|
||||
if req.ImgUrl != "" {
|
||||
set["newPic"] = append([]string{}, strings.TrimSpace(req.ImgUrl))
|
||||
}
|
||||
if req.Msg != "操作成功" {
|
||||
set["remark"] = req.Msg
|
||||
status = aichangefaceimgmod.REFUND
|
||||
}
|
||||
set["status"] = status
|
||||
switch status {
|
||||
case aichangefaceimgmod.SUCCESS:
|
||||
transitioned, updateErr := aichangefaceimgmod.TransitionStatus(nil, id, aichangefaceimgmod.StatusSubmit, aichangefaceimgmod.SUCCESS, set)
|
||||
if updateErr != nil {
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
if !transitioned {
|
||||
return stderr.Success
|
||||
}
|
||||
common.Go(func() {
|
||||
if aichangeFaceInfo.ShareStatus != 1 {
|
||||
return
|
||||
}
|
||||
// 新增一个分享
|
||||
alPlaza := aiplazamod.AiPlaza{
|
||||
Type: 1,
|
||||
OrderId: aichangeFaceInfo.ID,
|
||||
Template: aichangeFaceInfo.MId,
|
||||
Uid: aichangeFaceInfo.UID,
|
||||
Title: aichangeFaceInfo.ShareTitle,
|
||||
OriginalImage: aichangeFaceInfo.ModPic,
|
||||
OriginalVideo: "",
|
||||
OriginContent: "",
|
||||
GenerateImage: req.ImgUrl,
|
||||
GenerateVideo: "",
|
||||
Status: aiplazamod.DefaultStatus,
|
||||
}
|
||||
_, err = aiplazamod.Insert(nil, alPlaza)
|
||||
if err != nil {
|
||||
log.Error("新增ai广场帖子失败", log.Any("alPlaza", alPlaza), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
return stderr.Success
|
||||
case aichangefaceimgmod.REFUND:
|
||||
//换脸失败,进行退款操作
|
||||
// 失败处理 退金币
|
||||
deAmt := -aichangeFaceInfo.DebitAmountCoin
|
||||
deIncome := -aichangeFaceInfo.DebitIncomeCoin
|
||||
uid := aichangeFaceInfo.UID
|
||||
if err = webg.VideoDB.Trans(func(tool *db.MongoTool) error {
|
||||
transitioned, transitionErr := aichangefaceimgmod.TransitionStatus(tool, id, aichangefaceimgmod.StatusSubmit, aichangefaceimgmod.REFUND, set)
|
||||
if transitionErr != nil {
|
||||
return transitionErr
|
||||
}
|
||||
if !transitioned {
|
||||
return nil
|
||||
}
|
||||
var tl []txnmod.TransactionLog
|
||||
// 根据是否是免费次数退回
|
||||
if aichangeFaceInfo.Count > 0 || aichangeFaceInfo.IsFreeTimes {
|
||||
deCount := aichangeFaceInfo.Count
|
||||
if aichangeFaceInfo.Count == 0 {
|
||||
deCount = 1
|
||||
}
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.DebitAiFreeTimes(tool, -deCount, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAiFreeTimes error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: deCount,
|
||||
ActualAmount: float64(deCount),
|
||||
TranType: txnmod.AiChangeFaceImgDebitFreeTimesReturn.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgDebitFreeTimesReturn),
|
||||
Desc: fmt.Sprintf("AI图片换脸免费次数退返[%d次]", deCount),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
if aichangeFaceInfo.DebitAmountCoin > 0 || aichangeFaceInfo.DebitIncomeCoin > 0 {
|
||||
// 加钱包余额
|
||||
wallet, wErr := walletmod.ReturnAmountAndIncome(tool, deAmt, deIncome, uid)
|
||||
if wErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit walletmod.DebitAmount error:%+v:", wErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
if aichangeFaceInfo.DebitIncomeCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deIncome,
|
||||
ActualAmount: float64(-deIncome),
|
||||
TranType: txnmod.AiChangeFaceImgReturnIncomeGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnIncomeGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸收益金币退返-%d", -deIncome),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
if aichangeFaceInfo.DebitAmountCoin > 0 {
|
||||
tl = append(tl, txnmod.TransactionLog{
|
||||
TransNo: primitive.NewObjectID(),
|
||||
UID: uid,
|
||||
Amount: -deAmt,
|
||||
ActualAmount: float64(-deAmt),
|
||||
TranType: txnmod.AiChangeFaceImgReturnGold.Key(),
|
||||
TranTypeInt: int64(txnmod.AiChangeFaceImgReturnGold),
|
||||
Desc: fmt.Sprintf("AI图片换脸金币退返-%d", -deAmt),
|
||||
RealAmount: walletmod.GetRealAmount(wallet),
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(tl) > 0 {
|
||||
txnErr := txnmod.InsertManyTransactionLog(tool, tl)
|
||||
if txnErr != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit txnmod.InsertManyTransactionLog error:%+v:", txnErr), log.Any("uid", uid))
|
||||
return stderr.ErrDbQueryError
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(fmt.Sprintf("Handle AiChangeFaceImg Edit Trans error:%+v;uid:%v;", err, uid))
|
||||
return stderr.ErrDbUpdateError
|
||||
}
|
||||
}
|
||||
return stderr.Success
|
||||
}
|
||||
Reference in New Issue
Block a user