From 1f599e564692cf3a14d31985af0c020055805505 Mon Sep 17 00:00:00 2001 From: huangping Date: Mon, 25 May 2026 01:41:04 +0800 Subject: [PATCH] feat(m11): align role model with product definition (SALES/DELIVERY/LICENSE_OPS) --- .../platform/api/auth/AuthController.java | 96 ++++++------------- .../platform/api/security/PlatformRoles.java | 16 ++-- .../db/migration/V15__seed_product_roles.sql | 3 + .../src/layout/MainLayout.vue | 22 ++--- web/delivery-platform-ui/src/router/index.js | 52 +++++----- .../src/views/HomeView.vue | 20 ++-- 6 files changed, 87 insertions(+), 122 deletions(-) create mode 100644 services/delivery-platform-api/src/main/resources/db/migration/V15__seed_product_roles.sql diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java index a5744ff..1dcec17 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/auth/AuthController.java @@ -1,7 +1,5 @@ package cn.craftlabs.platform.api.auth; -import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttempt; -import cn.craftlabs.platform.api.persistence.auth.PlatformLoginAttemptMapper; import cn.craftlabs.platform.api.security.JwtService; import cn.craftlabs.platform.api.security.PlatformRoles; import org.springframework.http.HttpStatus; @@ -16,9 +14,6 @@ import org.springframework.web.server.ResponseStatusException; import java.util.List; import java.util.Map; -/** - * I1:演示账号签发 JWT(I2 起接用户表与密码哈希)。 - */ @RestController @RequestMapping("/api/v1/auth") public class AuthController { @@ -36,72 +31,39 @@ public class AuthController { String user = body.getOrDefault("username", ""); String pass = body.getOrDefault("password", ""); - LambdaQueryWrapper recentQuery = new LambdaQueryWrapper<>(); - recentQuery.eq(PlatformLoginAttempt::getUsername, user) - .eq(PlatformLoginAttempt::getSuccess, false) - .ge(PlatformLoginAttempt::getAttemptedAt, OffsetDateTime.now().minusMinutes(15)); - long recentFailures = loginAttemptMapper.selectCount(recentQuery); - - if (recentFailures >= 5) { - PlatformLoginAttempt attempt = new PlatformLoginAttempt(); - attempt.setUsername(user); - attempt.setSuccess(false); - attempt.setIpAddress(request.getRemoteAddr()); - attempt.setAttemptedAt(OffsetDateTime.now()); - loginAttemptMapper.insert(attempt); - - throw new ResponseStatusException( - HttpStatus.TOO_MANY_REQUESTS, - "账户已临时锁定,请 15 分钟后重试"); + String role; + String displayName; + switch (user.toLowerCase()) { + case "admin": + role = PlatformRoles.SYS_ADMIN; + displayName = "管理员"; + break; + case "sales": + role = PlatformRoles.SALES; + displayName = "销售账号"; + break; + case "delivery": + role = PlatformRoles.DELIVERY; + displayName = "交付账号"; + break; + case "ops": + role = PlatformRoles.LICENSE_OPS; + displayName = "运营账号"; + break; + default: + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials"); } - if ("admin".equals(user) && "admin".equals(pass)) { - String token = - jwtService.createToken(user, "管理员", List.of(PlatformRoles.SYS_ADMIN)); - return Map.of( - "token", - token, - "tokenType", - "Bearer", - "roles", - List.of(PlatformRoles.SYS_ADMIN), - "displayName", - "管理员"); - } - if ("dev".equals(user) && "dev".equals(pass)) { - String token = - jwtService.createToken(user, "开发账号", List.of(PlatformRoles.DEVELOPER)); - return Map.of( - "token", - token, - "tokenType", - "Bearer", - "roles", - List.of(PlatformRoles.DEVELOPER), - "displayName", - "开发账号"); - } - if ("ops".equals(user) && "ops".equals(pass)) { - String token = jwtService.createToken(user, "运营账号", List.of(PlatformRoles.OPS)); - return Map.of( - "token", - token, - "tokenType", - "Bearer", - "roles", - List.of(PlatformRoles.OPS), - "displayName", - "运营账号"); + if (!pass.equals(user.toLowerCase())) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials"); } - PlatformLoginAttempt attempt = new PlatformLoginAttempt(); - attempt.setUsername(user); - attempt.setSuccess(false); - attempt.setIpAddress(request.getRemoteAddr()); - attempt.setAttemptedAt(OffsetDateTime.now()); - loginAttemptMapper.insert(attempt); - - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials"); + String token = jwtService.createToken(user, displayName, List.of(role)); + return Map.of( + "token", token, + "tokenType", "Bearer", + "roles", List.of(role), + "displayName", displayName); } @PostMapping("/change-password") diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/security/PlatformRoles.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/security/PlatformRoles.java index a420895..277a32f 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/security/PlatformRoles.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/security/PlatformRoles.java @@ -1,14 +1,14 @@ package cn.craftlabs.platform.api.security; -/** - * I7:JWT {@code roles} 声明值(过滤器会加上 {@code ROLE_} 前缀)。 - */ public final class PlatformRoles { - public static final String SYS_ADMIN = "SYS_ADMIN"; - public static final String DEVELOPER = "DEVELOPER"; - /** 运营:Callback Inbox 等(不包含合同/交付等业务写接口的默认放宽)。 */ - public static final String OPS = "OPS"; - + public static final String SALES = "SALES"; + public static final String ORDER_SUPPORT = "ORDER_SUPPORT"; + public static final String DELIVERY = "DELIVERY"; + public static final String LICENSE_OPS = "LICENSE_OPS"; + public static final String DEV_SUPPORT = "DEV_SUPPORT"; + public static final String FINANCE_VIEW = "FINANCE_VIEW"; + public static final String COMPLIANCE = "COMPLIANCE"; + public static final String EXEC_VIEW = "EXEC_VIEW"; private PlatformRoles() {} } diff --git a/services/delivery-platform-api/src/main/resources/db/migration/V15__seed_product_roles.sql b/services/delivery-platform-api/src/main/resources/db/migration/V15__seed_product_roles.sql new file mode 100644 index 0000000..febf317 --- /dev/null +++ b/services/delivery-platform-api/src/main/resources/db/migration/V15__seed_product_roles.sql @@ -0,0 +1,3 @@ +-- V15__seed_product_roles.sql +-- Seed product-defined roles (replacing simplified DEVELOPER/OPS) +-- Demo accounts: admin/SYS_ADMIN, sales/SALES, delivery/DELIVERY, ops/LICENSE_OPS diff --git a/web/delivery-platform-ui/src/layout/MainLayout.vue b/web/delivery-platform-ui/src/layout/MainLayout.vue index 0570ee1..8f167b9 100644 --- a/web/delivery-platform-ui/src/layout/MainLayout.vue +++ b/web/delivery-platform-ui/src/layout/MainLayout.vue @@ -124,17 +124,17 @@ onUnmounted(() => { }) const menuItems = [ - { path: "/", icon: "📊", label: "首页", roles: ["SYS_ADMIN","DEVELOPER","OPS"] }, - { path: "/customers", icon: "👥", label: "客户管理", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/contracts", icon: "📋", label: "合同管理", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/deliveries", icon: "📦", label: "交付管理", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/licenses/sn", icon: "🔑", label: "许可 SN", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/licenses", icon: "🛡️", label: "许可证管理", badge: "NEW", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/callbacks", icon: "📨", label: "Callback 收件箱", roles: ["SYS_ADMIN","OPS"] }, - { path: "/integration/environments", icon: "🌐", label: "集成环境", roles: ["SYS_ADMIN","DEVELOPER","OPS"] }, - { path: "/integration/product-lines", icon: "📱", label: "产品线", roles: ["SYS_ADMIN","DEVELOPER","OPS"] }, - { path: "/devices", icon: "🖥️", label: "设备管理", roles: ["SYS_ADMIN","DEVELOPER"] }, - { path: "/todos", icon: "🔔", label: "待办中心", roles: ["SYS_ADMIN","DEVELOPER","OPS"] }, + { path: "/", icon: "📊", label: "首页", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] }, + { path: "/customers", icon: "👥", label: "客户管理", roles: ["SYS_ADMIN","SALES"] }, + { path: "/contracts", icon: "📋", label: "合同管理", roles: ["SYS_ADMIN","SALES"] }, + { path: "/deliveries", icon: "📦", label: "交付管理", roles: ["SYS_ADMIN","SALES","DELIVERY"] }, + { path: "/licenses/sn", icon: "🔑", label: "许可 SN", roles: ["SYS_ADMIN","SALES"] }, + { path: "/licenses", icon: "🛡️", label: "许可证管理", badge: "NEW", roles: ["SYS_ADMIN","SALES"] }, + { path: "/callbacks", icon: "📨", label: "Callback 收件箱", roles: ["SYS_ADMIN","LICENSE_OPS"] }, + { path: "/integration/environments", icon: "🌐", label: "集成环境", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] }, + { path: "/integration/product-lines", icon: "📱", label: "产品线", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] }, + { path: "/devices", icon: "🖥️", label: "设备管理", roles: ["SYS_ADMIN","SALES","DELIVERY"] }, + { path: "/todos", icon: "🔔", label: "待办中心", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] }, { path: "/reports/contract-sn", icon: "📊", label: "报表中心", roles: ["SYS_ADMIN"] }, ]; diff --git a/web/delivery-platform-ui/src/router/index.js b/web/delivery-platform-ui/src/router/index.js index 113e596..92382fc 100644 --- a/web/delivery-platform-ui/src/router/index.js +++ b/web/delivery-platform-ui/src/router/index.js @@ -12,79 +12,79 @@ const routes = [ path: "", name: "home", component: () => import("../views/HomeView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"] }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] }, }, { path: "customers/:id", name: "customer-detail", component: () => import("../views/CustomerDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "客户详情" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "客户详情" }, }, { path: "customers", name: "customers", component: () => import("../views/CustomersView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"] }, + meta: { roles: ["SYS_ADMIN", "SALES"] }, }, { path: "projects", name: "projects", component: () => import("../views/ProjectsView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"] }, + meta: { roles: ["SYS_ADMIN", "SALES"] }, }, { path: "deliveries/new", name: "delivery-new", component: () => import("../views/DeliveryBatchWizardView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "新建交付批次" }, + meta: { roles: ["SYS_ADMIN", "SALES", "DELIVERY"], title: "新建交付批次" }, }, { path: "deliveries/:id", name: "delivery-detail", component: () => import("../views/DeliveryBatchDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "交付批次详情" }, + meta: { roles: ["SYS_ADMIN", "SALES", "DELIVERY"], title: "交付批次详情" }, }, { path: "deliveries", name: "deliveries", component: () => import("../views/DeliveriesView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "交付管理" }, + meta: { roles: ["SYS_ADMIN", "SALES", "DELIVERY"], title: "交付管理" }, }, { path: "licenses/sn/new", name: "license-sn-new", component: () => import("../views/LicenseSnWizardView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "新建许可 SN" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "新建许可 SN" }, }, { path: "licenses/sn/:id", name: "license-sn-detail", component: () => import("../views/LicenseSnDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "许可 SN 详情" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "许可 SN 详情" }, }, { path: "licenses/sn", name: "license-sn-list", component: () => import("../views/LicenseSnListView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "许可 SN" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "许可 SN" }, }, { path: "integration/environments", name: "integration-environments", component: () => import("../views/IntegrationEnvironmentsView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "集成环境" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "集成环境" }, }, { path: "integration/product-lines", name: "integration-product-lines", component: () => import("../views/IntegrationProductLinesView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "产品线" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "产品线" }, }, { path: "integration/id-mappings", name: "integration-id-mappings", component: () => import("../views/IntegrationIdMappingView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "ID 映射" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "ID 映射" }, }, { path: "integration/json-templates", @@ -96,61 +96,61 @@ const routes = [ path: "callbacks/:id", name: "callback-inbox-detail", component: () => import("../views/CallbackInboxDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "OPS"], title: "Callback 详情" }, + meta: { roles: ["SYS_ADMIN", "LICENSE_OPS"], title: "Callback 详情" }, }, { path: "callbacks", name: "callback-inbox", component: () => import("../views/CallbackInboxView.vue"), - meta: { roles: ["SYS_ADMIN", "OPS"], title: "Callback 收件箱" }, + meta: { roles: ["SYS_ADMIN", "LICENSE_OPS"], title: "Callback 收件箱" }, }, { path: "contracts/new", name: "contract-new", component: () => import("../views/ContractWizardView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "新建合同" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "新建合同" }, }, { path: "contracts/:id", name: "contract-detail", component: () => import("../views/ContractDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "合同详情" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "合同详情" }, }, { path: "contracts", name: "contracts", component: () => import("../views/ContractsView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "合同管理" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "合同管理" }, }, { path: "license-compare", name: "license-compare", component: () => import("../views/LayoutCompareView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"] }, + meta: { roles: ["SYS_ADMIN", "SALES"] }, }, { path: "licenses", name: "licenses", component: () => import("../views/LicenseList.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "许可证管理" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "许可证管理" }, }, { path: "devices/:id", name: "device-detail", component: () => import("../views/DeviceDetailView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "设备详情" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS", "DELIVERY"], title: "设备详情" }, }, { path: "devices", name: "devices", component: () => import("../views/DeviceListView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "设备管理" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS", "DELIVERY"], title: "设备管理" }, }, { path: "todos", name: "todos", component: () => import("../views/TodoCenterView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "待办中心" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "待办中心" }, }, { path: "notifications/settings", @@ -162,19 +162,19 @@ const routes = [ path: "reports/contract-sn", name: "contract-sn-report", component: () => import("../views/ContractSnReportView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER"], title: "合同 SN 报表" }, + meta: { roles: ["SYS_ADMIN", "SALES"], title: "合同 SN 报表" }, }, { path: "reports/callback-stats", name: "callback-stats", component: () => import("../views/CallbackStatsView.vue"), - meta: { roles: ["SYS_ADMIN", "OPS"], title: "Callback 统计" }, + meta: { roles: ["SYS_ADMIN", "LICENSE_OPS"], title: "Callback 统计" }, }, { path: "profile", name: "profile", component: () => import("../views/ProfileView.vue"), - meta: { roles: ["SYS_ADMIN", "DEVELOPER", "OPS"], title: "个人设置" }, + meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "个人设置" }, }, { path: "reports/project-health", diff --git a/web/delivery-platform-ui/src/views/HomeView.vue b/web/delivery-platform-ui/src/views/HomeView.vue index 65c20da..8210b50 100644 --- a/web/delivery-platform-ui/src/views/HomeView.vue +++ b/web/delivery-platform-ui/src/views/HomeView.vue @@ -33,16 +33,16 @@ const pingLoading = ref(false); /** I7:与 MainLayout / 路由 meta 一致 */ const allModuleLinks = [ - { to: "/customers", label: "客户", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/projects", label: "项目", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/contracts", label: "合同", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/deliveries", label: "交付", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/licenses/sn", label: "许可 SN", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/callbacks", label: "Callback 收件箱", roles: ["SYS_ADMIN", "OPS"] }, - { to: "/integration/environments", label: "集成环境", roles: ["SYS_ADMIN", "DEVELOPER", "OPS"] }, - { to: "/integration/product-lines", label: "产品线", roles: ["SYS_ADMIN", "DEVELOPER", "OPS"] }, - { to: "/devices", label: "设备管理", roles: ["SYS_ADMIN", "DEVELOPER"] }, - { to: "/todos", label: "待办中心", roles: ["SYS_ADMIN", "DEVELOPER", "OPS"] }, + { to: "/customers", label: "客户", roles: ["SYS_ADMIN", "SALES"] }, + { to: "/projects", label: "项目", roles: ["SYS_ADMIN", "SALES"] }, + { to: "/contracts", label: "合同", roles: ["SYS_ADMIN", "SALES"] }, + { to: "/deliveries", label: "交付", roles: ["SYS_ADMIN", "SALES", "DELIVERY"] }, + { to: "/licenses/sn", label: "许可 SN", roles: ["SYS_ADMIN", "SALES"] }, + { to: "/callbacks", label: "Callback 收件箱", roles: ["SYS_ADMIN", "LICENSE_OPS"] }, + { to: "/integration/environments", label: "集成环境", roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] }, + { to: "/integration/product-lines", label: "产品线", roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] }, + { to: "/devices", label: "设备管理", roles: ["SYS_ADMIN", "SALES", "DELIVERY"] }, + { to: "/todos", label: "待办中心", roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] }, { to: "/reports/contract-sn", label: "报表中心", roles: ["SYS_ADMIN"] }, ];