refactor: service module and dao module
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// 数据库表名
|
||||
@@ -15,3 +21,20 @@ const (
|
||||
NumberNull = 0
|
||||
StringNull = ""
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
// RestPage 分页查询
|
||||
// page 设置起始页、每页条数,
|
||||
// name 查询目标表的名称
|
||||
// query 查询条件,
|
||||
// dest 查询结果绑定的结构体,
|
||||
// bind 绑定表结构对应的结构体
|
||||
func restPage(page models.Page, name string, query interface{}, dest interface{}, bind interface{}) (int64, error) {
|
||||
if page.PageNum > 0 && page.PageSize > 0 {
|
||||
offset := (page.PageNum - 1) * page.PageSize
|
||||
global.Db.Offset(offset).Limit(page.PageSize).Table(name).Where(query).Find(dest)
|
||||
}
|
||||
res := global.Db.Table(name).Where(query).Find(bind)
|
||||
return res.RowsAffected, res.Error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ContractDao struct {
|
||||
}
|
||||
|
||||
func NewContractDao() *ContractDao {
|
||||
return &ContractDao{}
|
||||
}
|
||||
|
||||
func (c *ContractDao) Create(param *models.ContractCreateParam) error {
|
||||
contract := models.Contract{
|
||||
Name: param.Name,
|
||||
Amount: param.Amount,
|
||||
BeginTime: param.BeginTime,
|
||||
OverTime: param.OverTime,
|
||||
Remarks: param.Remarks,
|
||||
Cid: param.Cid,
|
||||
Productlist: param.Productlist,
|
||||
Status: param.Status,
|
||||
Creator: param.Creator,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Create(&contract).Error
|
||||
}
|
||||
|
||||
func (c *ContractDao) Update(param *models.ContractUpdateParam) error {
|
||||
contract := models.Contract{
|
||||
Id: param.Id,
|
||||
Name: param.Name,
|
||||
Amount: param.Amount,
|
||||
BeginTime: param.BeginTime,
|
||||
OverTime: param.OverTime,
|
||||
Remarks: param.Remarks,
|
||||
Cid: param.Cid,
|
||||
Productlist: param.Productlist,
|
||||
Status: param.Status,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
db := global.Db.Model(&contract).Select("*").Omit("id", "creator", "created")
|
||||
return db.Updates(&contract).Error
|
||||
}
|
||||
|
||||
func (c *ContractDao) Delete(param *models.ContractDeleteParam) error {
|
||||
return global.Db.Delete(&models.Contract{}, param.Ids).Error
|
||||
}
|
||||
|
||||
func (c *ContractDao) GetList(param *models.ContractQueryParam) ([]*models.ContractList, int64, error) {
|
||||
contractList := make([]*models.ContractList, 0)
|
||||
s := "contract.id, contract.name, contract.amount, contract.begin_time, contract.over_time, customer.name as cname, contract.remarks, contract.status, contract.created, contract.updated"
|
||||
j := "inner join customer on contract.cid = customer.id and contract.creator = ?"
|
||||
|
||||
// 分页查询
|
||||
offset := (param.Page.PageNum - 1) * param.Page.PageSize
|
||||
mdb := global.Db.Offset(offset).Limit(param.Page.PageSize).Table(CONTRACT).Select(s)
|
||||
var err error
|
||||
if param.Id != 0 {
|
||||
err = mdb.Joins(j+" and contract.id = ?", param.Creator, param.Id).Scan(&contractList).Error
|
||||
} else {
|
||||
err = mdb.Joins(j, param.Creator).Scan(&contractList).Error
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var rows int64
|
||||
global.Db.Raw("select count(*) from contract where creator = ?", param.Creator).Scan(&rows)
|
||||
return contractList, rows, nil
|
||||
}
|
||||
|
||||
func (c *ContractDao) GetListByUid(uid int64) ([]*models.ContractList, error) {
|
||||
contracts := make([]*models.ContractList, 0)
|
||||
s := "contract.id, contract.name, contract.amount, contract.begin_time, contract.over_time, customer.name as cname, contract.remarks, contract.status, contract.created, contract.updated"
|
||||
j := "left join customer on contract.cid = customer.id and contract.creator = ?"
|
||||
err := global.Db.Table(CONTRACT).Select(s).Joins(j, uid).Scan(&contracts).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contracts, nil
|
||||
}
|
||||
|
||||
func (c *ContractDao) GetInfo(param *models.ContractQueryParam) (*models.ContractInfo, error) {
|
||||
contract := models.Contract{
|
||||
Id: param.Id,
|
||||
}
|
||||
contractInfo := models.ContractInfo{}
|
||||
err := global.Db.Table(CONTRACT).Where(&contract).First(&contractInfo).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &contractInfo, nil
|
||||
}
|
||||
|
||||
func (c *ContractDao) GetAddedPList(id int64) (*models.Contract, error) {
|
||||
var contract models.Contract
|
||||
err := global.Db.Table(CONTRACT).Select("productlist").First(&contract, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &contract, nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CustomerDao struct {
|
||||
}
|
||||
|
||||
func NewCustomerDao() *CustomerDao {
|
||||
return &CustomerDao{}
|
||||
}
|
||||
|
||||
func (c *CustomerDao) Create(param *models.CustomerCreateParam) error {
|
||||
customer := models.Customer{
|
||||
Name: param.Name,
|
||||
Source: param.Source,
|
||||
Phone: param.Phone,
|
||||
Email: param.Email,
|
||||
Industry: param.Industry,
|
||||
Level: param.Level,
|
||||
Remarks: param.Remarks,
|
||||
Region: param.Region,
|
||||
Address: param.Address,
|
||||
Status: 1,
|
||||
Creator: param.Creator,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Create(&customer).Error
|
||||
}
|
||||
|
||||
func (c *CustomerDao) Update(param *models.CustomerUpdateParam) error {
|
||||
customer := models.Customer{
|
||||
Id: param.Id,
|
||||
Name: param.Name,
|
||||
Source: param.Source,
|
||||
Phone: param.Phone,
|
||||
Email: param.Email,
|
||||
Industry: param.Industry,
|
||||
Level: param.Level,
|
||||
Remarks: param.Remarks,
|
||||
Region: param.Region,
|
||||
Address: param.Address,
|
||||
Status: param.Status,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
db := global.Db.Model(&customer).Select("*").Omit("id", "creator", "created")
|
||||
return db.Updates(&customer).Error
|
||||
}
|
||||
|
||||
func (c *CustomerDao) Delete(param *models.CustomerDeleteParam) error {
|
||||
return global.Db.Delete(&models.Customer{}, param.Ids).Error
|
||||
}
|
||||
|
||||
func (c *CustomerDao) IsExists(name string, uid int64) bool {
|
||||
var customer models.Customer
|
||||
db := global.Db.Table(CUSTOMER).Where("name = ? and creator = ?", name, uid).First(&customer)
|
||||
return db.RowsAffected != NumberNull
|
||||
}
|
||||
|
||||
func (c *CustomerDao) GetList(param *models.CustomerQueryParam) ([]*models.CustomerList, int64, error) {
|
||||
customer := models.Customer{
|
||||
Name: param.Name,
|
||||
Creator: param.Creator,
|
||||
}
|
||||
customerList := make([]*models.CustomerList, 0)
|
||||
rows, err := restPage(param.Page, CUSTOMER, customer, &customerList, &[]*models.CustomerList{})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return customerList, rows, nil
|
||||
}
|
||||
|
||||
func (c *CustomerDao) GetInfo(param *models.CustomerQueryParam) (*models.CustomerInfo, error) {
|
||||
customer := models.Customer{
|
||||
Id: param.Id,
|
||||
}
|
||||
customerInfo := models.CustomerInfo{}
|
||||
err := global.Db.Table(CUSTOMER).Where(&customer).First(&customerInfo).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &customerInfo, nil
|
||||
}
|
||||
|
||||
func (c *CustomerDao) GetOption(uid int64) ([]*models.CustomerOption, error) {
|
||||
customer := models.Customer{
|
||||
Creator: uid,
|
||||
}
|
||||
option := make([]*models.CustomerOption, 0)
|
||||
err := global.Db.Table(CUSTOMER).Where(&customer).Find(&option).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return option, nil
|
||||
}
|
||||
|
||||
func (c *CustomerDao) GetListByUid(uid int64) ([]*models.Customer, error) {
|
||||
customers := make([]*models.Customer, 0)
|
||||
err := global.Db.Where("creator = ?", uid).Find(&customers).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return customers, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
)
|
||||
|
||||
type DashboardDao struct {
|
||||
}
|
||||
|
||||
func NewDashboardDao() *DashboardDao {
|
||||
return &DashboardDao{}
|
||||
}
|
||||
|
||||
func (d *DashboardDao) Count(uid int64, days int) models.DashboardSum {
|
||||
var ds models.DashboardSum
|
||||
global.Db.Raw("select count(*) from customer where creator = ?", uid).Scan(&ds.Customers)
|
||||
global.Db.Raw("select count(*) from contract where creator = ?", uid).Scan(&ds.Contracts)
|
||||
global.Db.Raw("select sum(amount) from contract where creator = ?", uid).Scan(&ds.ContractAmount)
|
||||
global.Db.Raw("select count(*) from product where creator = ?", uid).Scan(&ds.Products)
|
||||
|
||||
s := "industry as name, count(*) as value"
|
||||
global.Db.Model(&models.Customer{}).Select(s).Where("creator = ?", uid).Group("industry").Find(&ds.CustomerIndustry)
|
||||
return ds
|
||||
}
|
||||
|
||||
func (d *DashboardDao) AmountSum(start, end, uid int64) float64 {
|
||||
var as float64
|
||||
con := "created > ? and created < ? and status = 1 and creator = ?"
|
||||
global.Db.Table(CONTRACT).Where(con, start, end, uid).Pluck("amount", &as)
|
||||
return as
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Read = 1 // 已读
|
||||
UnRead = 2 // 未读
|
||||
)
|
||||
|
||||
type NoticeDao struct {
|
||||
}
|
||||
|
||||
func NewNoticeDao() *NoticeDao {
|
||||
return &NoticeDao{}
|
||||
}
|
||||
|
||||
func (n *NoticeDao) Create(param *models.NoticeCreateParam) error {
|
||||
notice := models.Notice{
|
||||
Content: param.Content,
|
||||
Status: UnRead,
|
||||
Creator: param.Creator,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Create(¬ice).Error
|
||||
}
|
||||
|
||||
func (n *NoticeDao) Update(param *models.NoticeUpdateParam) error {
|
||||
notice := models.Notice{
|
||||
Id: param.Id,
|
||||
Status: Read,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Model(¬ice).Updates(¬ice).Error
|
||||
}
|
||||
|
||||
func (n *NoticeDao) GetUnReadCountByUid(uid int64) (models.UnReadNotice, error) {
|
||||
var unRead models.UnReadNotice
|
||||
raw := "select count(*) from notice where status = 2 and creator = ?"
|
||||
if err := global.Db.Raw(raw, uid).Scan(&unRead.Count).Error; err != nil {
|
||||
return unRead, err
|
||||
}
|
||||
return unRead, nil
|
||||
}
|
||||
|
||||
func (n *NoticeDao) Delete(param *models.NoticeDeleteParam) error {
|
||||
return global.Db.Delete(&models.Notice{}, param.Ids).Error
|
||||
}
|
||||
|
||||
func (n *NoticeDao) GetList(uid int64) ([]*models.NoticeList, error) {
|
||||
noticeList := make([]*models.NoticeList, 0)
|
||||
db := global.Db.Table(NOTICE).Where("creator = ?", uid)
|
||||
if err := db.Order("status desc").Order("created desc").Find(¬iceList).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return noticeList, nil
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProductDao struct {
|
||||
}
|
||||
|
||||
func NewProductDao() *ProductDao {
|
||||
return &ProductDao{}
|
||||
}
|
||||
|
||||
func (p *ProductDao) Create(param *models.ProductCreateParam) error {
|
||||
product := models.Product{
|
||||
Name: param.Name,
|
||||
Type: param.Type,
|
||||
Unit: param.Unit,
|
||||
Code: param.Code,
|
||||
Price: param.Price,
|
||||
Description: param.Description,
|
||||
Status: param.Status,
|
||||
Creator: param.Creator,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Create(&product).Error
|
||||
}
|
||||
|
||||
func (p *ProductDao) Update(param *models.ProductUpdateParam) error {
|
||||
product := models.Product{
|
||||
Id: param.Id,
|
||||
Name: param.Name,
|
||||
Type: param.Type,
|
||||
Unit: param.Unit,
|
||||
Code: param.Code,
|
||||
Price: param.Price,
|
||||
Description: param.Description,
|
||||
Status: param.Status,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
db := global.Db.Model(&product).Select("*").Omit("id", "creator", "created")
|
||||
return db.Updates(&product).Error
|
||||
}
|
||||
|
||||
func (p *ProductDao) Delete(param *models.ProductDeleteParam) error {
|
||||
return global.Db.Delete(&models.Product{}, param.Ids).Error
|
||||
}
|
||||
|
||||
func (p *ProductDao) IsExists(name string, uid int64) bool {
|
||||
var product models.Product
|
||||
db := global.Db.Table(PRODUCT).Where("name = ? and creator = ?", name, uid).First(&product)
|
||||
return db.RowsAffected != NumberNull
|
||||
}
|
||||
|
||||
func (p *ProductDao) GetList(param *models.ProductQueryParam) ([]*models.ProductList, int64, error) {
|
||||
product := models.Product{
|
||||
Name: param.Name,
|
||||
Status: param.Status,
|
||||
Creator: param.Creator,
|
||||
}
|
||||
productList := make([]*models.ProductList, 0)
|
||||
rows, err := restPage(param.Page, PRODUCT, product, &productList, &[]*models.ProductList{})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return productList, rows, err
|
||||
}
|
||||
|
||||
func (p *ProductDao) GetListByIds(ids []int64) ([]*models.Products, error) {
|
||||
products := make([]*models.Products, 0)
|
||||
if err := global.Db.Table(PRODUCT).Find(&products, ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (p *ProductDao) GetInfo(param *models.ProductQueryParam) (*models.ProductInfo, error) {
|
||||
product := models.Product{
|
||||
Id: param.Id,
|
||||
}
|
||||
productInfo := models.ProductInfo{}
|
||||
err := global.Db.Table(PRODUCT).Where(&product).First(&productInfo).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &productInfo, err
|
||||
}
|
||||
|
||||
func (p *ProductDao) GetListByUid(uid int64) ([]*models.Product, error) {
|
||||
products := make([]*models.Product, 0)
|
||||
err := global.Db.Where("creator = ?", uid).Find(&products).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SubscribeDao struct {
|
||||
}
|
||||
|
||||
func NewSubscribeDao() *SubscribeDao {
|
||||
return &SubscribeDao{}
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) Create(param *models.SubscribeCreateParam) error {
|
||||
subscribe := models.Subscribe{
|
||||
Uid: param.Uid,
|
||||
Version: param.Version,
|
||||
Expired: param.Expired,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Table(SUBSCRIBE).Create(&subscribe).Error
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) Update(param *models.SubscribeUpdateParam) error {
|
||||
subscribe := models.Subscribe{
|
||||
Version: param.Version,
|
||||
Expired: param.Expired,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Model(&models.Subscribe{}).Where("uid = ?", param.Uid).Updates(&subscribe).Error
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) UpdateVersion(uid int64, v int) error {
|
||||
return global.Db.Model(&models.Subscribe{}).Where("uid = ?", uid).Update("version", v).Error
|
||||
}
|
||||
|
||||
func (u *SubscribeDao) IsExists(uid int64) bool {
|
||||
var subscribe models.Subscribe
|
||||
db := global.Db.Table(SUBSCRIBE).Where("uid = ?", uid).First(&subscribe)
|
||||
return db.RowsAffected != NumberNull
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) GetInfo(uid int64) (*models.SubscribeInfo, error) {
|
||||
var si models.SubscribeInfo
|
||||
if err := global.Db.Table(SUBSCRIBE).First(&si, "uid = ?", uid).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &si, nil
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) SetOrder(tradeNo string, param *models.SubscribePayOrder) error {
|
||||
order, _ := json.Marshal(¶m)
|
||||
return global.Rdb.Set(ctx, tradeNo, string(order), time.Minute*30).Err()
|
||||
}
|
||||
|
||||
func (s *SubscribeDao) GetOrder(orderNo string) (string, error) {
|
||||
orderJson, err := global.Rdb.Get(ctx, orderNo).Result()
|
||||
if err != nil {
|
||||
return StringNull, err
|
||||
}
|
||||
return orderJson, nil
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserDao struct {
|
||||
}
|
||||
|
||||
func NewUserDao() *UserDao {
|
||||
return &UserDao{}
|
||||
}
|
||||
|
||||
func (u *UserDao) Create(param *models.UserCreateParam) error {
|
||||
user := models.User{
|
||||
Email: param.Email,
|
||||
Password: param.Password,
|
||||
Status: 1,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Create(&user).Error
|
||||
}
|
||||
|
||||
func (u *UserDao) UpdatePass(email, password string) error {
|
||||
userPass := models.User{
|
||||
Password: password,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
return global.Db.Model(&models.User{}).Where("email = ?", email).Updates(&userPass).Error
|
||||
}
|
||||
|
||||
func (u *UserDao) IsExists(email string) bool {
|
||||
rows := global.Db.Where("email = ?", email).First(&models.User{}).RowsAffected
|
||||
return rows != NumberNull
|
||||
}
|
||||
|
||||
func (u *UserDao) GetUid(email string) (int64, error) {
|
||||
user, err := u.GetUser(email)
|
||||
if err != nil {
|
||||
return NumberNull, err
|
||||
}
|
||||
return user.Id, nil
|
||||
}
|
||||
|
||||
func (u *UserDao) GetUser(email string) (*models.User, error) {
|
||||
var user models.User
|
||||
err := global.Db.Table(USER).Where("email = ?", email).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (u *UserDao) DeleteData(param models.UserDeleteParam) error {
|
||||
return global.Db.Transaction(func(tx *gorm.DB) error {
|
||||
mw := map[interface{}]string{
|
||||
&models.Product{}: "creator = ?",
|
||||
&models.Customer{}: "creator = ?",
|
||||
&models.Contract{}: "creator = ?",
|
||||
&models.MailConfig{}: "creator = ?",
|
||||
&models.Subscribe{}: "uid = ?",
|
||||
&models.User{}: "id = ?",
|
||||
}
|
||||
for k, v := range mw {
|
||||
if err := tx.Where(v, param.Id).Delete(k).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (u *UserDao) GetInfo(uid int64) (*models.UserPersonInfo, error) {
|
||||
var user models.UserPersonInfo
|
||||
err := global.Db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Table(USER).Where("id = ?", uid).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var subscribe models.Subscribe
|
||||
if err := tx.Table(SUBSCRIBE).Select("version").Where("uid = ?", uid).First(&subscribe).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
user.Version = subscribe.Version
|
||||
return nil
|
||||
})
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (u *UserDao) SetCode(code int, email string) error {
|
||||
key := fmt.Sprintf("user:%s:code", email)
|
||||
return global.Rdb.SetEx(ctx, key, strconv.Itoa(code), 10*time.Minute).Err()
|
||||
}
|
||||
|
||||
func (u *UserDao) GetCode(email string) string {
|
||||
key := fmt.Sprintf("user:%s:code", email)
|
||||
return global.Rdb.Get(ctx, key).Val()
|
||||
}
|
||||
Reference in New Issue
Block a user