package cms

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"git.rosy.net.cn/baseapi/platformapi/alipayapi"
	"git.rosy.net.cn/baseapi/platformapi/qywxapi"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/alipay"
	"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
	"git.rosy.net.cn/jx-callback/business/partner"
	beego "github.com/astaxie/beego/server/web"

	"git.rosy.net.cn/jx-callback/business/jxstore/permission"

	"git.rosy.net.cn/jx-callback/business/partner/purchase/jd"

	"git.rosy.net.cn/jx-callback/globals/api/apimanager"

	"git.rosy.net.cn/baseapi"
	"git.rosy.net.cn/baseapi/platformapi/jdapi"
	"git.rosy.net.cn/jx-callback/business/jxutils/excel"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"

	"git.rosy.net.cn/baseapi/utils/errlist"

	"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/auth2"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
	"git.rosy.net.cn/jx-callback/business/authz"
	"git.rosy.net.cn/jx-callback/business/authz/autils"
	"git.rosy.net.cn/jx-callback/business/jxutils"
	"git.rosy.net.cn/jx-callback/business/jxutils/jsonerr"
	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/globals"
	"git.rosy.net.cn/jx-callback/globals/api"
	"git.rosy.net.cn/jx-callback/globals/api2"
)

var (
	ErrUserIDAndNameMustGiven = errors.New("用户ID2和用户名必须不为空")
)

var (
	userProvider     = &UserProvider{}
	authTypeFieldMap = map[string]string{
		auth2.UserIDID:     "user_id",
		auth2.UserIDID2:    "user_id2",
		auth2.UserIDMobile: "mobile",
		auth2.UserIDEmail:  "email",
	}
	jdUsersStruct   GetJdUsersStruct
	titleListJdUser = []string{
		"用户名",
		"关联门店",
		"所属角色",
		"状态",
	}
)

type GetJdUsersStruct struct {
	locker  sync.RWMutex
	userMap []JdUserStruct
}

type JdUserStruct struct {
	UserName string `json:"用户名"`
	StoreIDs string `json:"关联门店"`
	RoleName string `json:"所属角色"`
	Status   string `json:"状态"`
}

type UserProvider struct {
}

func (*UserProvider) GetUser(authID, authIDType string) (user auth2.IUser) {
	fieldName := authTypeFieldMap[authIDType]
	if fieldName != "" {
		user2, err := dao.GetUserByID(dao.GetDB(), fieldName, authID)
		if err == nil {
			user = user2 // 这样写的原因是golang nil的比较问题
		}
	}
	return user
}

func (*UserProvider) UpdateUserMobile(userID string, mobile string) (err error) {
	_, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{
		"Mobile": mobile,
	}, userID, map[string]interface{}{
		"UserID": userID,
	})
	return err
}

func (*UserProvider) UpdateUserEmail(userID string, email string) (err error) {
	_, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{
		"Email": email,
	}, userID, map[string]interface{}{
		"UserID": userID,
	})
	return err
}

func (*UserProvider) UpdateUserType(userID string, userTypeMask int8, updateType int) (err error) {
	db := dao.GetDB()
	user := &model.User{
		UserID: userID,
	}
	if err = dao.GetEntity(db, user, "UserID"); err == nil {
		if updateType == auth2.UpdateUserTypeAdd {
			user.Type |= userTypeMask
		} else if updateType == auth2.UpdateUserTypeDelete {
			user.Type &= ^userTypeMask
		} else {
			user.Type = userTypeMask
		}
		dao.WrapUpdateULEntity(user, userID)
		_, err = dao.UpdateEntity(db, user, "Type", model.FieldUpdatedAt, model.FieldLastOperator)
	}
	return err
}

func (*UserProvider) UpdateLastLogin(userID string, lastLoginType, fromIP string) (err error) {
	_, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{
		"LastLoginAt":   utils.Time2Pointer(time.Now()),
		"LastLoginType": lastLoginType,
		"LastLoginIP":   fromIP,
	}, userID, map[string]interface{}{
		"UserID": userID,
	})
	return err
}

// func (*UserProvider) CreateUser(userID2, mobile, email, name string) (user auth2.IUser, err error) {
// 	realUser := &model.User{
// 		UserID2: userID2,
// 		Mobile:  mobile,
// 		Email:   email,
// 		Name:    name,
// 	}
// 	return realUser, CreateUser(realUser)
// }

func init() {
	auth2.Init(userProvider)
}

func RegisterUserWithMobile(ctx *jxcontext.Context, user *model.User, mobileVerifyCode string, inAuthInfo, manTokenInfo *auth2.AuthInfo) (outAuthInfo *auth2.AuthInfo, err error) {
	var mobileAuth *auth2.AuthInfo
	fakeMobile := false
	user.Type = model.UserTypeConsumer | model.UserTypeStoreBoss // 先不区分商户与消息者
	//user.Type = model.UserTypeConsumer // 先不区分商户与消息者
	createName := ctx.GetRealRemoteIP()
	authType := auth2.AuthTypeMobile
	if manTokenInfo != nil && mobileVerifyCode == "" {
		manUser, err2 := dao.GetUserByID(dao.GetDB(), "user_id", manTokenInfo.GetID())
		if err = err2; err != nil {
			return nil, err
		}
		if manUser.Type&(model.UserTypeOperator|model.UserTypeBoss) == 0 {
			return nil, fmt.Errorf("管理员才能添加商户")
		}
		if utils.Pointer2String(user.Mobile) == "" {
			return nil, fmt.Errorf("管理员添加必须指定用户手机号")
		}
		mobileVerifyCode = auth2.InternalAuthSecret
		fakeMobile = true
		user.Type |= model.UserTypeStoreBoss
		createName = manTokenInfo.GetName()
	}

	if mobileVerifyCode != "" {
		if fakeMobile {
			mobileAuth, err = auth2.LoginInternal(ctx.Context, auth2.AuthTypeMobile, user.GetMobile(), auth2.UserIDMobile, mobileVerifyCode)
		} else {
			mobileAuth, err = auth2.Login(ctx.Context, auth2.AuthTypeMobile, user.GetMobile(), auth2.UserIDMobile, mobileVerifyCode)
		}
		if err != nil {
			return nil, err
		}
		if mobileAuth != nil && !mobileAuth.IsUserEmpty() {
			return mobileAuth, jsonerr.New(mobileAuth, model.ErrCodeJsonUserAlreadyExist)

			// auth2.RemoveUserInfo(mobileAuth.Token)
			// if newAuthInfo, err := auth2.BindUser(mobileAuth, user); err == nil {
			// 	return nil, jsonerr.New(newAuthInfo, model.ErrCodeJsonUserAlreadyExist)
			// } else {
			// 	return nil, err
			// }
		}
	} else if inAuthInfo != nil {
		// user.Mobile = nil
	} else {
		return nil, fmt.Errorf("短信验证码与其它认证方式至少要指定一种")
	}

	if inAuthInfo != nil {
		if inAuthInfo.AuthBindInfo.Type == dingding.AuthTypeStaff {
			user.Type |= model.UserTypeOperator
			user.Type |= model.UserTypeRole
		} else if user.Mobile != nil {
			//user.Type |= model.UserTypeStoreBoss
		}
		createName += "," + inAuthInfo.GetAuthID()
		authType = inAuthInfo.GetAuthType()
		if user.Avatar == "" {
			user.Avatar = inAuthInfo.GetAvatar()
		}
	}
	if err = CreateUser(user, utils.LimitUTF8StringLen(createName, 32)); err == nil {
		userProvider.UpdateLastLogin(user.GetID(), authType, ctx.GetRealRemoteIP())
		TryAddStoreBossRole4User(ctx, user)
		if mobileAuth != nil {
			if outAuthInfo, err = auth2.BindUser(mobileAuth, user); err == nil && inAuthInfo != nil {
				err = auth2.AddAuthBind(&outAuthInfo.UserBasic, inAuthInfo)
			}
		} else {
			outAuthInfo, err = auth2.BindUser(inAuthInfo, user)
		}
	} else if dao.IsDuplicateError(err) {
		if user.LastLoginType == alipay.AuthType {
			if user2, _ := dao.GetUserByID(dao.GetDB(), "mobile", user.GetMobile()); user2 != nil {
				user.UserID = user2.GetID()
				outAuthInfo, err = auth2.BindUser(inAuthInfo, user)
				err = nil
			} else {
				err = auth2.ErrUserID2AlreadyExist
			}
		} else {
			err = auth2.ErrUserID2AlreadyExist
		}
	}
	return outAuthInfo, err
}

func TryAddStoreBossRole4User(ctx *jxcontext.Context, user *model.User) (err error) {
	if user.Type&model.UserTypeStoreBoss != 0 {
		userMobile := user.GetMobile()
		if userMobile != "" {
			if storeList, err := dao.GetStoreList(dao.GetDB(), nil, nil, nil, nil, []string{userMobile}, ""); err == nil && len(storeList) > 0 {
				roleList := make([]*authz.RoleInfo, len(storeList))
				for k, v := range storeList {
					roleList[k] = autils.NewStoreBossRole(v.ID)
				}
				err = AddRoles4User(ctx, user.GetID(), roleList)
			}
		}
	}
	return err
}

func TryAddStoreBossRole4StoreByMobile(ctx *jxcontext.Context, storeID int, mobileList []string) (err error) {
	if storeID > 0 {
		var userIDs []string
		for _, v := range mobileList {
			if v != "" {
				if userList, _, err := dao.GetUsers(dao.GetDB(), model.UserTypeStoreBoss, "", nil, nil, []string{v}, 0, -1); err == nil && len(userList) > 0 {
					userIDs = append(userIDs, userList[0].GetID())
				} else {
					if manTokenInfo, err := auth2.GetTokenInfo(ctx.GetToken()); err == nil {
						RegisterUserWithMobile(ctx, &model.User{
							Name:    v,
							Mobile:  &v,
							UserID2: v,
						}, "", nil, manTokenInfo)
					}
				}
			}
		}
		if len(userIDs) > 0 {
			role := autils.NewStoreBossRole(storeID)
			err = AddUsers4Role(ctx, role, userIDs)
		}
	}
	return err
}

func HandleOrder4Consignee(order *model.GoodsOrder) (err error) {
	var user *model.User
	mobileNumber := jxutils.GetRealMobile4Order(order)
	if mobileNumber == "" && order.VendorUserID == "" {
		return fmt.Errorf("订单:%s手机号与平台用户标识都是空", order.VendorOrderID)
	}
	authType := dao.GetAuthType4Vendor(order.VendorID)
	if authType == "" {
		msg := fmt.Sprintf("平台ID:%d当前不被支持，请联系开发", order.VendorID)
		return fmt.Errorf(msg)
	}

	oeratorName := order.VendorOrderID
	db := dao.GetDB()
	if mobileNumber != "" {
		userList, _, err2 := dao.GetUsers(db, 0, "", nil, nil, []string{mobileNumber}, 0, 0)
		if err = err2; err != nil {
			return err
		}
		if len(userList) > 0 {
			user = userList[0]
		}
	}

	vendorUserID := order.VendorUserID
	if vendorUserID != "" {
		authInfo, err2 := dao.GetAuthBind(db, model.AuthBindTypeID, authType, vendorUserID)
		if err = err2; err != nil && !dao.IsNoRowsError(err) {
			return err
		}
		if err == nil {
			vendorUserID = ""
			if user == nil {
				if user, err = dao.GetUserByID(db, "user_id", authInfo.UserID); err != nil {
					return err
				}
			}
		} else {
			err = nil
		}
	}

	const Remark = "fromOrder"
	if user == nil {
		user = &model.User{
			UserID2: mobileNumber,
			Name:    order.ConsigneeName,
			Mobile:  &mobileNumber,
			Type:    model.UserTypeConsumer,
			Address: order.ConsigneeAddress,
			Remark:  Remark,
		}
		setUserAddress(db, user, order)

		if user.GetID2() == "" {
			user.UserID2 = order.VendorUserID
		}
		if user.GetID2() == "" {
			user.UserID2 = order.VendorOrderID
		}

		user.Type = model.UserTypeConsumer
		err = CreateUser(user, oeratorName)
	} else {
		if user.GetMobile() == "" && mobileNumber != "" {
			user.Mobile = &mobileNumber
			dao.UpdateEntity(db, user, "Mobile")
		}
	}
	if err == nil {
		order.UserID = user.GetID()
		if vendorUserID != "" {
			authBind := &model.AuthBind{
				UserID:   user.GetID(),
				BindType: model.AuthBindTypeID,
				AuthID:   vendorUserID,
				Type:     authType,
				Remark:   Remark,
			}
			dao.WrapAddIDCULDEntity(authBind, oeratorName)
			authBind.Status = model.AuthBindStatusNormal
			err = dao.CreateEntity(nil, authBind)
		}
	}
	return err
}

func setUserAddress(db *dao.DaoDB, user *model.User, order *model.GoodsOrder) {
	user.Address = order.ConsigneeAddress
	store := &model.Store{}
	store.ID = jxutils.GetSaleStoreIDFromOrder(order)
	if err := dao.GetEntity(db, store); err == nil {
		user.CityCode = store.CityCode
		user.DistrictCode = store.DistrictCode
	} else if dao.IsNoRowsError(err) {
		if order.ConsigneeLng != 0 && order.ConsigneeLat != 0 {
			if user.DistrictCode = api.TencentMapAPI.GetCoordinateDistrictCode(jxutils.IntCoordinate2Standard(order.ConsigneeLng), jxutils.IntCoordinate2Standard(order.ConsigneeLat)); user.DistrictCode > 0 {
				if placeInfo, err := dao.GetPlaceByCode(db, user.DistrictCode); err == nil {
					user.CityCode = placeInfo.ParentCode
				}
			}
		}
	}
}

func GetUserBindAuthInfo(ctx *jxcontext.Context) (authList []*model.AuthBind, err error) {
	authInfo, err := ctx.GetV2AuthInfo()
	if err == nil {
		return auth2.GetUserBindAuthInfo(authInfo.GetID())
	}
	return nil, err
}

func CreateUser(user *model.User, creatorName string) (err error) {

	if user == nil || user.UserID2 == "" || user.Name == "" {
		return ErrUserIDAndNameMustGiven
	}
	if user.GetMobile() == "" {
		user.Mobile = nil
	}
	if user.GetEmail() == "" {
		user.Email = nil
	}
	//支付宝注册电话需要解密
	if user.GetMobile() != "" && user.GetName() != "" && user.GetID2() != "" {
		if user.LastLoginType == alipay.AuthType {
			mobile := &alipayapi.KeyMobile{}
			data, _ := base64.StdEncoding.DecodeString(user.GetMobile())
			key, _ := base64.StdEncoding.DecodeString(alipay.AuthKey)
			iv := []byte{}
			iv = append(iv, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
			if result, err := utils.AESCBCDecpryt(data, key, iv); err == nil && result != nil {
				json.Unmarshal(result, &mobile)
			}
			if mobile.Mobile == "" {
				return fmt.Errorf("支付宝注册解密失败！")
			}
			user.Mobile = &mobile.Mobile
			user.UserID2 = mobile.Mobile
			user.Name = mobile.Mobile
		}
	}
	dao.WrapAddIDCULDEntity(user, creatorName)
	user.UserID = utils.GetUUID()
	user.Status = model.UserStatusNormal
	user.DividePercentage = 1
	return dao.CreateEntity(nil, user)
}

func DisableUser(ctx *jxcontext.Context, userID string) (err error) {
	userName := ctx.GetUserName()
	if _, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{
		"Status": model.UserStatusDisabled,
	}, userName, map[string]interface{}{
		"UserID": userID,
	}); err == nil {
		auth2.DisableUser(userID, userName)
	}
	return err
}

func OnDingDingMsg(msg map[string]interface{}) (callbackResponse *dingdingapi.CallbackResponse) {
	eventType := utils.Interface2String(msg[dingdingapi.KeyEventType])
	if eventType == dingdingapi.CBTagUserLeaveOrg {
		var (
			authBind *model.AuthBind
			err      error
		)
		db := dao.GetDB()
		for _, userID := range msg[dingdingapi.KeyUserID].([]interface{}) {
			userIDStr := utils.Interface2String(userID)
			if authBind, err = dao.GetAuthBind(db, model.AuthBindTypeAuth, dingding.AuthTypeStaff, userIDStr); err == nil { // 直接找到了
				if err = DeleteUserInfo(jxcontext.AdminCtx, authBind.UserID); err != nil {
					globals.SugarLogger.Errorf("OnDingDingMsg failed with error:%v", err)
				}
			}
		}
	} else if eventType == dingdingapi.CBTagCheckURL {
		return api.DingDingAPI.PackCallbackResult("success")
	}
	return api.DingDingAPI.Err2CallbackResponse(nil)
}

func GetUsers(ctx *jxcontext.Context, userType int, keyword string, userIDs []string, userID2, mobile string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
	userList, totalCount, err := dao.GetUsers(dao.GetDB(), userType, keyword, userIDs, jxutils.BatchString2Slice(userID2), jxutils.BatchString2Slice(mobile), offset, pageSize)
	if err == nil {
		pagedInfo = &model.PagedInfo{
			TotalCount: totalCount,
			Data:       userList,
		}
	}
	return pagedInfo, err
}

func GetStoreList4User(ctx *jxcontext.Context, mobileNum, userID string) (storeList []*dao.StoreWithCityName, err error) {
	roleList, err := api2.RoleMan.GetUserRoleList(userID)
	if err != nil {
		return nil, err
	}
	var (
		storeIDs          []int
		shortRoleNameList []string
	)
	for _, v := range roleList {
		if v.StoreID == 0 {
			shortRoleNameList = append(shortRoleNameList, v.Name)
		} else {
			storeIDs = append(storeIDs, v.StoreID)
		}
	}

	storeList, err = dao.GetStoreListByMobileOrStoreIDs(dao.GetDB(), mobileNum, shortRoleNameList, storeIDs)

	storeMap, _ := dao.GetStoresMapList(dao.GetDB(), nil, storeIDs, nil, -9, -1, "", "", "")
	mapList := make(map[int][]dao.StoreMapList, len(storeMap))
	if len(storeMap) != 0 {
		for i := 0; i < len(storeMap); i++ {
			mapList[storeMap[i].StoreID] = append(mapList[storeMap[i].StoreID], dao.StoreMapList{
				JxStoreId:     storeMap[i].StoreID,
				VendorStoreId: storeMap[i].VendorStoreID,
				VendorId:      storeMap[i].VendorID,
				VendorOrgCode: storeMap[i].VendorOrgCode,
			})
		}
	}

	for _, v := range storeList {
		v.VendorStore = mapList[v.ID]
	}

	return storeList, err
}

func GetMyStoreListNew(ctx *jxcontext.Context, version string) (storesInfo interface{}, errCode string, err error) {
	if !auth2.IsV2Token(ctx.GetToken()) {
		return nil, model.ErrCodeTokenIsInvalid, model.ErrTokenIsInvalid
	}
	if ctx.GetLoginType() == weixin.AuthTypeWxApp || ctx.GetLoginType() == weixin.AuthTypeWxAppCaishi {
		if configs, _ := dao.QueryConfigs(dao.GetDB(), "checkversion", model.ConfigTypeSys, ""); len(configs) > 0 {
			if version == "" || configs[0].Value != version {
				return nil, "", fmt.Errorf("当前APP版本过旧，数据显示有错误，请到'京西菜市'公众号下载最新版本APP！")
			}
		}
	}
	mobile, userID := ctx.GetMobileAndUserID()
	if mobile == "" || userID == "" || userID == "null" || userID == "NULL" {
		return nil, "", fmt.Errorf("不能得到用户手机号,%s,%s", userID, mobile)
	}
	var storeList []*dao.StoreWithCityName
	if storeList, err = GetStoreList4User(ctx, mobile, userID); err == nil && len(storeList) > 0 {
		// todo，应该用通用方法
		mapDataList := make([]map[string]interface{}, len(storeList))
		for k, v := range storeList {
			mapDataList[k] = map[string]interface{}{
				"id":              v.ID,
				"address":         v.Address,
				"cityName":        v.CityName,
				"name":            v.Name,
				"tel1":            v.Tel1,
				"tel2":            v.Tel2,
				"payeeName":       v.PayeeName,
				"status":          v.Status,
				"vendorStoreList": v.VendorStore,
			}
		}
		storesInfo = mapDataList
	}
	return storesInfo, "", err
}

func SolveNullMobileUser(ctx *jxcontext.Context) (err error) {

	return err
}

func GetStoreRoleList(ctx *jxcontext.Context) (roleList []*authz.RoleInfo, err error) {
	return authz.StoreRoleList, nil
}

func GetUserRoleList(ctx *jxcontext.Context, userID string) (roleList []*authz.RoleInfo, err error) {
	return api2.RoleMan.GetUserRoleList(userID)
}

func GetRoleUserList(ctx *jxcontext.Context, r *authz.RoleInfo) (userIDList []string, err error) {
	return api2.RoleMan.GetRoleUserList(r)
}

func GetRolesUserList(ctx *jxcontext.Context, rList []*authz.RoleInfo) (userIDMap map[string][]string, err error) {
	userIDMap = make(map[string][]string)
	for _, r := range rList {
		userIDList, err2 := api2.RoleMan.GetRoleUserList(r)
		if err = err2; err == nil {
			userIDMap[r.GetFullName()] = userIDList
		} else {
			break
		}
	}
	return userIDMap, err
}

func checkUserType(userID string, userType int8) (err error) {
	userList, _, err := dao.GetUsers(dao.GetDB(), 0, "", []string{userID}, nil, nil, 0, 0)
	if err != nil {
		return err
	}
	if len(userList) == 0 {
		return fmt.Errorf("找不到用户:%s", userID)
	}
	if userList[0].Type&userType == 0 {
		return fmt.Errorf("用户:%s不能用于当前操作", userID)
	}
	return nil
}

func AddRoles4User(ctx *jxcontext.Context, userID string, rList []*authz.RoleInfo) (err error) {
	errList := errlist.New()
	if err = checkUserType(userID, model.UserTypeNonConsumer); err != nil {
		return err
	}
	for _, v := range rList {
		if err = autils.ValidateRole(v.Name, v.StoreID); err == nil {
			if err = api2.RoleMan.AddRole4User(userID, v); err != nil {
				errList.AddErr(err)
			} else if v.StoreID > 0 {
				HandleUserWXRemark(dao.GetDB(), userID, true)
			}
		} else {
			errList.AddErr(err)
		}
	}
	return errList.GetErrListAsOne()
}

func DeleteRoles4User(ctx *jxcontext.Context, userID string, rList []*authz.RoleInfo) (err error) {
	errList := errlist.New()
	for _, v := range rList {
		if err = api2.RoleMan.DeleteRole4User(userID, v); err != nil {
			errList.AddErr(err)
		} else if v.StoreID > 0 {
			HandleUserWXRemark(dao.GetDB(), userID, true)
		}
	}
	return errList.GetErrListAsOne()
}

func AddUsers4Role(ctx *jxcontext.Context, r *authz.RoleInfo, userIDList []string) (err error) {
	if err = autils.ValidateRole(r.Name, r.StoreID); err != nil {
		return err
	}

	errList := errlist.New()
	for _, v := range userIDList {
		// if permission.IsRoledByUserID(v) {
		// 	if storeIDsMap, err := permission.GetUserStoresResultMap(v); err == nil {
		// 		if storeIDsMap[r.StoreID] == 0 {
		// 			return fmt.Errorf("此用户没有该门店[%v]的权限，无法添加到分组", r.StoreID)
		// 		}
		// 	}
		// }

		if err = checkUserType(v, model.UserTypeNonConsumer); err != nil {
			return err
		}
		if err = api2.RoleMan.AddRole4User(v, r); err != nil {
			errList.AddErr(err)
		} else if r.StoreID > 0 {
			HandleUserWXRemark(dao.GetDB(), v, true)
		}
	}
	return errList.GetErrListAsOne()
}

func DeleteUsers4Role(ctx *jxcontext.Context, r *authz.RoleInfo, userIDList []string) (err error) {
	errList := errlist.New()
	for _, v := range userIDList {
		if err = api2.RoleMan.DeleteRole4User(v, r); err != nil {
			errList.AddErr(err)
		} else if r.StoreID > 0 {
			HandleUserWXRemark(dao.GetDB(), v, true)
		}
	}
	return errList.GetErrListAsOne()
}

func getAddressInfoFromCoord(db *dao.DaoDB, lng, lat float64, districtCodeParam int) (formattedAddress string, districtCode, cityCode int, err error) {
	place, err := dao.GetPlacesByDistrictCode(districtCodeParam)
	if err != nil {
		return "", 0, 0, err
	}
	formattedAddress = place.ProvinceName + place.CityName + place.CountyName
	districtCode = place.CountyCode
	cityCode = place.CityCode
	//regeoInfo, err := api.TencentMapAPI.GeoCodeRegeoSingle(lng, lat, 0, false, nil, 0, 0)
	//if err == nil {
	//	formattedAddress = regeoInfo.Result.Address
	//	districtCode = int(utils.Str2Int64WithDefault(regeoInfo.Result.AdInfo.Adcode, 0))
	//	if districtCode == 0 {
	//		err = fmt.Errorf("坐标lng:%f,lat:%f找不到位置信息", lng, lat)
	//	} else if districtInfo, err2 := dao.GetPlaceByCode(db, districtCode); err2 == nil {
	//		cityCode = districtInfo.ParentCode
	//	}
	//}
	// 地址			// 行政区划code
	return formattedAddress, districtCode, cityCode, err
}

func AddUserDeliveryAddress(ctx *jxcontext.Context, address *model.UserDeliveryAddress) (outAddress *model.UserDeliveryAddress, err error) {
	if address.UserID == "" {
		return nil, fmt.Errorf("操作用户配送地址时必须指定UserID")
	}

	db := dao.GetDB()
	lng := address.Lng
	lat := address.Lat

	address.AutoAddress, address.DistrictCode, address.CityCode, err = getAddressInfoFromCoord(db, lng, lat, address.DistrictCode)
	address.AutoAddress = address.AutoAddress + address.Address
	if err == nil {
		txDB, _ := dao.Begin(db)
		defer func() {
			if r := recover(); r != nil {
				dao.Rollback(db, txDB)
				panic(r)
			}
		}()
		dao.WrapAddIDCULDEntity(address, ctx.GetUserName())
		if address.IsDefault == 1 {
			if err = dao.ClearUserDeliveryAddressDefault(db, address.UserID, 0); err != nil {
				dao.Rollback(db, txDB)
				return nil, err
			}
		}
		if err = dao.CreateEntity(db, address); err == nil {
			dao.Commit(db, txDB)
			outAddress = address
		} else {
			dao.Rollback(db, txDB)
		}
	}
	return outAddress, err
}

func AddMyDeliveryAddress(ctx *jxcontext.Context, address *model.UserDeliveryAddress) (outAddress *model.UserDeliveryAddress, err error) {
	_, address.UserID = ctx.GetMobileAndUserID()
	outAddress, err = AddUserDeliveryAddress(ctx, address)
	return outAddress, err
}

func DeleteUserDeliveryAddress(ctx *jxcontext.Context, userID string, addressID int) (err error) {
	num, err := dao.DeleteEntityLogically(dao.GetDB(), &model.UserDeliveryAddress{}, nil, ctx.GetUserName(), map[string]interface{}{
		model.FieldID: addressID,
		"UserID":      userID,
	})
	if err == nil {
		if num == 0 {
			err = fmt.Errorf("地址ID:%d不存在", addressID)
		}
	}
	return err
}

func DeleteMyDeliveryAddress(ctx *jxcontext.Context, addressID int) (err error) {
	_, userID := ctx.GetMobileAndUserID()
	return DeleteUserDeliveryAddress(ctx, userID, addressID)
}

func UpdateUserDeliveryAddress(ctx *jxcontext.Context, userID string, addressID int, payload map[string]interface{}) (err error) {
	if userID == "" {
		return fmt.Errorf("操作用户配送地址时必须指定UserID")
	}
	address := &model.UserDeliveryAddress{
		UserID: userID,
	}
	address.ID = addressID
	db := dao.GetDB()
	if err = dao.GetEntity(db, address, model.FieldID, "UserID"); err == nil {
		var outAddress *model.UserDeliveryAddress
		valid := dao.StrictMakeMapByStructObject2(payload, address, &outAddress, ctx.GetUserName())
		delete(valid, "autoAddress")
		//delete(valid, "districtCode")
		delete(valid, "cityCode")
		if len(valid) > 0 {
			if valid["lng"] != nil || valid["lat"] != nil {
				valid["autoAddress"], valid["districtCode"], valid["cityCode"], err = getAddressInfoFromCoord(db, outAddress.Lng, outAddress.Lat, valid["districtCode"].(int))
				if err != nil {
					return err
				}
			}
			txDB, _ := dao.Begin(db)
			defer func() {
				if r := recover(); r != nil {
					dao.Rollback(db, txDB)
					panic(r)
				}
			}()
			if utils.ForceInterface2Int64(valid["isDefault"]) == 1 {
				if err = dao.ClearUserDeliveryAddressDefault(db, userID, 0); err != nil {
					dao.Rollback(db, txDB)
					return err
				}
			}
			if _, err = dao.UpdateEntityLogically(db, address, valid, ctx.GetUserName(), nil); err == nil {
				dao.Commit(db, txDB)
			} else {
				dao.Rollback(db, txDB)
			}
		}
	}
	return err
}

func UpdateMyDeliveryAddress(ctx *jxcontext.Context, addressID int, payload map[string]interface{}) (err error) {
	_, userID := ctx.GetMobileAndUserID()
	return UpdateUserDeliveryAddress(ctx, userID, addressID, payload)
}

func QueryUserDeliveryAddress(ctx *jxcontext.Context, userIDs []string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
	addressList, totalCount, err := dao.QueryUserDeliveryAddress(dao.GetDB(), 0, userIDs, offset, pageSize)
	if err == nil {
		pagedInfo = &model.PagedInfo{
			TotalCount: totalCount,
			Data:       addressList,
		}
	}
	return pagedInfo, err
}

func QueryMyDeliveryAddress(ctx *jxcontext.Context) (addressList []*dao.UserDeliveryAddressEx, err error) {
	_, userID := ctx.GetMobileAndUserID()
	addressList, _, err = dao.QueryUserDeliveryAddress(dao.GetDB(), 0, []string{userID}, 0, model.UnlimitedPageSize)
	return addressList, err
}

func SaveUserCart(ctx *jxcontext.Context, userID string, storeID int, cartItems []*model.UserCartItem) (err error) {
	if userID == "" || storeID == 0 {
		return fmt.Errorf("用户与门店必须要指定")
	}
	for _, v := range cartItems {
		v.UserID = userID
		v.StoreID = storeID
		dao.WrapAddIDCULEntity(v, userID)
	}
	db := dao.GetDB()
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	_, err = dao.ExecuteSQL(db, `
	DELETE t1
	FROM user_cart_item t1
	WHERE t1.user_id = ? AND t1.store_id = ?
	`, userID, storeID)
	if err != nil {
		return err
	}
	if len(cartItems) > 0 {
		err = dao.CreateMultiEntities(db, cartItems)
	}
	if err == nil {
		dao.Commit(db, txDB)
	}
	return err
}

func LoadUserCart(ctx *jxcontext.Context, userID string, storeIDs []int) (cartItems []*model.UserCartItem, err error) {
	if userID == "" || len(storeIDs) == 0 {
		return nil, fmt.Errorf("用户与门店必须要指定")
	}
	sql := `
	SELECT t1.*
	FROM user_cart_item t1
	WHERE t1.user_id = ? AND t1.store_id IN (` + dao.GenQuestionMarks(len(storeIDs)) + `)
	ORDER BY t1.sku_id
	`
	err = dao.GetRows(dao.GetDB(), &cartItems, sql, userID, storeIDs)
	return cartItems, err
}

func GetSelfInfo(ctx *jxcontext.Context) (getSelfInfoResult *dao.GetSelfInfoResult, err error) {
	tokenInfo, err := auth2.GetTokenInfo(ctx.GetToken())
	if err == nil {
		if getSelfInfoResult, err = dao.GetUserByIDWithMembers(dao.GetDB(), "user_id", tokenInfo.GetID()); err == nil {
			if userMembers, err3 := dao.GetUserMember(dao.GetDB(), getSelfInfoResult.UserID, "", "", model.VendorIDJX, 0, model.YES); err3 == nil {
				getSelfInfoResult.UserMembers = userMembers
			} else {
				err = err3
			}
		}
	}
	return getSelfInfoResult, err
}

func HandleUserWXRemark(db *dao.DaoDB, mobile string, mobileIsUerID bool) (err error) {
	if db == nil {
		db = dao.GetDB()
	}
	openIDs := []string{}
	storeID := 0
	remark := ""

	// if !globals.DisableWXAuth1 {
	// 	wxinfo, err := dao.GetUserStoreInfo(db, "tel", mobile)
	// 	if err == nil {
	// 		openIDs = []string{wxinfo.OpenID}
	// 		storeID = wxinfo.JxStoreID
	// 	}
	// }
	if globals.EnableWXAuth2 {
		userID := ""
		if mobileIsUerID {
			userID = mobile
		} else {
			userList, _, err2 := dao.GetUsers(db, model.UserTypeStoreBoss, "", nil, nil, []string{mobile}, 0, -1)
			if err = err2; len(userList) > 0 {
				userID = userList[0].GetID()
			}
		}
		if userID != "" {
			authBindList, err2 := dao.GetUserBindAuthInfo(db, userID, model.AuthBindTypeAuth, []string{weixin.AuthTypeMP}, "", "", nil)
			if err = err2; err == nil {
				for _, v := range authBindList {
					openIDs = append(openIDs, v.AuthID)
				}
			}
			roleList, err2 := api2.RoleMan.GetUserRoleList(userID)
			if err = err2; err == nil && len(roleList) > 0 {
				storeID = roleList[0].StoreID
			}
		}
	}

	if len(openIDs) > 0 {
		if storeID > 0 {
			store := &model.Store{}
			store.ID = storeID
			if err = dao.GetEntity(db, store); err == nil {
				city := &model.Place{
					Code: store.CityCode,
				}
				if err = dao.GetEntity(db, city, "Code"); err == nil {
					remark = city.Name + "-" + store.Name
				}
			}
		}
		if err == nil {
			if globals.EnableStoreWrite {
				for _, openID := range openIDs {
					err = api.WeixinAPI.CBUpdateRemark(openID, remark)
				}
			}
		}
	}
	return err
}

func GetJdUsers(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		jxVendorIDsMap = make(map[string]string)
		pageNoList     []int
		storeUserList  []interface{}
		disabledIdList []interface{}
	)
	db := dao.GetDB()
	jdUsersStruct.userMap = jdUsersStruct.userMap[0:0]
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			//获取京东商城所有用户
			apiList := apimanager.CurAPIManager.GetAppOrgCodeList(model.VendorIDJD)
			for _, v := range apiList {
				_, _, toatlPage, _ := jd.GetAPI(v).PrivilegeSearchUser(1)
				for i := 1; i <= toatlPage; i++ {
					pageNoList = append(pageNoList, i)
				}
				storeMapList, _ := dao.GetStoreMapsListWithoutDisabled(db, []int{model.VendorIDJD}, model.StoreStatusDisabled)
				for _, v := range storeMapList {
					jxVendorIDsMap[v.VendorStoreID] = utils.Int64ToStr(int64(v.StoreID))
				}
				taskFunc1 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
					pageNo := batchItemList[0].(int)
					storeUserLists, _, _, err := jd.GetAPI(v).PrivilegeSearchUser(pageNo)
					retVal = storeUserLists
					return retVal, err
				}
				taskParallel1 := tasksch.NewParallelTask("获取京东商城所有用户列表", tasksch.NewParallelConfig(), ctx, taskFunc1, pageNoList)
				tasksch.HandleTask(taskParallel1, task, true).Run()
				storeUserList, err = taskParallel1.GetResult(0)

				taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
					vv := batchItemList[0].(*jdapi.StoreUserInfo)
					vendorStoreIDs, err := jd.GetAPI(v).GetJdUserBindStoreIDs(vv.ID)
					var vendorStoreIDsMap = make(map[string]string, len(vendorStoreIDs))
					var vendorStoreIDsResult []string
					var roleNameStr string
					for _, v := range vendorStoreIDs {
						if jxVendorIDsMap[v] == "" {
							continue
						}
						vendorStoreIDsMap[v] = jxVendorIDsMap[v]
					}
					if vv.RoleNameStr != "" {
						roleNameStr = strings.ReplaceAll(vv.RoleNameStr, " ", "")
						if roleNameStr != jdapi.JdUserRoleJHYName && roleNameStr != jdapi.JdUserRolesName && roleNameStr != jdapi.JdUserNoRole {
							jd.GetAPI(v).UpdateJdUserRoles(int64(vv.ID), []string{jdapi.JdUserRoleJHYId})
						}
					}
					if len(vendorStoreIDsMap) == 0 {
						isManager, _ := jd.GetAPI(v).IsJdManagerUser(int64(vv.ID))
						if isManager {
							jdStruct := JdUserStruct{vv.LoginName, "商家管理员", vv.RoleNameStr, vv.LockStatus}
							jdUsersStruct.AppendData(jdStruct)
						} else {
							retVal = []int64{int64(vv.ID)}
						}
					} else {
						for _, m := range vendorStoreIDsMap {
							vendorStoreIDsResult = append(vendorStoreIDsResult, m)
						}
						sort.Strings(vendorStoreIDsResult[:])
						jdStruct := JdUserStruct{vv.LoginName, strings.Join(vendorStoreIDsResult, ","), vv.RoleNameStr, vv.LockStatus}
						jdUsersStruct.AppendData(jdStruct)
					}
					return retVal, err
				}
				taskParallel := tasksch.NewParallelTask("获取京东商城用户关联门店列表", tasksch.NewParallelConfig(), ctx, taskFunc, storeUserList)
				tasksch.HandleTask(taskParallel, task, true).Run()
				disabledIdList, err = taskParallel.GetResult(0)

				taskFunc2 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
					id := batchItemList[0].(int64)
					jd.GetAPI(v).PrivilegeUpdateJdUserStatus(id, jdapi.JdUserStatusDisable)
					return retVal, err
				}
				taskParallel2 := tasksch.NewParallelTask("禁用未关联活跃门店用户", tasksch.NewParallelConfig(), ctx, taskFunc2, disabledIdList)
				tasksch.HandleTask(taskParallel2, task, true).Run()
				_, err = taskParallel2.GetResult(0)
			}
		case 1:
			WriteToExcelJd(task, jdUsersStruct.userMap)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("获取京东商城用户关联门店列表-序列任务", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func (d *GetJdUsersStruct) AppendData(jd JdUserStruct) {
	d.locker.RLock()
	defer d.locker.RUnlock()
	d.userMap = append(d.userMap, jd)
}

func WriteToExcelJd(task *tasksch.SeqTask, jd []JdUserStruct) (err error) {
	var sheetList []*excel.Obj2ExcelSheetConfig
	var downloadURL, fileName string
	excelConf := &excel.Obj2ExcelSheetConfig{
		Title:       "京东用户列表",
		Data:        jd,
		CaptionList: titleListJdUser,
	}
	sheetList = append(sheetList, excelConf)
	if excelConf != nil {
		downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "京东用户列表")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: JdUserStruct is nil!")
	}
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s,failed error:%v", fileName, err)
	} else {
		noticeMsg := fmt.Sprintf("[详情点我]path=%s, \n", downloadURL)
		task.SetNoticeMsg(noticeMsg)
		baseapi.SugarLogger.Debugf("WriteToExcel:upload %s, success, downloadURL:%s", fileName, downloadURL)
	}
	return err
}

func UpdateUserWxNoAndPercent(user *model.User, isReceiver bool) (num int64, err error) {
	db := dao.GetDB()
	user2, err := dao.GetUserByID(db, "user_id", user.UserID)
	auth, err := dao.GetUserBindAuthInfo(db, user.UserID, model.AuthBindTypeAuth, []string{"weixinmini"}, "", "", []string{"wx4b5930c13f8b1170"})
	if len(auth) == 0 {
		return 0, fmt.Errorf("未找到此用户的微信验证方式！用户ID：[%v]\n", user.UserID)
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	user2.DividePercentage = user.DividePercentage
	num2, err := dao.UpdateEntity(db, user2, "DividePercentage")
	num += num2
	if err != nil {
		dao.Rollback(db, txDB)
	}
	if isReceiver {

	}
	dao.Commit(db, txDB)
	return num, err
}

func DeleteUserInfo(ctx *jxcontext.Context, userID string) (err error) {
	db := dao.GetDB()
	sql := `
		UPDATE user
		SET status = ?
		WHERE user_id = ?
	`
	sqlParams := []interface{}{
		model.NO,
		userID,
	}
	_, err = dao.ExecuteSQL(db, sql, sqlParams)

	/* 删除离职人员门店市场人员 */
	sql2 := `
		UPDATE store t1
		LEFT JOIN user t2 ON t2.mobile = t1.market_man_phone AND t2.status <> 0
		SET t1.market_man_phone = ''
		WHERE t2.id IS NULL
	`
	_, err = dao.ExecuteSQL(db, sql2)

	/* 删除离职人员门店运营人员 */
	sql3 := `
		UPDATE store t1
		LEFT JOIN user t2 ON t2.mobile = t1.operator_phone AND t2.status <> 0
		SET t1.operator_phone = ''
		WHERE t2.id IS NULL
	`
	_, err = dao.ExecuteSQL(db, sql3)

	/* 删除离职人员门店运营人员 */
	sql4 := `
		UPDATE store t1
		LEFT JOIN user t2 ON t2.mobile = t1.operator_phone2 AND t2.status <> 0
		SET t1.operator_phone2 = ''
		WHERE t2.id IS NULL
	`
	_, err = dao.ExecuteSQL(db, sql4)

	/*删除离职人员的角色信息*/
	sql5 := `
		DELETE t1
		FROM casbin_rule t1
		LEFT JOIN user t2 ON t2.user_id = t1.v0 AND t2.status <> 0
		WHERE t2.id IS NULL
	`
	_, err = dao.ExecuteSQL(db, sql5)

	//删除token
	tokens, err := api.Cacher.Keys("TOKEN.V2." + userID + "*")
	for _, v := range tokens {
		err = api.Cacher.Del(v)
	}
	return err
}

func GetMyJxStoreList(ctx *jxcontext.Context, mobile string) (storesInfo interface{}, err error) {
	db := dao.GetDB()
	user, err := dao.GetUserByID(db, "mobile", mobile)
	if err != nil {
		return nil, err
	}
	userID := user.UserID
	var storeList []*dao.StoreWithCityName
	if storeList, err = GetStoreList4User(ctx, mobile, userID); err == nil && len(storeList) > 0 {
		// todo，应该用通用方法
		mapDataList := make([]map[string]interface{}, len(storeList))
		for k, v := range storeList {
			mapDataList[k] = map[string]interface{}{
				"id":        v.ID,
				"address":   v.Address,
				"cityName":  v.CityName,
				"name":      v.Name,
				"tel1":      v.Tel1,
				"tel2":      v.Tel2,
				"payeeName": v.PayeeName,
				"status":    v.Status,
			}
		}
		storesInfo = mapDataList
	}
	return storesInfo, err
}

func CreateUserAgreement(ctx *jxcontext.Context, userAgrs []*model.UserAgreement) (err error) {
	var (
		db = dao.GetDB()
	)
	for _, v := range userAgrs {
		dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
	}
	err = dao.CreateMultiEntities(db, userAgrs)
	return err
}

func GetUserAgreement(ctx *jxcontext.Context, name, idNumber, bankNumber, mobile string) (userAgrs []*model.UserAgreement, err error) {
	var (
		db = dao.GetDB()
	)
	userAgrs, err = dao.GetUserAgreement(db, name, idNumber, bankNumber, mobile)
	return userAgrs, err
}

func CleanUserOrderSMSMark(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)
	if time.Now().Day() == 1 {
		sql := `
		UPDATE user_order_sms 
		   SET sms_mark = ?
	`
		sqlParams := []interface{}{model.NO}
		_, err = dao.ExecuteSQL(db, sql, sqlParams)
	}
	return err
}

func RefreshUserMemberStatus(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)
	userMembers, err := dao.GetUserMember(db, "", "", "", -1, model.MemberTypeDiscountCard, -1)
	for _, userMember := range userMembers {
		if time.Now().Sub(userMember.EndAt) > 0 {
			userMember.DeletedAt = time.Now()
			dao.UpdateEntity(db, userMember, "DeletedAt")
		}
	}
	return err
}

func UpdateUserLastInfo(ctx *jxcontext.Context, storeID, brandID int) (err error) {
	var (
		db = dao.GetDB()
	)
	if user, err := dao.GetUserByID(db, "user_id", ctx.GetUserID()); err == nil {
		//本次与上次不同时再更新
		if storeID != user.LastStoreID {
			user.LastStoreID = storeID
			_, err = dao.UpdateEntity(db, user, "LastStoreID")
		}

		// 不应该出现这种情况，0表示未找到，1表示找到，其他值为门店ID，表示锁定店铺。
		if user.LastBrandID > 1 && user.LastBrandID != storeID {
			brandID = 0
		}

		if brandID != user.LastBrandID {
			user.LastBrandID = brandID
			_, err = dao.UpdateEntity(db, user, "LastBrandID")
		}
	}
	return err
}

func SetUserCId(userId string, cid string) error {
	var db = dao.GetDB()
	userObj, err := dao.GetUserByID(db, "user_id", userId)
	if err != nil {
		return err
	}

	if userObj.CID == "" || userObj.CID != cid {
		userObj.CID = cid
		_, err := dao.UpdateEntity(db, userObj, "CID")
		return err
	}
	return nil
}

func GetUserStoreAuth(ctx *jxcontext.Context, storeID int) (outStoreID int, err error) {
	if permission.IsRoled(ctx) {
		configs, _ := dao.QueryConfigs(dao.GetDB(), "checkversion", model.ConfigTypeSys, "")
		storeMaps, _, err := GetMyStoreListNew(ctx, configs[0].Value)
		if err != nil {
			return 0, err
		}
		for _, v := range storeMaps.([]map[string]interface{}) {
			if v["id"].(int) != storeID {
				continue
			}
			return storeID, nil
		}
		//storeMap, _ := permission.GetUserStoresResultMap(ctx.GetUserID())
		//if storeMap[storeID] != 0 {
		//	return storeID, err
		//} else {
		//	return 0, err
		//}
	}
	return storeID, err
}

func SendQywxPeopleCount(ctx *jxcontext.Context) (err error) {
	var (
		db         = dao.GetDB()
		groupIDMap = make(map[string][]interface{})
	)
	if beego.BConfig.RunMode == model.ServerTypeFruits {
		groupIDMap[qywxapi.GyGroupID] = []interface{}{1, "京西果园"}
	} else {
		groupIDMap[qywxapi.BldGroupID] = []interface{}{7, "京西超市"}
		groupIDMap[qywxapi.CsGroupID] = []interface{}{1, "京西菜市"}
	}
	//先找出群组中所有的unionID
	for groupID, list := range groupIDMap {
		var (
			unionIDMap     = make(map[string]string)
			mobileMap      = make(map[string]string)
			mobileExistMap = make(map[string]string)
			mobiles        []string
		)
		stores, _ := dao.GetStoreList(db, nil, nil, []int{model.StoreStatusOpened}, []int{int(utils.Interface2Int64WithDefault(list[0], 0))}, nil, "")
		for _, store := range stores {
			if store.Tel1 != "" {
				mobileMap[store.Tel1] = store.Tel1
			}
			if store.Tel2 != "" {
				mobileMap[store.Tel2] = store.Tel2
			}
		}
		group, _ := api.QywxAPI.Groupchat(groupID)
		for _, member := range group.GroupChat.MemberList {
			if member.UnionID != "" {
				unionIDMap[member.UnionID] = member.UnionID
			}
		}

		//再找出群组中所有unionID对应的电话
		for _, v := range unionIDMap {
			binds, _ := dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, nil, "", v, nil)
			if len(binds) > 0 {
				user, _ := dao.GetUserByID(db, "user_id", binds[0].UserID)
				if user != nil && user.Mobile != nil {
					mobileExistMap[*user.Mobile] = *user.Mobile
				}
			}
		}
		for _, v := range mobileMap {
			if mobileExistMap[v] == "" {
				mobiles = append(mobiles, v)
			}
		}
		//最后找出已有门店电话不在已有群组里的发消息
		userMobileMap := make(map[string][]*model.Store)
		for _, v := range mobiles {
			store := &model.Store{}
			sql := `
			SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ?
		`
			sqlParams := []interface{}{v, v, utils.DefaultTimeValue}
			dao.GetRow(db, &store, sql, sqlParams)
			if store != nil {
				if mobileExistMap[store.Tel1] != "" || mobileExistMap[store.Tel2] != "" {
					continue
				}
				//发消息
				var (
					userIDMap = make(map[string]string)
				)
				operatorRoleList := []string{store.OperatorRole, store.OperatorRole2, store.OperatorRole3}
				for _, vv := range operatorRoleList {
					var (
						roleList []*authz.RoleInfo
					)
					if vv != "" {
						roleList = append(roleList, autils.NewRole(vv, 0))
						if userIDMap2, err := GetRolesUserList(jxcontext.AdminCtx, roleList); err == nil {
							for _, w := range userIDMap2 {
								for _, ww := range w {
									userIDMap[ww] = ww
								}
							}
						}
					}
					//noticeMsg := fmt.Sprintf("您有负责的商家还未加入相应的[%s]沟通群，门店ID :[%d]，门店名：[%s]，商家电话：[%s,%s]", list[1].(string), store.ID, store.Name, store.Tel1, store.Tel2)
					user2, err := dao.GetUserByID(db, "mobile", store.MarketManPhone)
					if err == nil {
						userIDMap[user2.UserID] = user2.UserID
						for _, v := range userIDMap {
							userMobileMap[v] = append(userMobileMap[v], store)
							//if api.DingDingAPI.GetToken() != "" {
							//ddmsg.SendUserMessage(dingdingapi.MsgTyeText, v, "您有商家还未加入企业微信群", noticeMsg)
							//}
						}
					}
				}
			}
		}
		//一次发太多了，尝试分组发
		groupList := []string{}
		groupListAll := [][]string{}
		for k, v := range userMobileMap {
			msg := fmt.Sprintf("您有负责的商家还未加入相应的[%s]沟通群，门店信息 :", list[1].(string))
			for i := 0; i < len(v); i++ {
				if (i+1)%25 != 0 {
					msg += utils.Int2Str(v[i].ID) + "，" + v[i].Name + "，商家电话：" + v[i].Tel1 + "，" + v[i].Tel2 + "，平台负责人：" + v[i].MarketManPhone + "，"
				} else {
					groupList = append(groupList, k, msg)
					groupListAll = append(groupListAll, groupList)
					groupList = []string{}
					msg = fmt.Sprintf("您有负责的商家还未加入相应的[%s]沟通群，门店信息 :", list[1].(string))
					msg += utils.Int2Str(v[i].ID) + "，" + v[i].Name + "，商家电话：" + v[i].Tel1 + "，" + v[i].Tel2 + "，平台负责人：" + v[i].MarketManPhone + "，"
				}
				if i == len(v)-1 {
					groupList = append(groupList, k, msg)
					groupListAll = append(groupListAll, groupList)
				}
			}
		}
		for _, v := range groupListAll {
			if api.DingDingAPI.GetToken() != "" {
				ddmsg.SendUserMessage(dingdingapi.MsgTyeText, v[0], "您有商家还未加入企业微信群", v[1])
			}
		}
	}
	return err
}

type UserInfo struct {
	model.User
	WxInfo    string      `json:"wxInfo"`
	BrandMaps []*BrandMap `json:"brandMaps"`
}

type BrandMap struct {
	BrandID     int    `orm:"column(brand_id)" json:"brandID"`
	Name        string `json:"name"`
	ManageCount int    `json:"managerCount"`
	Balance     int    `json:"balance"`
}

func GetUserInfo(ctx *jxcontext.Context) (userInfo *UserInfo, err error) {
	var (
		db = dao.GetDB()
	)
	sql := `
		SELECT a.*, b.detail_data wx_info
		  FROM user a
	 LEFT JOIN auth_bind b ON a.user_id = b.user_id AND b.type_id = ? AND b.type = ?
		 WHERE a.user_id = ?
	`
	sqlParams := []interface{}{"wx2bb99eb5d2c9b82c", "weixinsns", ctx.GetUserID()}
	if err = dao.GetRow(db, &userInfo, sql, sqlParams); err == nil && userInfo != nil {
		if brandUsers, _ := dao.GetBrandUser(db, 0, ctx.GetUserID()); len(brandUsers) > 0 {
			for _, v := range brandUsers {
				brands, _ := dao.GetBrands(db, "", v.BrandID, "", false, "")
				balance, _ := partner.CurStoreAcctManager.GetBrandBalance(v.BrandID)
				brandMap := &BrandMap{
					BrandID: v.BrandID,
					Name:    brands[0].Name,
					Balance: balance,
				}
				stores, _ := dao.GetStoreList(db, nil, nil, nil, []int{v.BrandID}, nil, "")
				brandMap.ManageCount = len(stores)
				userInfo.BrandMaps = append(userInfo.BrandMaps, brandMap)
			}
		}
	}
	return userInfo, err
}
