Files
jx-callback/business/jxstore/cms/user2.go
suyl 48be414a4f aa
2021-06-03 15:29:18 +08:00

1348 lines
42 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cms
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"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"
beego "github.com/astaxie/beego/server/web"
"sort"
"strings"
"sync"
"time"
"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) {
globals.SugarLogger.Debugf("GetUser, authID:%s, authIDType:%s", authID, authIDType)
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 // 先不区分商户与消息者
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 {
// globals.SugarLogger.Debugf("testRegisterUserWithMobile", utils.Format4Output(mobileAuth, false), utils.Format4Output(newAuthInfo, false))
// 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) {
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)
globals.SugarLogger.Warn(msg)
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.AutonaviAPI.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) {
globals.SugarLogger.Debugf("CreateUser user:%s, creatorName:%s", utils.Format4Output(user, true), creatorName)
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 != "" && 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)
}
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)
globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s left company", userIDStr)
if authBind, err = dao.GetAuthBind(db, model.AuthBindTypeAuth, dingding.AuthTypeStaff, userIDStr); err == nil { // 直接找到了
globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s, userID:%s left company", userIDStr, authBind.UserID)
if err = DisableUser(jxcontext.AdminCtx, authBind.UserID); err != nil {
globals.SugarLogger.Errorf("OnDingDingMsg failed with error:%v", err)
}
}
}
}
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)
return storeList, err
}
func GetMyStoreListNew(ctx *jxcontext.Context) (storesInfo interface{}, errCode string, err error) {
if !auth2.IsV2Token(ctx.GetToken()) {
return nil, model.ErrCodeTokenIsInvalid, model.ErrTokenIsInvalid
}
mobile, userID := ctx.GetMobileAndUserID()
if mobile == "" {
return nil, "", fmt.Errorf("不能得到用户手机号")
}
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 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) (formattedAddress string, districtCode, cityCode int, err error) {
regeoInfo, err := api.AutonaviAPI.GeoCodeRegeoSingle(lng, lat, 0, false, nil, 0, 0)
if err == nil {
formattedAddress = regeoInfo.FormattedAddress
districtCode = int(utils.Str2Int64WithDefault(regeoInfo.AddressComponent.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
}
}
return formattedAddress, districtCode, cityCode, err
}
func AddUserDeliveryAddress(ctx *jxcontext.Context, address *model.UserDeliveryAddress) (outAddress *model.UserDeliveryAddress, err error) {
globals.SugarLogger.Debugf("AddUserDeliveryAddress1 address:%s", utils.Format4Output(address, true))
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)
if err == nil {
globals.SugarLogger.Debugf("AddUserDeliveryAddress2 address:%s", utils.Format4Output(address, true))
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)
}
}
globals.SugarLogger.Debugf("AddUserDeliveryAddress3 address:%s", utils.Format4Output(address, true))
return outAddress, err
}
func AddMyDeliveryAddress(ctx *jxcontext.Context, address *model.UserDeliveryAddress) (outAddress *model.UserDeliveryAddress, err error) {
globals.SugarLogger.Debugf("AddMyDeliveryAddress address:%s", utils.Format4Output(address, true))
_, address.UserID = ctx.GetMobileAndUserID()
outAddress, err = AddUserDeliveryAddress(ctx, address)
globals.SugarLogger.Debugf("AddMyDeliveryAddress2 address:%s, err:%v", utils.Format4Output(address, true), err)
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)
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, nil)
/* 删除离职人员门店运营人员 */
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, nil)
/* 删除离职人员门店运营人员 */
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, nil)
/*删除离职人员的角色信息*/
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, nil)
//删除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 != 0 {
user.LastStoreID = storeID
_, err = dao.UpdateEntity(db, user, "LastStoreID")
}
if brandID != 0 {
user.LastBrandID = brandID
_, err = dao.UpdateEntity(db, user, "LastBrandID")
}
}
return err
}
func GetUserStoreAuth(ctx *jxcontext.Context, storeID int) (outStoreID int, err error) {
if permission.IsRoled(ctx) {
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 == "jxgy" {
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 + ""
} 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 + ""
}
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
}