package cms import ( "fmt" "reflect" "regexp" "strconv" "strings" "time" "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai" "git.rosy.net.cn/baseapi/utils/errlist" "git.rosy.net.cn/jx-callback/globals/api2" "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/jx-callback/business/auth2/authprovider/mobile" "git.rosy.net.cn/jx-callback/business/authz/autils" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals/api" ) const ( SendMsgTypeOpenStoreRequest = "openStoreRequest" SendMsgTypeSuggestRequest = "suggestRequest" ) type SysConfigLimit struct { ValueType reflect.Kind MinValue int64 MaxValue int64 AfterChanged func() error } var ( serviceInfo map[string]interface{} allowUpdatePlaceFieldsMap = map[string]bool{ "name": true, "enabled": true, "mtpsPrice": true, } regexpMsgContentOpID = regexp.MustCompile(`"openid":"(.*?)"`) receiveMsgUsersMap = map[string][]string{ SendMsgTypeOpenStoreRequest: []string{ "石锋", // "x", // "周扬", }, SendMsgTypeSuggestRequest: []string{ "石锋", // "x", // "周扬", // "苏尹岚", }, } needConfirmRequestMap = map[string]int{ SendMsgTypeOpenStoreRequest: 1, } SysConfigLimitMap = map[string]*SysConfigLimit{ model.ConfigSysEbaiBoxFee: &SysConfigLimit{ ValueType: reflect.Int, MinValue: 0, MaxValue: 500, AfterChanged: func() (err error) { _, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDEBAI}, nil, model.SyncFlagModifiedMask) return err }, }, model.ConfigSysMtwmBoxFee: &SysConfigLimit{ ValueType: reflect.Int, MinValue: 0, MaxValue: 500, AfterChanged: func() (err error) { _, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDMTWM}, nil, model.SyncFlagModifiedMask) return err }, }, model.ConfigSysMtwmSkuBoxFee: &SysConfigLimit{ ValueType: reflect.Int, MinValue: 0, MaxValue: 50, AfterChanged: func() (err error) { _, err = dao.SetStoreSkuSyncStatus(dao.GetDB(), model.VendorIDMTWM, nil, nil, model.SyncFlagModifiedMask) return err }, }, } ) func InitServiceInfo(version string, buildTime time.Time, gitCommit string) { buildTimeStr := "" if !utils.IsTimeZero(buildTime) { buildTimeStr = utils.Time2Str(buildTime) } serviceInfo = map[string]interface{}{ "startupTime": utils.Time2Str(time.Now()), "version": version, "buildTime": buildTimeStr, "gitCommit": gitCommit, "metaData": map[string]interface{}{ "skuNamePrefix": model.SkuNamePrefixNames, "skuNameUnit": model.UnitNames, "skuSpecUnit": model.SpecUnitNames, "skuStatus": model.SkuStatusName, "storeDeliveryRangeType": model.DeliveryRangeTypeName, "storeDeliveryType": model.DeliveryTypeName, "storeStatus": model.StoreStatusName, "categoryType": model.CategoryTypeName, "vendorTypeName": model.VendorTypeName, "vendorName": model.VendorChineseNames, "orderStatus": model.OrderStatusName, "waybillStatus": model.WaybillStatusName, "orderTypeName": model.OrderTypeName, "taskStatusName": tasksch.TaskStatusName, "opRequestTypeName": model.RequestTypeName, "opRequestStatusName": model.RequestStatusName, "storeMsgSendStatusName": model.StoreMsgSendStatusName, "shopChineseNames": model.ShopChineseNames, "printerVendorInfo": model.PrinterVendorInfo, "printerStatusName": partner.PrinterStatusName, "purchaseVendorInfo": model.PurchaseVendorInfo, "afsReasonTypeName": model.AfsReasonTypeName, "afsAppealTypeName": model.AfsAppealTypeName, "actTypeName": model.ActTypeName, "actStatusName": model.ActStatusName, "actCreateTypeName": model.ActCreateTypeName, "storeAuditStatusName": model.StoreAuditStatusName, "configTypeName": model.ConfigTypeName, "autoSaleAt": AutoSaleAtStr, "userTypeName": model.UserTypeName, "storePriceTypeName": model.StorePriceTypeName, "payStatusName": model.PayStatusName, "refundStatusName": model.RefundStatusName, "autoReplyTypeName": model.AutoReplyTypeName, "complaintReasons": model.ComplaintReasons, "supplementType": model.SupplementTypeName, "operateType": model.OperateTypeName, "thingType": model.ThingTypeName, "apiFunctionName": model.ApiFunctionName, "vendorStatus": model.VendorStatus, "couponsStatus": model.CouponStatusName, "ebaiSupplierID": ebai.EbaiSupplierIDMap, "ebaiSupplierInfo": ebai.EbaiSupplierInfo, }, } } func GetServiceInfo(ctx *jxcontext.Context) map[string]interface{} { return serviceInfo } func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, params map[string]interface{}) ([]*model.Place, error) { sql := ` SELECT * FROM place t1 WHERE 1 = 1 ` if !includeDisabled { sql += " AND enabled = 1" } sqlParams := make([]interface{}, 0) if keyword != "" { sql += " AND (t1.name LIKE ?" sqlParams = append(sqlParams, "%"+keyword+"%") if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { sql += " OR t1.code = ?" sqlParams = append(sqlParams, keywordInt64) } sql += ")" } if params["parentCode"] != nil { sql += " AND t1.parent_code = ?" sqlParams = append(sqlParams, params["parentCode"]) } if params["level"] != nil { sql += " AND t1.level = ?" sqlParams = append(sqlParams, params["level"]) } sql += " ORDER BY t1.level, t1.name" // globals.SugarLogger.Debug(sql) places := []*model.Place{} return places, dao.GetRows(nil, &places, sql, sqlParams) } func UpdatePlaces(ctx *jxcontext.Context, places []map[string]interface{}, userName string) (num int64, err error) { if len(places) == 0 { return 0, ErrMissingInput } updateFields := []string{} for k := range places[0] { if allowUpdatePlaceFieldsMap[k] { updateFields = append(updateFields, k) } } for _, place := range places { if place["code"] == nil { return 0, ErrMissingInput } placeid := &model.Place{} valid := dao.NormalMakeMapByFieldList(place, updateFields, userName) if num, err = dao.UpdateEntityLogically(nil, placeid, valid, userName, utils.Params2Map("Code", place["code"])); err != nil { return num, err } } return num, err } func UpdatePlace(ctx *jxcontext.Context, placeCode int, payload map[string]interface{}, userName string) (num int64, err error) { payload["code"] = placeCode return UpdatePlaces(ctx, []map[string]interface{}{payload}, userName) } func GetCoordinateDistrictCode(ctx *jxcontext.Context, lng, lat float64) (code int, err error) { return api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat), nil } func GetCoordinateCityInfo(ctx *jxcontext.Context, lng, lat float64) (name string, err error) { name, _ = api.AutonaviAPI.GetCoordinateCityInfo(lng, lat) return name, err } func SendMsg2Somebody(ctx *jxcontext.Context, mobileNum, verifyCode, msgType, msgContent string) (err error) { if needConfirmRequestMap[msgType] == 1 { if _, err = mobile.AutherObj.VerifySecret(mobileNum, verifyCode); err != nil { return err } } db := dao.GetDB() //获取门店信息 var ( stores []*model.Store authBinds []*model.AuthBind order *model.GoodsOrder storeName string storeID int vendorOrderID string ) if mobileNum != "" { sql := ` SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ? ` sqlParams := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue} err = dao.GetRows(db, &stores, sql, sqlParams) if len(stores) > 0 { storeName = stores[0].Name storeID = stores[0].ID } if storeID == 0 { results := regexpMsgContentOpID.FindStringSubmatch(msgContent) if len(results) > 0 { sql3 := ` SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ? ` sqlParams3 := []interface{}{results[1], results[1]} err = dao.GetRows(db, &authBinds, sql3, sqlParams3) if len(authBinds) > 0 { user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID) mobileNum = *user.Mobile sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue} err = dao.GetRows(db, &stores, sql, sqlParams4) if len(stores) > 0 { storeName = stores[0].Name storeID = stores[0].ID } } } } } else { results := regexpMsgContentOpID.FindStringSubmatch(msgContent) if len(results) > 0 { sql3 := ` SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ? ` sqlParams3 := []interface{}{results[1], results[1]} err = dao.GetRows(db, &authBinds, sql3, sqlParams3) if len(authBinds) > 0 { user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID) mobileNum = *user.Mobile sql4 := ` SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ? ` sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue} err = dao.GetRows(db, &stores, sql4, sqlParams4) if len(stores) > 0 { storeName = stores[0].Name storeID = stores[0].ID } } } } sql2 := ` SELECT * FROM goods_order WHERE IF(store_id <> '', store_id, jx_store_id) = ? ORDER BY order_created_at DESC LIMIT 1 ` sqlParams2 := []interface{}{storeID} err = dao.GetRow(db, &order, sql2, sqlParams2) if order != nil { vendorOrderID = order.VendorOrderID } if storeID == 0 { vendorOrderID = "" } msgContent = msgContent + " 门店名称:" + storeName + " 门店ID:" + utils.Int2Str(storeID) + " 最新订单号:" + vendorOrderID for _, v := range receiveMsgUsersMap[msgType] { user, err2 := dao.GetUserByID(db, "name", v) if err2 == nil { ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.GetID(), msgType, msgContent) } else if err == nil { err = err2 } } return err } func checkSysConfig(key, value string) (err error) { if limit := SysConfigLimitMap[key]; limit != nil { if limit.ValueType == reflect.Int { int64Value, err2 := strconv.ParseInt(value, 10, 64) if err = err2; err == nil { if int64Value < limit.MinValue || int64Value > limit.MaxValue { err = fmt.Errorf("配置%s,值%s超范围[%d,%d]", key, value, limit.MinValue, limit.MaxValue) } } } } return err } func onSysConfigChanged(key, value string) (err error) { if limit := SysConfigLimitMap[key]; limit != nil && limit.AfterChanged != nil { err = limit.AfterChanged() } return err } func checkConfig(opFlag int, configType, key, value string) (err error) { switch configType { case model.ConfigTypePricePack: if value != "" { pricePack := dao.PricePercentagePack2Obj(value) if pricePack == nil { err = fmt.Errorf("配置:%s不合法", value) } } case model.ConfigTypeFreightPack: if value != "" { freightPack := dao.FreightDeductionPack2Obj(value) if freightPack == nil { err = fmt.Errorf("配置:%s不合法", value) } else { var lastStage *model.FreightDeductionItem for _, v := range freightPack.FreightDeductionList { if lastStage != nil && lastStage.DeductFreight > v.DeductFreight { err = fmt.Errorf("免运设置不合理:门槛:%s,免运金额:%s,门槛:%s,免运金额:%s", jxutils.IntPrice2StandardString(int64(lastStage.BeginPrice)), jxutils.IntPrice2StandardString(int64(lastStage.DeductFreight)), jxutils.IntPrice2StandardString(int64(v.BeginPrice)), jxutils.IntPrice2StandardString(int64(v.DeductFreight))) return err } lastStage = v } } } case model.ConfigTypeBank: if value != "" { if model.BankName[key] == "" { err = fmt.Errorf("此银行代码:%s不支持,请联系开发", value) } } case model.ConfigTypeRole: case model.ConfigTypeSys: if opFlag&( /*model.SyncFlagNewMask|*/ model.SyncFlagDeletedMask) != 0 { err = fmt.Errorf("系统参数只支持修改或添加,不支持删除") } else { err = checkSysConfig(key, value) } case model.ConfigTypeJxStore: case model.ConfigTypeCookie: case model.ConfigTypeDiscountCard: default: err = fmt.Errorf("当前只支持配置:%s, 传入的配置类型:%s", utils.Format4Output(model.ConfigTypeName, true), configType) } return err } func AddConfig(ctx *jxcontext.Context, key, configType, value string) (err error) { if err = checkConfig(model.SyncFlagNewMask, configType, key, value); err != nil { return err } db := dao.GetDB() conf := &model.NewConfig{ Key: key, Type: configType, Value: value, } dao.WrapAddIDCULDEntity(conf, ctx.GetUserName()) err = dao.CreateEntity(db, conf) if configType == model.ConfigTypeSys && err == nil { err = onSysConfigChanged(key, value) } return err } func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) { if err = checkConfig(model.SyncFlagDeletedMask, configType, key, ""); err != nil { return err } db := dao.GetDB() switch configType { case model.ConfigTypePricePack: storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "") if err = err2; err == nil { var storeInfo []string for _, v := range storeMapList { storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID])) } if len(storeInfo) > 0 { err = fmt.Errorf("还有门店在使用价格包:%s,门店信息:%s", key, strings.Join(storeInfo, ",")) } } case model.ConfigTypeFreightPack: storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "") if err = err2; err == nil { var storeInfo []string for _, v := range storeMapList { if v.FreightDeductionPack == key { storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID])) } } if len(storeInfo) > 0 { err = fmt.Errorf("还有门店在使用价格包:%s,门店信息:%s", key, strings.Join(storeInfo, ",")) } } case model.ConfigTypeBank: //todo return fmt.Errorf("暂不支持删除银行") case model.ConfigTypeRole: errList := errlist.New() userIDs, err2 := api2.RoleMan.GetRoleUserList(autils.NewRole(key, 0)) if err = err2; err == nil && len(userIDs) > 0 { userList, totalCount, err2 := dao.GetUsers(dao.GetDB(), 0, "", userIDs, nil, nil, 0, -1) if err = err2; err == nil && totalCount > 0 { // todo // err = fmt.Errorf("还有人员在使用角色:%s,人员信息:%s", key, utils.MustMarshal(utils.Struct2Map(userList, "compact"))) err = fmt.Errorf("还有人员在使用角色:%s,人员信息:%s", key, utils.Format4Output(userList, false)) } } errList.AddErr(err) storeList, err2 := dao.GetStoreList(db, nil, nil, nil, nil, nil, key) if err = err2; err == nil && len(storeList) > 0 { storeIDs := make([]int, len(storeList)) for k, v := range storeList { storeIDs[k] = v.ID } err = fmt.Errorf("还有门店在使用角色:%s,门店信息:%s", key, utils.MustMarshal(storeIDs)) } errList.AddErr(err) err = errList.GetErrListAsOne() } if err == nil { _, err = dao.DeleteEntityLogically(db, &model.NewConfig{}, nil, ctx.GetUserName(), map[string]interface{}{ "Key": key, "Type": configType, }) } if configType == model.ConfigTypeSys && err == nil { err = onSysConfigChanged(key, "") } return err } func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint string, err error) { if key == "" { return "", fmt.Errorf("修改配置必须给定key") } if err = checkConfig(model.SyncFlagModifiedMask, configType, key, value); err != nil { return "", err } hint = "1" db := dao.GetDB() txDB , _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() configList, err := dao.QueryConfigs(db, key, configType, "") if err != nil { dao.Rollback(db, txDB) return "", err } if _, err = dao.UpdateEntityLogically(db, configList[0], map[string]interface{}{ "Value": value, }, ctx.GetUserName(), nil); err != nil { dao.Rollback(db, txDB) return "", err } switch configType { case model.ConfigTypePricePack: storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "") if err != nil { dao.Rollback(db, txDB) return "", err } dao.Commit(db, txDB) vendorStoreMap := make(map[int][]int) for _, v := range storeMapList { vendorStoreMap[v.VendorID] = append(vendorStoreMap[v.VendorID], v.StoreID) } for vendorID, storeIDs := range vendorStoreMap { if vendorID != model.VendorIDJX { dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, nil, model.SyncFlagPriceMask) } else { hint, err = ReCalculateJxPrice(db, ctx, storeIDs) } } case model.ConfigTypeFreightPack: dao.Commit(db, txDB) storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "") if err != nil { return "", err } for _, v := range storeMapList { var storeMapList2 []*model.StoreMap if v.FreightDeductionPack == key { storeMapList2 = append(storeMapList2, v) } if len(storeMapList2) > 0 { task := tasksch.NewParallelTask("同步门店配送免运", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { storeMap := batchItemList[0].(*model.StoreMap) _, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeMap.StoreID, false, ctx.GetUserName()) return retVal, err }, storeMapList2) tasksch.HandleTask(task, nil, true).Run() if len(storeMapList2) < 5 { _, err = task.GetResult(0) } else { hint = task.GetID() } } } default: dao.Commit(db, txDB) } if configType == model.ConfigTypeSys && err == nil { err = onSysConfigChanged(key, value) } return hint, err } func QueryConfigs(key, configType, keyword string) (configList []*model.NewConfig, err error) { return dao.QueryConfigs(dao.GetDB(), key, configType, keyword) } func GetCityBankBranches(ctx *jxcontext.Context, cityCode int, bankCode string) (info map[int]map[string][]string, err error) { list, err := dao.GetCityBankBranches(dao.GetDB(), cityCode, bankCode) if err == nil && len(list) > 0 { info = make(map[int]map[string][]string) for _, v := range list { if info[v.CityCode] == nil { info[v.CityCode] = make(map[string][]string) } info[v.CityCode][v.PayeeBankCode] = append(info[v.CityCode][v.PayeeBankCode], v.PayeeBankBranchName) } } return info, err }