feat:订单退款购物车
This commit is contained in:
1 parent
5c6549713d
commit
5647b14b9d
11 files changed
+350
-113
No files matched your search
@@ -220,6 +220,33 @@ CREATE TABLE hard_car
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='购物车表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_after_sale_car
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `hard_after_sale_car`;
|
||||
CREATE TABLE hard_after_sale_car
|
||||
(
|
||||
id bigint NOT NULL COMMENT '编号',
|
||||
refund_id bigint NOT NULL COMMENT '退款id',
|
||||
parent_car_id bigint NOT NULL COMMENT '父购物车id',
|
||||
product_id bigint NOT NULL COMMENT '商品id',
|
||||
product_name varchar(128) NOT NULL COMMENT '商品名',
|
||||
product_pic varchar(256) NOT NULL COMMENT '商品图片',
|
||||
product_price bigint NOT NULL COMMENT '商品价格',
|
||||
product_count int NOT NULL COMMENT '数量',
|
||||
product_weight decimal(10, 2) COMMENT '重量',
|
||||
total_price bigint COMMENT '总价(分)',
|
||||
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
|
||||
create_time datetime DEFAULT NULL COMMENT '创建时间',
|
||||
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
|
||||
update_time datetime DEFAULT NULL COMMENT '修改时间',
|
||||
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
|
||||
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COLLATE = utf8mb4_general_ci COMMENT ='购物车退款表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for hard_parent_car
|
||||
-- ----------------------------
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.bsd.pjcloud.hard.api.dto.order;
|
||||
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleCarEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class HardAdminRefundReinforceDto extends HardAdminRefundDto {
|
||||
|
||||
/**
|
||||
* 退款商品列表
|
||||
*/
|
||||
private List<HardAfterSaleCarEntity> carList;
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.bsd.pjcloud.hard.api.entity.order;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 购物车退款表
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("hard_after_sale_car")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "购物车退款表")
|
||||
public class HardAfterSaleCarEntity extends Model<HardAfterSaleCarEntity> {
|
||||
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "编号")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 退款id
|
||||
*/
|
||||
@Schema(description = "退款id")
|
||||
private Long refundId;
|
||||
|
||||
/**
|
||||
* 父购物车id
|
||||
*/
|
||||
@Schema(description = "父购物车id")
|
||||
private Long parentCarId;
|
||||
|
||||
/**
|
||||
* 商品id
|
||||
*/
|
||||
@Schema(description = "商品id")
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 商品名
|
||||
*/
|
||||
@Schema(description = "商品名")
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 商品图片
|
||||
*/
|
||||
@Schema(description = "商品图片")
|
||||
private String productPic;
|
||||
|
||||
/**
|
||||
* 商品价格
|
||||
*/
|
||||
@Schema(description = "商品价格")
|
||||
private Long productPrice;
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
@Schema(description = "数量")
|
||||
private Integer productCount;
|
||||
|
||||
/**
|
||||
* 重量
|
||||
*/
|
||||
@Schema(description = "重量")
|
||||
private BigDecimal productWeight;
|
||||
|
||||
/**
|
||||
* 总价(分)
|
||||
*/
|
||||
@Schema(description = "总价(分)")
|
||||
private Long totalPrice;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+102
-101
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
@@ -23,154 +24,154 @@ import java.time.LocalDateTime;
|
||||
@Schema(description = "售后订单")
|
||||
public class HardAfterSaleEntity extends Model<HardAfterSaleEntity> {
|
||||
|
||||
/**
|
||||
* 售后编号
|
||||
*/
|
||||
/**
|
||||
* 售后编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description="售后编号")
|
||||
@Schema(description = "售后编号")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 售后单号
|
||||
*/
|
||||
@Schema(description="售后单号")
|
||||
/**
|
||||
* 售后单号
|
||||
*/
|
||||
@Schema(description = "售后单号")
|
||||
private String no;
|
||||
|
||||
/**
|
||||
* 售后类型
|
||||
*/
|
||||
@Schema(description="售后类型")
|
||||
/**
|
||||
* 售后类型
|
||||
*/
|
||||
@Schema(description = "售后类型")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 售后状态
|
||||
*/
|
||||
@Schema(description="售后状态")
|
||||
/**
|
||||
* 售后状态
|
||||
*/
|
||||
@Schema(description = "售后状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 售后方式
|
||||
*/
|
||||
@Schema(description="售后方式")
|
||||
/**
|
||||
* 售后方式
|
||||
*/
|
||||
@Schema(description = "售后方式")
|
||||
private String way;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@Schema(description="用户id")
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 申请原因
|
||||
*/
|
||||
@Schema(description="申请原因")
|
||||
/**
|
||||
* 申请原因
|
||||
*/
|
||||
@Schema(description = "申请原因")
|
||||
private String applyReason;
|
||||
|
||||
/**
|
||||
* 补充描述
|
||||
*/
|
||||
@Schema(description="补充描述")
|
||||
/**
|
||||
* 补充描述
|
||||
*/
|
||||
@Schema(description = "补充描述")
|
||||
private String applyDescription;
|
||||
|
||||
/**
|
||||
* 补充凭证图片
|
||||
*/
|
||||
@Schema(description="补充凭证图片")
|
||||
/**
|
||||
* 补充凭证图片
|
||||
*/
|
||||
@Schema(description = "补充凭证图片")
|
||||
private String applyPicUrls;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@Schema(description="订单编号")
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@Schema(description = "订单编号")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 订单流水号
|
||||
*/
|
||||
@Schema(description="订单流水号")
|
||||
/**
|
||||
* 订单流水号
|
||||
*/
|
||||
@Schema(description = "订单流水号")
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 审批时间
|
||||
*/
|
||||
@Schema(description="审批时间")
|
||||
/**
|
||||
* 审批时间
|
||||
*/
|
||||
@Schema(description = "审批时间")
|
||||
private LocalDateTime auditTime;
|
||||
|
||||
/**
|
||||
* 审批人
|
||||
*/
|
||||
@Schema(description="审批人")
|
||||
/**
|
||||
* 审批人
|
||||
*/
|
||||
@Schema(description = "审批人")
|
||||
private Long auditUserId;
|
||||
|
||||
/**
|
||||
* 审批备注
|
||||
*/
|
||||
@Schema(description="审批备注")
|
||||
/**
|
||||
* 审批备注
|
||||
*/
|
||||
@Schema(description = "审批备注")
|
||||
private String auditReason;
|
||||
|
||||
/**
|
||||
* 退款金额,单位:分
|
||||
*/
|
||||
@Schema(description="退款金额,单位:分")
|
||||
/**
|
||||
* 退款金额,单位:分
|
||||
*/
|
||||
@Schema(description = "退款金额,单位:分")
|
||||
private Integer refundPrice;
|
||||
|
||||
/**
|
||||
* 支付退款编号
|
||||
*/
|
||||
@Schema(description="支付退款编号")
|
||||
/**
|
||||
* 支付退款编号
|
||||
*/
|
||||
@Schema(description = "支付退款编号")
|
||||
private Long payRefundId;
|
||||
|
||||
/**
|
||||
* 退款时间
|
||||
*/
|
||||
@Schema(description="退款时间")
|
||||
/**
|
||||
* 退款时间
|
||||
*/
|
||||
@Schema(description = "退款时间")
|
||||
private LocalDateTime refundTime;
|
||||
|
||||
/**
|
||||
* 退货时间
|
||||
*/
|
||||
@Schema(description="退货时间")
|
||||
/**
|
||||
* 退货时间
|
||||
*/
|
||||
@Schema(description = "退货时间")
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="创建人")
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="创建时间")
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description="修改人")
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改人")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description="修改时间")
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@Schema(description = "修改时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description="删除标记")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@Schema(description = "删除标记")
|
||||
private String delFlag;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description="租户ID")
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@Schema(description = "租户ID")
|
||||
private Long tenantId;
|
||||
}
|
||||
+10
-4
@@ -10,10 +10,7 @@ import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
import com.bsd.pjcloud.common.security.util.SecurityUtils;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAdminRefundDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAfterSaleDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardDisagreeDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.*;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleLogEntity;
|
||||
import com.bsd.pjcloud.hard.api.vo.order.HardAfterSaleVo;
|
||||
@@ -115,6 +112,15 @@ public class HardAfterSaleController {
|
||||
return R.ok(hardAfterSaleService.adminRefund(dto, user));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/admin-reinforce-refund")
|
||||
@Operation(summary = "后台强化退款")
|
||||
@Idempotent(expireTime = 10, info = "正在退款中,请勿重复操作")
|
||||
@PreAuthorize("@pms.hasPermission('hard_hardAfterSale_adminRefund')")
|
||||
public R<Boolean> adminReinforceRefund(@RequestBody HardAdminRefundReinforceDto dto) {
|
||||
PjcloudUser user = SecurityUtils.getUser();
|
||||
return R.ok(hardAfterSaleService.adminReinforceRefund(dto, user));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/history")
|
||||
@Operation(summary = "查询售后进度")
|
||||
public R<List<HardAfterSaleLogEntity>> history(@RequestParam Long id) {
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.bsd.pjcloud.hard.mapper.order;
|
||||
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleCarEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface HardAfterSaleCarMapper extends PjcloudBaseMapper<HardAfterSaleCarEntity> {
|
||||
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.bsd.pjcloud.hard.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleCarEntity;
|
||||
|
||||
public interface HardAfterSaleCarService extends IService<HardAfterSaleCarEntity> {
|
||||
|
||||
}
|
||||
+6
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAdminRefundDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAdminRefundReinforceDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAfterSaleDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardDisagreeDto;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleEntity;
|
||||
@@ -69,6 +70,11 @@ public interface HardAfterSaleService extends IService<HardAfterSaleEntity> {
|
||||
*/
|
||||
Boolean adminRefund(HardAdminRefundDto dto, PjcloudUser user);
|
||||
|
||||
/**
|
||||
* 后台强化退款
|
||||
*/
|
||||
Boolean adminReinforceRefund(HardAdminRefundReinforceDto dto, PjcloudUser user);
|
||||
|
||||
/**
|
||||
* 消费机限时退款
|
||||
*/
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.bsd.pjcloud.hard.service.impl.order;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleCarEntity;
|
||||
import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleCarMapper;
|
||||
import com.bsd.pjcloud.hard.service.HardAfterSaleCarService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 购物车退款表
|
||||
*/
|
||||
@Service
|
||||
public class HardAfterSaleCarServiceImpl extends ServiceImpl<HardAfterSaleCarMapper, HardAfterSaleCarEntity> implements HardAfterSaleCarService {
|
||||
|
||||
}
|
||||
+23
-8
@@ -28,11 +28,9 @@ import com.bsd.pjcloud.hard.api.constant.refund.HardRefundStatusEnum;
|
||||
import com.bsd.pjcloud.hard.api.constant.refund.HardSaleStatusEnum;
|
||||
import com.bsd.pjcloud.hard.api.constant.refund.HardSaleTypeEnum;
|
||||
import com.bsd.pjcloud.hard.api.dto.ali.AliRefundDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAdminRefundDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardAfterSaleDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardDisagreeDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
|
||||
import com.bsd.pjcloud.hard.api.dto.order.*;
|
||||
import com.bsd.pjcloud.hard.api.dto.wechat.WechatRefundDto;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleCarEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleLogEntity;
|
||||
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
|
||||
@@ -401,10 +399,6 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
|
||||
if (!HardOrderTypeEnum.isRefund(orderTypeEnum)) {
|
||||
throw new RuntimeException("该订单类型不支持退款");
|
||||
}
|
||||
// 校验订单是否已退款
|
||||
// if (ObjectUtil.isNotEmpty(order.getPayRefundId())) {
|
||||
// throw new RuntimeException("发起退款失败,订单已退款");
|
||||
// }
|
||||
// 查询所有成功的退款
|
||||
List<HardAfterSaleEntity> afterList = baseMapper.selectList(new LambdaQueryWrapper<HardAfterSaleEntity>()
|
||||
.eq(HardAfterSaleEntity::getOrderId, order.getId())
|
||||
@@ -419,6 +413,12 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
|
||||
return orderVo;
|
||||
}
|
||||
|
||||
public boolean validateOrderCarCanRefund(HardOrderEntity order, List<HardAfterSaleCarEntity> carList) {
|
||||
if (ObjectUtil.isEmpty(carList)) throw new RuntimeException("售后商品列表不能为空");
|
||||
// 查询订单购物车
|
||||
return false;
|
||||
}
|
||||
|
||||
private HardAfterSaleEntity validateAfterSaleAuditable(Long id) {
|
||||
HardAfterSaleEntity afterSale = baseMapper.selectById(id);
|
||||
if (afterSale == null) throw new RuntimeException("售后单不存在");
|
||||
@@ -500,6 +500,21 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台强化退款
|
||||
*/
|
||||
@Override
|
||||
public Boolean adminReinforceRefund(HardAdminRefundReinforceDto dto, PjcloudUser user) {
|
||||
// 校验订单是否可以退款
|
||||
HardOrderVo order = validateOrderCanRefund(dto.getOrderId());
|
||||
// 校验订单购物车是否支持退款
|
||||
validateOrderCarCanRefund(order, dto.getCarList());
|
||||
// 添加售后订单
|
||||
HardAfterSaleEntity saveObj = BeanUtils.toBean(dto, HardAfterSaleEntity.class);
|
||||
String refundCode = "hard_" + order.getId();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费机限时退款
|
||||
*/
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.bsd.pjcloud.hard.mapper.order.HardAfterSaleCarMapper">
|
||||
|
||||
</mapper>
|
||||
Reference in new issue
Block a user