package app import ( "context" "strings" "git.5iot.com/iotlcm-public/iotlcm-app/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/library/mcache" "github.com/gogf/gf/v2/errors/gerror" ) type appFeatureAccessRule struct { Feature string Permissions []string Routes []string } // appFeatureAccessRules maps mini-program business features to existing console // menu permissions in sys_menu.perms. It deliberately avoids role names so the // app follows the same role-menu binding model as the admin console. var appFeatureAccessRules = []appFeatureAccessRule{ { Feature: "card:list", Permissions: []string{"cpa:terminal:user", "cpm:iot:tSimCardInfo:list"}, Routes: []string{"/pages/card/index"}, }, { Feature: "card:detail", Permissions: []string{"cpm:iot:tSimCardInfo:view", "cpm:iot:tSimCardInfo:list"}, Routes: []string{"/pages/card/views/detail/index"}, }, { Feature: "package:view", Permissions: []string{ "cpm:iot:tSimCardBindPkg:list", "cpm:iot:tSimFlowPackageInfo:list", "cpm:iot:tSimFlowPackageInfo:view", "cpm:iot:tSimCardBindPkg:getPkgInfo", }, Routes: []string{"/pages/card/views/packageDetail/index"}, }, { Feature: "card:bind", Permissions: []string{consts.SysCheckPermissionCurrentUser}, }, { Feature: "card:renew", Permissions: []string{"cpm:iot:tSimCardRenew:batch:confirm"}, Routes: []string{ "/pages/business/views/renew/index", "/pages/business/views/cardRenew/index", }, }, { Feature: "card:renew:submit", Permissions: []string{"cpm:iot:tSimCardRenew:batch:confirm"}, }, { Feature: "card:addon", Permissions: []string{ "cpm:iot:tSimCardBindPkg:list", "cpm:iot:tSimFlowPackageInfo:view", "cpm:iot:tSimCardBindPkg:getPkgInfo", }, Routes: []string{"/pages/business/views/addon/index"}, }, { Feature: "card:change", Permissions: []string{"cpm:iot:tSimCardChange:batch:submit"}, Routes: []string{"/pages/business/views/change/index"}, }, { Feature: "card:change:submit", Permissions: []string{"cpm:iot:tSimCardChange:batch:submit"}, }, { Feature: "card:allocate", Permissions: []string{ "cpm:iot:tSimCardAllocate:targets&cpm:iot:tSimCardAllocate:input:prepare&cpm:iot:tSimCardAllocate:batch:submit", }, Routes: []string{"/pages/business/views/allocate/index"}, }, { Feature: "card:stop", Permissions: []string{"cpm:iot:tSimCardStop:input:submit"}, }, { Feature: "card:start", Permissions: []string{"cpm:iot:tSimCardStart:input:submit"}, }, { Feature: "card:restart", Permissions: []string{"cpm:iot:tSimCardRestart:input:submit"}, }, { Feature: "card:dataStart", Permissions: []string{"cpm:iot:tSimCardDataStart:input:submit"}, }, { Feature: "card:dataStop", Permissions: []string{"cpm:iot:tSimCardDataStop:input:submit"}, }, { Feature: "card:diagnosis", Permissions: []string{ "cpm:iot:tSimdiagnose:info", "cpm:iot:tSimdiagnose:flow", "cpm:iot:tSimCardInfo:querySimSession", "cpm:iot:tSimCardInfo:querySimStatus", }, Routes: []string{"/pages/business/views/diagnosis/index"}, }, { Feature: "warn:view", Permissions: []string{ "cpm:iot:simWarningConfig:list", "cpm:iot:simWarningConfig:view", "cpm:iot:simWarningConfig:save", "cpm:iot:tSimWarningConfig:input:submit", "cpm:iot:tSimWarningConfig:range:submit", "cpm:iot:tSimWarningConfig:excel:submit", }, Routes: []string{"/pages/business/views/warning/index"}, }, { Feature: "order:list", Permissions: []string{ "cpm:common:tBalanceDetail:list", "cpm:common:tFinanceApply:list", "cpm:gen:tFinanceApply:list", }, Routes: []string{"/pages/business/views/order/index"}, }, { Feature: "order:detail", Permissions: []string{ "cpm:common:tBalanceDetail:view", "cpm:common:tBalanceDetail:list", "cpm:common:tFinanceApply:view", "cpm:common:tFinanceApply:list", "cpm:gen:tFinanceApply:list", }, }, { Feature: "customer:list", Permissions: []string{"cpm:agent:dept:list", "cpm:agent:tAgentEnterprise:list"}, Routes: []string{"/pages/business/views/customer/index"}, }, { Feature: "customer:detail", Permissions: []string{"cpm:agent:tAgentEnterprise:view", "cpm:agent:tAgentEnterprise:list"}, Routes: []string{"/pages/business/views/customerDetail/index"}, }, { Feature: "message:view", Permissions: []string{consts.SysCheckPermissionCurrentUser, "cpm:system:notice:list"}, Routes: []string{"/pages/message/index", "/pages/message/views/cardDetail/index", "/pages/message/views/systemDetail/index"}, }, { Feature: "workorder:list", Permissions: []string{"cpm:gen:tWoMain:list"}, Routes: []string{"/pages/business/views/workOrder/index"}, }, { Feature: "workorder:create", Permissions: []string{"cpm:gen:tWoMain:edit"}, Routes: []string{"/pages/business/views/workOrder/index", "/pages/business/views/workOrderCreate/index"}, }, { Feature: "workorder:detail", Permissions: []string{"cpm:gen:tWoMain:view", "cpm:gen:tWoMain:list"}, Routes: []string{"/pages/business/views/workOrderDetail/index"}, }, } func appEnsureFeatureAccess(ctx context.Context, feature string) error { feature = strings.TrimSpace(feature) if feature == "" { return nil } configured, err := appAccessConfigured(ctx) if err != nil { return err } if !configured || contexts.IsSuperAdmin(ctx) { return nil } userId := contexts.GetUserId(ctx) if userId == 0 { return gerror.New("登录已过期") } requiredCodes := appFeaturePermissionCandidates(feature) if len(requiredCodes) == 0 { return nil } accessCodes, _, err := mcache.GetUserAccessCodeList(ctx, userId) if err != nil { return err } for _, requiredCode := range requiredCodes { if appAccessCodesMatchExpression(accessCodes, requiredCode) { return nil } } return gerror.New("暂无操作权限") } func appAccessCodesMatchExpression(accessCodes []string, expression string) bool { parts := appPermissionExpressionParts(expression) if len(parts) == 0 { return false } for _, part := range parts { matched := false for _, accessCode := range accessCodes { if appMatchAccessCode(accessCode, part) { matched = true break } } if !matched { return false } } return true } func appMatchAccessCode(pattern string, code string) bool { pattern = appNormalizePermissionCode(pattern) code = appNormalizePermissionCode(code) if pattern == "" || code == "" { return false } if pattern == code || pattern == "*" || pattern == "cpm:*" { return true } if strings.HasSuffix(pattern, "*") { return strings.HasPrefix(code, pattern[:len(pattern)-1]) } return false } func appFeaturePermissionCandidates(feature string) []string { feature = appNormalizeFeatureKey(feature) if feature == "" { return nil } if feature == "*" { return []string{"*"} } codes := make([]string, 0) for _, rule := range appFeatureAccessRules { if rule.Feature != feature { continue } for _, permission := range rule.Permissions { codes = appendUniqueString(codes, appNormalizePermissionExpression(permission)) } } if len(codes) == 0 && strings.HasPrefix(feature, consts.SysCheckPermissionPrefix) { codes = appendUniqueString(codes, appNormalizePermissionCode(feature)) } return codes } func appFeaturesByPermission(permission string) []string { permission = appNormalizePermissionCode(permission) if permission == "" { return nil } features := make([]string, 0) for _, rule := range appFeatureAccessRules { for _, rulePermission := range rule.Permissions { if strings.Contains(rulePermission, "&") { continue } if appMatchAccessCode(permission, rulePermission) || appMatchAccessCode(rulePermission, permission) { features = appendUniqueString(features, rule.Feature) break } } } return features } func appRoutesByFeature(feature string) []string { feature = appNormalizeFeatureKey(feature) routes := make([]string, 0) for _, rule := range appFeatureAccessRules { if rule.Feature != feature { continue } for _, route := range rule.Routes { routes = appendUniqueString(routes, route) } } return routes } func appKnownPermissionCodes() []string { codes := make([]string, 0) for _, rule := range appFeatureAccessRules { for _, permission := range rule.Permissions { for _, part := range appPermissionExpressionParts(permission) { codes = appendUniqueString(codes, appNormalizePermissionCode(part)) } } } return codes } func appAppendFeaturesByPermissionSet(accessPermissions []string, appendFeature func(string)) { if len(accessPermissions) == 0 || appendFeature == nil { return } for _, rule := range appFeatureAccessRules { for _, permission := range rule.Permissions { if appAccessCodesMatchExpression(accessPermissions, permission) { appendFeature(rule.Feature) break } } } } func appMenuPermCandidates() []string { values := make([]string, 0) for _, permission := range appKnownPermissionCodes() { if permission == "" || permission == consts.SysCheckPermissionCurrentUser { continue } values = appendUniqueString(values, permission) if strings.HasPrefix(permission, consts.SysCheckPermissionMenuPrefix) { values = appendUniqueString(values, strings.TrimPrefix(permission, consts.SysCheckPermissionMenuPrefix)) } } return values } func appNormalizeFeatureKey(value string) string { value = strings.TrimSpace(value) if value == "" { return "" } if value == "*" || value == "cpm:*" { return "*" } return value } func appNormalizePermissionCode(value string) string { value = strings.TrimSpace(value) if value == "" { return "" } for strings.HasPrefix(value, consts.SysCheckPermissionMenuPrefix+consts.SysCheckPermissionMenuPrefix) { value = strings.TrimPrefix(value, consts.SysCheckPermissionMenuPrefix) } if value == "*" || value == consts.SysCheckPermissionCurrentUser || value == consts.SysCheckPermissionTerminalUser || strings.HasPrefix(value, consts.SysCheckPermissionMenuPrefix) || strings.HasPrefix(value, "cpa:") || strings.HasPrefix(value, consts.SysCheckPermissionRolePrefix) || strings.HasPrefix(value, consts.SysCheckPermissionDeptPrefix) || strings.HasPrefix(value, consts.SysCheckPermissionPostPrefix) || strings.HasPrefix(value, consts.SysCheckPermissionUserPrefix) { return value } return consts.SysCheckPermissionMenuPrefix + value } func appNormalizePermissionExpression(value string) string { parts := appPermissionExpressionParts(value) if len(parts) == 0 { return "" } return strings.Join(parts, "&") } func appPermissionExpressionParts(value string) []string { rawParts := strings.Split(strings.TrimSpace(value), "&") parts := make([]string, 0, len(rawParts)) for _, part := range rawParts { part = appNormalizePermissionCode(part) if part != "" { parts = appendUniqueString(parts, part) } } return parts }