Commit f5c2dda98ab577e575e87813031e8717e25f96e3
1 parent
5a1187de
新增菜单与角色管理功能,包括角色分配菜单、菜单树查询和相关接口
Showing
49 changed files
with
1652 additions
and
64 deletions
CLAUDE.md
0 → 100644
| 1 | +# CLAUDE.md | |
| 2 | + | |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | |
| 4 | + | |
| 5 | +## Common commands | |
| 6 | + | |
| 7 | +- `mvn spring-boot:run` — run the service with the checked-in `src/main/resources/application.yml` | |
| 8 | +- `mvn test` — run all tests | |
| 9 | +- `mvn -Dtest=DeliveryFeeServiceImplTest test` — run the pricing-engine test class | |
| 10 | +- `mvn -Dtest=DeliveryFeeServiceImplTest#shouldApplyMinFee test` — run one test method | |
| 11 | +- `mvn package` — build the jar under `target/` | |
| 12 | +- `mvn -DskipTests package` — build without running tests | |
| 13 | +- `mysql -u root -p dili_rider < src/main/resources/schema.sql` — initialize schema | |
| 14 | +- `mysql -u root -p dili_rider < src/main/resources/data-init.sql` — load seed data | |
| 15 | + | |
| 16 | +Notes: | |
| 17 | +- There is no Maven wrapper in this repo; use the system `mvn`. | |
| 18 | +- No dedicated lint/format command is configured in `pom.xml`. | |
| 19 | +- Test coverage is currently sparse; the only checked-in unit tests are in `src/test/java/com/diligrp/rider/service/impl/DeliveryFeeServiceImplTest.java`. | |
| 20 | + | |
| 21 | +## Architecture overview | |
| 22 | + | |
| 23 | +This is a Java 17 / Spring Boot 3.2 monolith for rider delivery operations. The main entrypoint is `src/main/java/com/diligrp/rider/RiderServiceApplication.java`, which enables MyBatis mapper scanning and `@EnableAsync`. | |
| 24 | + | |
| 25 | +### Main layers | |
| 26 | + | |
| 27 | +Code follows a conventional Spring layout under `src/main/java/com/diligrp/rider/`: | |
| 28 | +- `controller` — REST endpoints grouped by caller type | |
| 29 | +- `service` / `service/impl` — business logic | |
| 30 | +- `mapper` — MyBatis-Plus mappers | |
| 31 | +- `entity` — database entities | |
| 32 | +- `dto` / `vo` — request and response shapes | |
| 33 | +- `config` — interceptors, JWT utilities, MVC and WebSocket wiring | |
| 34 | +- `task` — scheduled background jobs | |
| 35 | +- `websocket` — live rider-location subscriptions and push | |
| 36 | +- `common` — shared `Result`, enums, and exception handling | |
| 37 | + | |
| 38 | +Most persistence uses MyBatis-Plus query/update wrappers. XML SQL exists, but only in a few files under `src/main/resources/mapper/`. Logical delete is globally configured through the `isDel` field in `src/main/resources/application.yml`. | |
| 39 | + | |
| 40 | +### API surfaces | |
| 41 | + | |
| 42 | +Controllers are split by audience rather than by technical module: | |
| 43 | +- `/api/rider/**` — rider app APIs | |
| 44 | +- `/api/admin/**` — admin and substation admin APIs | |
| 45 | +- `/api/platform/**` — super-admin platform APIs | |
| 46 | +- `/api/open/**` — signed open-platform APIs for third-party integrations | |
| 47 | +- `/api/delivery/fee/**` — internal fee-calculation APIs | |
| 48 | + | |
| 49 | +All controllers return the shared `Result<T>` envelope from `src/main/java/com/diligrp/rider/common/result/Result.java`. Cross-cutting exception mapping lives in `src/main/java/com/diligrp/rider/common/exception/GlobalExceptionHandler.java`. | |
| 50 | + | |
| 51 | +### Authentication and tenant/city scoping | |
| 52 | + | |
| 53 | +This service does not use Spring Security. Authentication is interceptor-driven: | |
| 54 | +- `src/main/java/com/diligrp/rider/config/AuthInterceptor.java` handles JWT auth for rider/admin/platform APIs. | |
| 55 | +- `src/main/java/com/diligrp/rider/config/OpenApiInterceptor.java` handles signed auth for `/api/open/**` using `X-App-Key`, `X-Timestamp`, `X-Nonce`, and `X-Sign`. | |
| 56 | +- `src/main/java/com/diligrp/rider/config/WebMvcConfig.java` wires both interceptors. | |
| 57 | +- JWT creation/parsing lives in `src/main/java/com/diligrp/rider/config/JwtUtil.java`. | |
| 58 | + | |
| 59 | +Important boundary rule: city/tenant identity is derived from trusted server-side state, not from caller input. | |
| 60 | +- Rider/admin JWTs inject `riderId`, `adminId`, `role`, and sometimes `cityId` into the request. | |
| 61 | +- Substation admins get `cityId` from the `substation` record, not from the request. | |
| 62 | +- Open-platform requests derive `cityId` from the bound `OpenApp`, not from payload fields. | |
| 63 | + | |
| 64 | +If you touch auth or routing, preserve that pattern. | |
| 65 | + | |
| 66 | +Also note: password checking in `RiderAuthServiceImpl` and `AdminAuthServiceImpl` uses MD5 hashing, so do not assume bcrypt/Spring Security conventions are already in place. | |
| 67 | + | |
| 68 | +### Core business flows | |
| 69 | + | |
| 70 | +#### Delivery pricing is DB-driven | |
| 71 | + | |
| 72 | +The pricing engine is centered on `src/main/java/com/diligrp/rider/service/impl/DeliveryFeeServiceImpl.java`, but the actual pricing configuration comes from DB tables, not hardcoded constants. | |
| 73 | + | |
| 74 | +`src/main/java/com/diligrp/rider/service/impl/CityServiceImpl.java` assembles the active pricing plan from: | |
| 75 | +- `delivery_fee_plan` | |
| 76 | +- `delivery_fee_plan_dimension` | |
| 77 | +- `delivery_fee_plan_distance_step` | |
| 78 | +- `delivery_fee_plan_piece_rule` | |
| 79 | +- `delivery_fee_plan_time_rule` | |
| 80 | + | |
| 81 | +That assembled config is then used to compute: | |
| 82 | +- base fee | |
| 83 | +- distance fee / distance steps | |
| 84 | +- weight fee | |
| 85 | +- piece-count fee | |
| 86 | +- time-window surcharge | |
| 87 | +- minimum fee | |
| 88 | +- estimated delivery time | |
| 89 | + | |
| 90 | +If you change pricing behavior, check both `CityServiceImpl` and `DeliveryFeeServiceImpl`, and extend `DeliveryFeeServiceImplTest`. | |
| 91 | + | |
| 92 | +#### Open-platform order creation drives the main order lifecycle | |
| 93 | + | |
| 94 | +`src/main/java/com/diligrp/rider/service/impl/DeliveryOrderServiceImpl.java` is the main open-platform order entry path. It: | |
| 95 | +- resolves the `OpenApp` from `appKey` | |
| 96 | +- forces `cityId` from the app binding | |
| 97 | +- optionally hydrates store info from merchant data | |
| 98 | +- computes the delivery fee | |
| 99 | +- creates the `orders` record | |
| 100 | +- emits webhook notifications for order events | |
| 101 | + | |
| 102 | +That service is a good starting point when tracing order ingestion and external callbacks. | |
| 103 | + | |
| 104 | +#### Dispatch is a scoring engine over DB state | |
| 105 | + | |
| 106 | +`src/main/java/com/diligrp/rider/service/impl/DispatchServiceImpl.java` performs rider selection. It scores candidates using current city, online/rest state, rider location, order load, refusal history, daily counts, and configured dispatch conditions. | |
| 107 | + | |
| 108 | +The dispatch engine depends on: | |
| 109 | +- the active dispatch rule template for the city | |
| 110 | +- current `rider_location` rows | |
| 111 | +- open `orders` | |
| 112 | +- rider/day statistics | |
| 113 | + | |
| 114 | +Scheduled jobs then advance the order lifecycle: | |
| 115 | +- `src/main/java/com/diligrp/rider/task/DispatchScheduleTask.java` runs every 3 seconds to auto-dispatch timed-out grab orders | |
| 116 | +- `src/main/java/com/diligrp/rider/task/OrderScheduleTask.java` runs every 60 seconds to auto-cancel stale unaccepted orders | |
| 117 | + | |
| 118 | +This app is DB-state-driven; there is no message queue coordinating dispatch. | |
| 119 | + | |
| 120 | +### Real-time location flow | |
| 121 | + | |
| 122 | +Live rider location is implemented with raw Spring WebSocket, not STOMP/SockJS. | |
| 123 | +- WebSocket endpoint: `/ws/location` | |
| 124 | +- Config: `src/main/java/com/diligrp/rider/config/LocationWebSocketConfig.java` | |
| 125 | +- Handshake auth: `src/main/java/com/diligrp/rider/websocket/LocationWebSocketHandshakeInterceptor.java` | |
| 126 | +- Update/push path: `src/main/java/com/diligrp/rider/service/impl/RiderLocationServiceImpl.java` | |
| 127 | + | |
| 128 | +Rider location updates are written to `rider_location` and then pushed to subscribed admin clients. Super admins must provide `cityId` when connecting; substation admins derive it from their account. | |
| 129 | + | |
| 130 | +### External integrations | |
| 131 | + | |
| 132 | +External notifications are sent asynchronously by `src/main/java/com/diligrp/rider/service/impl/WebhookServiceImpl.java` using JDK `HttpClient` plus `@Async`. There is no Kafka/RabbitMQ-style event bus in this repo. | |
| 133 | + | |
| 134 | +Redis is present, but its main visible use is SMS verification code storage in `src/main/java/com/diligrp/rider/service/impl/RiderAuthServiceImpl.java`; do not assume Redis-backed sessions or broad caching layers exist. | |
| 135 | + | |
| 136 | +### Database and runtime config | |
| 137 | + | |
| 138 | +- Main runtime config is `src/main/resources/application.yml` | |
| 139 | +- Schema lives in `src/main/resources/schema.sql` | |
| 140 | +- Seed data lives in `src/main/resources/data-init.sql` | |
| 141 | + | |
| 142 | +There is only one checked-in Spring config file; do not assume profile-specific config files already exist. | ... | ... |
src/main/java/com/diligrp/rider/common/auth/AdminScopeGuard.java
0 → 100644
| 1 | +package com.diligrp.rider.common.auth; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.common.exception.BizException; | |
| 4 | +import org.springframework.stereotype.Component; | |
| 5 | + | |
| 6 | +@Component | |
| 7 | +public class AdminScopeGuard { | |
| 8 | + | |
| 9 | + public void assertCityAccessible(Long currentCityId, Long targetCityId) { | |
| 10 | + if (currentCityId == null || currentCityId < 1) { | |
| 11 | + return; | |
| 12 | + } | |
| 13 | + if (targetCityId == null || !currentCityId.equals(targetCityId)) { | |
| 14 | + throw new BizException("只能操作当前城市数据"); | |
| 15 | + } | |
| 16 | + } | |
| 17 | +} | ... | ... |
src/main/java/com/diligrp/rider/common/enums/AdminRoleScopeEnum.java
0 → 100644
src/main/java/com/diligrp/rider/common/enums/MenuScopeEnum.java
0 → 100644
src/main/java/com/diligrp/rider/controller/AdminRefundController.java
| ... | ... | @@ -5,6 +5,7 @@ import com.diligrp.rider.entity.OrderRefundReason; |
| 5 | 5 | import com.diligrp.rider.entity.OrderRefundRecord; |
| 6 | 6 | import com.diligrp.rider.service.RefundService; |
| 7 | 7 | import com.diligrp.rider.service.RiderEvaluateService; |
| 8 | +import jakarta.servlet.http.HttpServletRequest; | |
| 8 | 9 | import lombok.RequiredArgsConstructor; |
| 9 | 10 | import org.springframework.web.bind.annotation.*; |
| 10 | 11 | |
| ... | ... | @@ -29,8 +30,8 @@ public class AdminRefundController { |
| 29 | 30 | |
| 30 | 31 | /** 查看订单退款记录 */ |
| 31 | 32 | @GetMapping("/refund/record") |
| 32 | - public Result<OrderRefundRecord> record(@RequestParam Long orderId) { | |
| 33 | - return Result.success(refundService.getByOrderId(orderId)); | |
| 33 | + public Result<OrderRefundRecord> record(@RequestParam Long orderId, HttpServletRequest request) { | |
| 34 | + return Result.success(refundService.getByOrderId(orderId, resolveScopedCityId(request))); | |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | 37 | /** |
| ... | ... | @@ -42,8 +43,9 @@ public class AdminRefundController { |
| 42 | 43 | public Result<Void> handle( |
| 43 | 44 | @RequestParam Long recordId, |
| 44 | 45 | @RequestParam int status, |
| 45 | - @RequestParam(required = false, defaultValue = "") String remark) { | |
| 46 | - refundService.handleRefund(recordId, status, remark); | |
| 46 | + @RequestParam(required = false, defaultValue = "") String remark, | |
| 47 | + HttpServletRequest request) { | |
| 48 | + refundService.handleRefund(recordId, status, remark, resolveScopedCityId(request)); | |
| 47 | 49 | return Result.success(); |
| 48 | 50 | } |
| 49 | 51 | |
| ... | ... | @@ -52,7 +54,14 @@ public class AdminRefundController { |
| 52 | 54 | public Result<List<?>> evaluateList( |
| 53 | 55 | @RequestParam Long riderId, |
| 54 | 56 | @RequestParam(defaultValue = "0") int type, |
| 55 | - @RequestParam(defaultValue = "1") int page) { | |
| 56 | - return Result.success(evaluateService.getRiderEvaluates(riderId, type, page)); | |
| 57 | + @RequestParam(defaultValue = "1") int page, | |
| 58 | + HttpServletRequest request) { | |
| 59 | + return Result.success(evaluateService.getRiderEvaluates(riderId, type, page, resolveScopedCityId(request))); | |
| 60 | + } | |
| 61 | + | |
| 62 | + private Long resolveScopedCityId(HttpServletRequest request) { | |
| 63 | + return "substation".equals(request.getAttribute("role")) | |
| 64 | + ? (Long) request.getAttribute("cityId") | |
| 65 | + : null; | |
| 57 | 66 | } |
| 58 | 67 | } | ... | ... |
src/main/java/com/diligrp/rider/controller/AdminRiderController.java
| ... | ... | @@ -55,8 +55,10 @@ public class AdminRiderController { |
| 55 | 55 | |
| 56 | 56 | /** 审核骑手:status=0拒绝 1通过 */ |
| 57 | 57 | @PostMapping("/setStatus") |
| 58 | - public Result<Void> setStatus(@RequestParam Long riderId, @RequestParam int status) { | |
| 59 | - adminRiderService.setStatus(riderId, status); | |
| 58 | + public Result<Void> setStatus(@RequestParam Long riderId, | |
| 59 | + @RequestParam int status, | |
| 60 | + HttpServletRequest request) { | |
| 61 | + adminRiderService.setStatus(riderId, status, resolveScopedCityId(request)); | |
| 60 | 62 | return Result.success(); |
| 61 | 63 | } |
| 62 | 64 | |
| ... | ... | @@ -74,29 +76,37 @@ public class AdminRiderController { |
| 74 | 76 | |
| 75 | 77 | /** 启用/禁用骑手账号:status=0禁用 1启用 */ |
| 76 | 78 | @PostMapping("/setEnableStatus") |
| 77 | - public Result<Void> setEnableStatus(@RequestParam Long riderId, @RequestParam int status) { | |
| 78 | - adminRiderService.setEnableStatus(riderId, status); | |
| 79 | + public Result<Void> setEnableStatus(@RequestParam Long riderId, | |
| 80 | + @RequestParam int status, | |
| 81 | + HttpServletRequest request) { | |
| 82 | + adminRiderService.setEnableStatus(riderId, status, resolveScopedCityId(request)); | |
| 79 | 83 | return Result.success(); |
| 80 | 84 | } |
| 81 | 85 | |
| 82 | 86 | /** 切换骑手类型:type=1兼职 2全职 */ |
| 83 | 87 | @PostMapping("/setType") |
| 84 | - public Result<Void> setType(@RequestParam Long riderId, @RequestParam int type) { | |
| 85 | - adminRiderService.setType(riderId, type); | |
| 88 | + public Result<Void> setType(@RequestParam Long riderId, | |
| 89 | + @RequestParam int type, | |
| 90 | + HttpServletRequest request) { | |
| 91 | + adminRiderService.setType(riderId, type, resolveScopedCityId(request)); | |
| 86 | 92 | return Result.success(); |
| 87 | 93 | } |
| 88 | 94 | |
| 89 | 95 | /** 指派骑手接单 */ |
| 90 | 96 | @PostMapping("/order/designate") |
| 91 | - public Result<Void> designate(@RequestParam Long orderId, @RequestParam Long riderId) { | |
| 92 | - adminRiderService.designate(orderId, riderId); | |
| 97 | + public Result<Void> designate(@RequestParam Long orderId, | |
| 98 | + @RequestParam Long riderId, | |
| 99 | + HttpServletRequest request) { | |
| 100 | + adminRiderService.designate(orderId, riderId, resolveScopedCityId(request)); | |
| 93 | 101 | return Result.success(); |
| 94 | 102 | } |
| 95 | 103 | |
| 96 | 104 | /** 处理转单申请:trans=1通过 3拒绝 */ |
| 97 | 105 | @PostMapping("/order/setTrans") |
| 98 | - public Result<Void> setTrans(@RequestParam Long orderId, @RequestParam int trans) { | |
| 99 | - adminRiderService.setTrans(orderId, trans); | |
| 106 | + public Result<Void> setTrans(@RequestParam Long orderId, | |
| 107 | + @RequestParam int trans, | |
| 108 | + HttpServletRequest request) { | |
| 109 | + adminRiderService.setTrans(orderId, trans, resolveScopedCityId(request)); | |
| 100 | 110 | return Result.success(); |
| 101 | 111 | } |
| 102 | 112 | |
| ... | ... | @@ -145,10 +155,15 @@ public class AdminRiderController { |
| 145 | 155 | @RequestParam(required = false) String appKey, |
| 146 | 156 | @RequestParam(required = false) String outOrderNo, |
| 147 | 157 | @RequestParam(required = false) Integer status, |
| 148 | - @RequestParam(defaultValue = "1") int page) { | |
| 158 | + @RequestParam(defaultValue = "1") int page, | |
| 159 | + HttpServletRequest request) { | |
| 149 | 160 | LambdaQueryWrapper<Orders> wrapper = new LambdaQueryWrapper<Orders>() |
| 150 | 161 | .eq(Orders::getIsDel, 0) |
| 151 | 162 | .orderByDesc(Orders::getId); |
| 163 | + Long cityId = resolveScopedCityId(request); | |
| 164 | + if (cityId != null) { | |
| 165 | + wrapper.eq(Orders::getCityId, cityId); | |
| 166 | + } | |
| 152 | 167 | if (appKey != null && !appKey.isBlank()) wrapper.eq(Orders::getAppKey, appKey); |
| 153 | 168 | if (outOrderNo != null && !outOrderNo.isBlank()) wrapper.like(Orders::getOutOrderNo, outOrderNo); |
| 154 | 169 | if (status != null) wrapper.eq(Orders::getStatus, status); |
| ... | ... | @@ -156,4 +171,10 @@ public class AdminRiderController { |
| 156 | 171 | wrapper.last("LIMIT " + offset + ",20"); |
| 157 | 172 | return Result.success(ordersMapper.selectList(wrapper)); |
| 158 | 173 | } |
| 174 | + | |
| 175 | + private Long resolveScopedCityId(HttpServletRequest request) { | |
| 176 | + return "substation".equals(request.getAttribute("role")) | |
| 177 | + ? (Long) request.getAttribute("cityId") | |
| 178 | + : null; | |
| 179 | + } | |
| 159 | 180 | } | ... | ... |
src/main/java/com/diligrp/rider/controller/PlatformAdminUserController.java
0 → 100644
| 1 | +package com.diligrp.rider.controller; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.common.result.Result; | |
| 4 | +import com.diligrp.rider.dto.ChangePasswordDTO; | |
| 5 | +import com.diligrp.rider.entity.AdminUser; | |
| 6 | +import com.diligrp.rider.service.AdminUserManageService; | |
| 7 | +import jakarta.validation.Valid; | |
| 8 | +import lombok.RequiredArgsConstructor; | |
| 9 | +import org.springframework.web.bind.annotation.*; | |
| 10 | + | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@RestController | |
| 14 | +@RequestMapping("/api/platform/admin-user") | |
| 15 | +@RequiredArgsConstructor | |
| 16 | +public class PlatformAdminUserController { | |
| 17 | + | |
| 18 | + private final AdminUserManageService adminUserManageService; | |
| 19 | + | |
| 20 | + @GetMapping("/list") | |
| 21 | + public Result<List<AdminUser>> list(@RequestParam(required = false) String keyword) { | |
| 22 | + return Result.success(adminUserManageService.list(keyword)); | |
| 23 | + } | |
| 24 | + | |
| 25 | + @PostMapping("/add") | |
| 26 | + public Result<Void> add(@RequestBody AdminUser adminUser) { | |
| 27 | + adminUserManageService.add(adminUser); | |
| 28 | + return Result.success(); | |
| 29 | + } | |
| 30 | + | |
| 31 | + @PutMapping("/edit") | |
| 32 | + public Result<Void> edit(@RequestBody AdminUser adminUser) { | |
| 33 | + adminUserManageService.edit(adminUser); | |
| 34 | + return Result.success(); | |
| 35 | + } | |
| 36 | + | |
| 37 | + @PostMapping("/ban") | |
| 38 | + public Result<Void> ban(@RequestParam Long id) { | |
| 39 | + adminUserManageService.ban(id); | |
| 40 | + return Result.success(); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @PostMapping("/cancelBan") | |
| 44 | + public Result<Void> cancelBan(@RequestParam Long id) { | |
| 45 | + adminUserManageService.cancelBan(id); | |
| 46 | + return Result.success(); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @DeleteMapping("/del") | |
| 50 | + public Result<Void> del(@RequestParam Long id) { | |
| 51 | + adminUserManageService.del(id); | |
| 52 | + return Result.success(); | |
| 53 | + } | |
| 54 | + | |
| 55 | + @PostMapping("/changePassword") | |
| 56 | + public Result<Void> changePassword(@RequestParam Long id, | |
| 57 | + @Valid @RequestBody ChangePasswordDTO dto) { | |
| 58 | + adminUserManageService.changePassword(id, dto.getOldPassword(), dto.getNewPassword()); | |
| 59 | + return Result.success(); | |
| 60 | + } | |
| 61 | +} | ... | ... |
src/main/java/com/diligrp/rider/controller/PlatformSubstationController.java
| ... | ... | @@ -70,10 +70,9 @@ public class PlatformSubstationController { |
| 70 | 70 | */ |
| 71 | 71 | @PostMapping("/changePassword") |
| 72 | 72 | public Result<Void> changePassword( |
| 73 | - @Valid @RequestBody ChangePasswordDTO dto, | |
| 74 | - jakarta.servlet.http.HttpServletRequest request) { | |
| 75 | - Long adminId = (Long) request.getAttribute("adminId"); | |
| 76 | - substationService.changePassword(adminId, dto.getOldPassword(), dto.getNewPassword()); | |
| 73 | + @RequestParam Long id, | |
| 74 | + @Valid @RequestBody ChangePasswordDTO dto) { | |
| 75 | + substationService.changePassword(id, dto.getOldPassword(), dto.getNewPassword()); | |
| 77 | 76 | return Result.success(); |
| 78 | 77 | } |
| 79 | 78 | } | ... | ... |
src/main/java/com/diligrp/rider/controller/PlatformSystemMenuController.java
0 → 100644
| 1 | +package com.diligrp.rider.controller; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.common.result.Result; | |
| 4 | +import com.diligrp.rider.dto.AdminMenuSaveDTO; | |
| 5 | +import com.diligrp.rider.service.SystemMenuService; | |
| 6 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 7 | +import jakarta.validation.Valid; | |
| 8 | +import lombok.RequiredArgsConstructor; | |
| 9 | +import org.springframework.web.bind.annotation.*; | |
| 10 | + | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 13 | +@RestController | |
| 14 | +@RequestMapping("/api/platform/system/menu") | |
| 15 | +@RequiredArgsConstructor | |
| 16 | +public class PlatformSystemMenuController { | |
| 17 | + | |
| 18 | + private final SystemMenuService systemMenuService; | |
| 19 | + | |
| 20 | + @GetMapping("/tree") | |
| 21 | + public Result<List<AdminRoleMenuTreeVO>> tree() { | |
| 22 | + return Result.success(systemMenuService.tree()); | |
| 23 | + } | |
| 24 | + | |
| 25 | + @PostMapping("/add") | |
| 26 | + public Result<Void> add(@Valid @RequestBody AdminMenuSaveDTO dto) { | |
| 27 | + systemMenuService.add(dto); | |
| 28 | + return Result.success(); | |
| 29 | + } | |
| 30 | + | |
| 31 | + @PutMapping("/edit") | |
| 32 | + public Result<Void> edit(@Valid @RequestBody AdminMenuSaveDTO dto) { | |
| 33 | + systemMenuService.edit(dto); | |
| 34 | + return Result.success(); | |
| 35 | + } | |
| 36 | + | |
| 37 | + @DeleteMapping("/del") | |
| 38 | + public Result<Void> delete(@RequestParam Long id) { | |
| 39 | + systemMenuService.delete(id); | |
| 40 | + return Result.success(); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @PostMapping("/setVisible") | |
| 44 | + public Result<Void> setVisible(@RequestParam Long id, @RequestParam int visible) { | |
| 45 | + systemMenuService.setVisible(id, visible); | |
| 46 | + return Result.success(); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PostMapping("/setStatus") | |
| 50 | + public Result<Void> setStatus(@RequestParam Long id, @RequestParam int status) { | |
| 51 | + systemMenuService.setStatus(id, status); | |
| 52 | + return Result.success(); | |
| 53 | + } | |
| 54 | +} | ... | ... |
src/main/java/com/diligrp/rider/controller/PlatformSystemRoleController.java
0 → 100644
| 1 | +package com.diligrp.rider.controller; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.common.result.Result; | |
| 4 | +import com.diligrp.rider.dto.AdminRoleMenuAssignDTO; | |
| 5 | +import com.diligrp.rider.service.SystemRoleMenuService; | |
| 6 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 7 | +import com.diligrp.rider.vo.AdminRoleVO; | |
| 8 | +import jakarta.validation.Valid; | |
| 9 | +import lombok.RequiredArgsConstructor; | |
| 10 | +import org.springframework.web.bind.annotation.*; | |
| 11 | + | |
| 12 | +import java.util.List; | |
| 13 | + | |
| 14 | +@RestController | |
| 15 | +@RequestMapping("/api/platform/system/role") | |
| 16 | +@RequiredArgsConstructor | |
| 17 | +public class PlatformSystemRoleController { | |
| 18 | + | |
| 19 | + private final SystemRoleMenuService systemRoleMenuService; | |
| 20 | + | |
| 21 | + @GetMapping("/list") | |
| 22 | + public Result<List<AdminRoleVO>> list() { | |
| 23 | + return Result.success(systemRoleMenuService.listRoles()); | |
| 24 | + } | |
| 25 | + | |
| 26 | + @GetMapping("/{roleId}/menu-tree") | |
| 27 | + public Result<List<AdminRoleMenuTreeVO>> menuTree(@PathVariable Long roleId) { | |
| 28 | + return Result.success(systemRoleMenuService.getRoleMenuTree(roleId)); | |
| 29 | + } | |
| 30 | + | |
| 31 | + @PostMapping("/{roleId}/menus") | |
| 32 | + public Result<Void> assignMenus(@PathVariable Long roleId, | |
| 33 | + @Valid @RequestBody AdminRoleMenuAssignDTO dto) { | |
| 34 | + systemRoleMenuService.assignMenus(roleId, dto); | |
| 35 | + return Result.success(); | |
| 36 | + } | |
| 37 | +} | ... | ... |
src/main/java/com/diligrp/rider/controller/RiderExtController.java
| ... | ... | @@ -104,7 +104,7 @@ public class RiderExtController { |
| 104 | 104 | @RequestParam(defaultValue = "1") int page, |
| 105 | 105 | HttpServletRequest request) { |
| 106 | 106 | Long riderId = (Long) request.getAttribute("riderId"); |
| 107 | - return Result.success(evaluateService.getRiderEvaluates(riderId, type, page)); | |
| 107 | + return Result.success(evaluateService.getRiderEvaluates(riderId, type, page, null)); | |
| 108 | 108 | } |
| 109 | 109 | |
| 110 | 110 | /** 本月好评数 */ | ... | ... |
src/main/java/com/diligrp/rider/dto/AdminMenuSaveDTO.java
0 → 100644
| 1 | +package com.diligrp.rider.dto; | |
| 2 | + | |
| 3 | +import jakarta.validation.constraints.NotBlank; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +@Data | |
| 8 | +public class AdminMenuSaveDTO { | |
| 9 | + private Long id; | |
| 10 | + | |
| 11 | + @NotBlank(message = "菜单编码不能为空") | |
| 12 | + private String code; | |
| 13 | + | |
| 14 | + @NotBlank(message = "菜单名称不能为空") | |
| 15 | + private String name; | |
| 16 | + | |
| 17 | + @NotBlank(message = "菜单类型不能为空") | |
| 18 | + private String type; | |
| 19 | + | |
| 20 | + private String path; | |
| 21 | + | |
| 22 | + private String icon; | |
| 23 | + | |
| 24 | + @NotNull(message = "父级菜单不能为空") | |
| 25 | + private Long parentId; | |
| 26 | + | |
| 27 | + @NotBlank(message = "菜单范围不能为空") | |
| 28 | + private String menuScope; | |
| 29 | + | |
| 30 | + private Integer listOrder = 0; | |
| 31 | + | |
| 32 | + private Integer visible = 1; | |
| 33 | + | |
| 34 | + private Integer status = 1; | |
| 35 | +} | ... | ... |
src/main/java/com/diligrp/rider/dto/AdminRoleMenuAssignDTO.java
0 → 100644
src/main/java/com/diligrp/rider/entity/AdminUser.java
src/main/java/com/diligrp/rider/entity/Substation.java
src/main/java/com/diligrp/rider/entity/SysMenu.java
0 → 100644
| 1 | +package com.diligrp.rider.entity; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.IdType; | |
| 4 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +@Data | |
| 9 | +@TableName("sys_menu") | |
| 10 | +public class SysMenu { | |
| 11 | + | |
| 12 | + @TableId(type = IdType.AUTO) | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + private String code; | |
| 16 | + | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + private String type; | |
| 20 | + | |
| 21 | + private String path; | |
| 22 | + | |
| 23 | + private String icon; | |
| 24 | + | |
| 25 | + private Long parentId; | |
| 26 | + | |
| 27 | + private String menuScope; | |
| 28 | + | |
| 29 | + private Integer listOrder; | |
| 30 | + | |
| 31 | + private Integer visible; | |
| 32 | + | |
| 33 | + private Integer status; | |
| 34 | + | |
| 35 | + private Long createTime; | |
| 36 | +} | ... | ... |
src/main/java/com/diligrp/rider/entity/SysRole.java
0 → 100644
| 1 | +package com.diligrp.rider.entity; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.IdType; | |
| 4 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +@Data | |
| 9 | +@TableName("sys_role") | |
| 10 | +public class SysRole { | |
| 11 | + | |
| 12 | + @TableId(type = IdType.AUTO) | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + private String code; | |
| 16 | + | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + private String roleScope; | |
| 20 | + | |
| 21 | + private Integer status; | |
| 22 | + | |
| 23 | + private Long createTime; | |
| 24 | +} | ... | ... |
src/main/java/com/diligrp/rider/entity/SysRoleMenu.java
0 → 100644
| 1 | +package com.diligrp.rider.entity; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.IdType; | |
| 4 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +@Data | |
| 9 | +@TableName("sys_role_menu") | |
| 10 | +public class SysRoleMenu { | |
| 11 | + | |
| 12 | + @TableId(type = IdType.AUTO) | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + private Long roleId; | |
| 16 | + | |
| 17 | + private Long menuId; | |
| 18 | + | |
| 19 | + private Long createTime; | |
| 20 | +} | ... | ... |
src/main/java/com/diligrp/rider/mapper/SysMenuMapper.java
0 → 100644
src/main/java/com/diligrp/rider/mapper/SysRoleMapper.java
0 → 100644
src/main/java/com/diligrp/rider/mapper/SysRoleMenuMapper.java
0 → 100644
src/main/java/com/diligrp/rider/service/AdminMenuService.java
0 → 100644
| 1 | +package com.diligrp.rider.service; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.entity.SysRole; | |
| 4 | +import com.diligrp.rider.vo.AdminMenuVO; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +public interface AdminMenuService { | |
| 9 | + SysRole resolveRole(Long roleId, String roleType); | |
| 10 | + | |
| 11 | + List<AdminMenuVO> getMenus(SysRole role, String roleType); | |
| 12 | + | |
| 13 | + String resolveHomePath(List<AdminMenuVO> menus); | |
| 14 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/AdminRiderService.java
| ... | ... | @@ -14,15 +14,15 @@ public interface AdminRiderService { |
| 14 | 14 | /** 指派候选骑手列表 */ |
| 15 | 15 | List<Rider> designateCandidates(Long orderId, Long cityId); |
| 16 | 16 | /** 审核骑手(通过/拒绝) */ |
| 17 | - void setStatus(Long riderId, int status); | |
| 17 | + void setStatus(Long riderId, int status, Long cityId); | |
| 18 | 18 | /** 设置骑手等级,为空则使用默认等级 */ |
| 19 | 19 | void setLevel(Long riderId, Long levelId, Long cityId); |
| 20 | 20 | /** 启用/禁用骑手账号 */ |
| 21 | - void setEnableStatus(Long riderId, int status); | |
| 21 | + void setEnableStatus(Long riderId, int status, Long cityId); | |
| 22 | 22 | /** 切换全职/兼职 */ |
| 23 | - void setType(Long riderId, int type); | |
| 23 | + void setType(Long riderId, int type, Long cityId); | |
| 24 | 24 | /** 指派骑手接单 */ |
| 25 | - void designate(Long orderId, Long riderId); | |
| 25 | + void designate(Long orderId, Long riderId, Long cityId); | |
| 26 | 26 | /** 处理转单申请(1=通过 3=拒绝) */ |
| 27 | - void setTrans(Long orderId, int trans); | |
| 27 | + void setTrans(Long orderId, int trans, Long cityId); | |
| 28 | 28 | } | ... | ... |
src/main/java/com/diligrp/rider/service/AdminUserManageService.java
0 → 100644
| 1 | +package com.diligrp.rider.service; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.entity.AdminUser; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +public interface AdminUserManageService { | |
| 8 | + List<AdminUser> list(String keyword); | |
| 9 | + | |
| 10 | + void add(AdminUser adminUser); | |
| 11 | + | |
| 12 | + void edit(AdminUser adminUser); | |
| 13 | + | |
| 14 | + void ban(Long id); | |
| 15 | + | |
| 16 | + void cancelBan(Long id); | |
| 17 | + | |
| 18 | + void del(Long id); | |
| 19 | + | |
| 20 | + void changePassword(Long id, String oldPassword, String newPassword); | |
| 21 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/MenuBootstrapService.java
0 → 100644
src/main/java/com/diligrp/rider/service/RefundService.java
| ... | ... | @@ -11,7 +11,7 @@ public interface RefundService { |
| 11 | 11 | /** 用户/骑手申请退款 */ |
| 12 | 12 | void applyRefund(Long orderId, Long uid, int role, Long reasonId, String reason); |
| 13 | 13 | /** 分站审核退款:status=1通过 2拒绝 */ |
| 14 | - void handleRefund(Long recordId, int status, String remark); | |
| 14 | + void handleRefund(Long recordId, int status, String remark, Long cityId); | |
| 15 | 15 | /** 查询订单退款记录 */ |
| 16 | - OrderRefundRecord getByOrderId(Long orderId); | |
| 16 | + OrderRefundRecord getByOrderId(Long orderId, Long cityId); | |
| 17 | 17 | } | ... | ... |
src/main/java/com/diligrp/rider/service/RiderEvaluateService.java
| ... | ... | @@ -8,7 +8,7 @@ public interface RiderEvaluateService { |
| 8 | 8 | /** 用户对骑手评价(订单完成后) */ |
| 9 | 9 | void evaluate(Long uid, Long orderId, int star, String content); |
| 10 | 10 | /** 骑手评价列表 */ |
| 11 | - List<RiderEvaluate> getRiderEvaluates(Long riderId, Integer type, int page); | |
| 11 | + List<RiderEvaluate> getRiderEvaluates(Long riderId, Integer type, int page, Long cityId); | |
| 12 | 12 | /** 本月好评数 */ |
| 13 | 13 | int getMonthGoodCount(Long riderId); |
| 14 | 14 | } | ... | ... |
src/main/java/com/diligrp/rider/service/RoleScopeGuardService.java
0 → 100644
src/main/java/com/diligrp/rider/service/SubstationService.java
| ... | ... | @@ -19,6 +19,6 @@ public interface SubstationService { |
| 19 | 19 | void del(Long id); |
| 20 | 20 | /** 根据城市ID获取分站管理员 */ |
| 21 | 21 | Substation getByCityId(Long cityId); |
| 22 | - /** 分站管理员修改自己的密码 */ | |
| 22 | + /** 重置目标分站管理员密码 */ | |
| 23 | 23 | void changePassword(Long substationId, String oldPassword, String newPassword); |
| 24 | 24 | } | ... | ... |
src/main/java/com/diligrp/rider/service/SystemMenuService.java
0 → 100644
| 1 | +package com.diligrp.rider.service; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.dto.AdminMenuSaveDTO; | |
| 4 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 5 | + | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +public interface SystemMenuService { | |
| 9 | + List<AdminRoleMenuTreeVO> tree(); | |
| 10 | + | |
| 11 | + void add(AdminMenuSaveDTO dto); | |
| 12 | + | |
| 13 | + void edit(AdminMenuSaveDTO dto); | |
| 14 | + | |
| 15 | + void delete(Long id); | |
| 16 | + | |
| 17 | + void setVisible(Long id, int visible); | |
| 18 | + | |
| 19 | + void setStatus(Long id, int status); | |
| 20 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/SystemRoleMenuService.java
0 → 100644
| 1 | +package com.diligrp.rider.service; | |
| 2 | + | |
| 3 | +import com.diligrp.rider.dto.AdminRoleMenuAssignDTO; | |
| 4 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 5 | +import com.diligrp.rider.vo.AdminRoleVO; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +public interface SystemRoleMenuService { | |
| 10 | + List<AdminRoleVO> listRoles(); | |
| 11 | + | |
| 12 | + List<AdminRoleMenuTreeVO> getRoleMenuTree(Long roleId); | |
| 13 | + | |
| 14 | + void assignMenus(Long roleId, AdminRoleMenuAssignDTO dto); | |
| 15 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/AdminAuthServiceImpl.java
| ... | ... | @@ -5,22 +5,34 @@ import com.diligrp.rider.common.exception.BizException; |
| 5 | 5 | import com.diligrp.rider.config.JwtUtil; |
| 6 | 6 | import com.diligrp.rider.dto.AdminLoginDTO; |
| 7 | 7 | import com.diligrp.rider.entity.AdminUser; |
| 8 | +import com.diligrp.rider.entity.City; | |
| 8 | 9 | import com.diligrp.rider.entity.Substation; |
| 10 | +import com.diligrp.rider.entity.SysRole; | |
| 9 | 11 | import com.diligrp.rider.mapper.AdminUserMapper; |
| 12 | +import com.diligrp.rider.mapper.CityMapper; | |
| 10 | 13 | import com.diligrp.rider.mapper.SubstationMapper; |
| 14 | +import com.diligrp.rider.service.AdminMenuService; | |
| 15 | +import com.diligrp.rider.vo.AdminLoginUserVO; | |
| 11 | 16 | import com.diligrp.rider.vo.AdminLoginVO; |
| 17 | +import com.diligrp.rider.vo.AdminMenuVO; | |
| 12 | 18 | import lombok.RequiredArgsConstructor; |
| 13 | 19 | import org.springframework.stereotype.Service; |
| 14 | 20 | import org.springframework.util.DigestUtils; |
| 15 | 21 | |
| 16 | 22 | import java.nio.charset.StandardCharsets; |
| 23 | +import java.util.List; | |
| 17 | 24 | |
| 18 | 25 | @Service |
| 19 | 26 | @RequiredArgsConstructor |
| 20 | 27 | public class AdminAuthServiceImpl { |
| 21 | 28 | |
| 29 | + private static final String ROLE_TYPE_ADMIN = "admin"; | |
| 30 | + private static final String ROLE_TYPE_SUBSTATION = "substation"; | |
| 31 | + | |
| 22 | 32 | private final AdminUserMapper adminUserMapper; |
| 23 | 33 | private final SubstationMapper substationMapper; |
| 34 | + private final CityMapper cityMapper; | |
| 35 | + private final AdminMenuService adminMenuService; | |
| 24 | 36 | private final JwtUtil jwtUtil; |
| 25 | 37 | |
| 26 | 38 | /** |
| ... | ... | @@ -29,7 +41,7 @@ public class AdminAuthServiceImpl { |
| 29 | 41 | * role=substation:分站管理员登录(substation表) |
| 30 | 42 | */ |
| 31 | 43 | public AdminLoginVO login(AdminLoginDTO dto) { |
| 32 | - if ("admin".equals(dto.getRole())) { | |
| 44 | + if (ROLE_TYPE_ADMIN.equals(dto.getRole())) { | |
| 33 | 45 | return loginAdmin(dto.getAccount(), dto.getPass()); |
| 34 | 46 | } |
| 35 | 47 | return loginSubstation(dto.getAccount(), dto.getPass()); |
| ... | ... | @@ -42,12 +54,22 @@ public class AdminAuthServiceImpl { |
| 42 | 54 | if (!encryptPass(pass).equals(user.getUserPass())) throw new BizException("密码错误"); |
| 43 | 55 | if (user.getUserStatus() == null || user.getUserStatus() == 0) throw new BizException("账号已被禁用"); |
| 44 | 56 | |
| 57 | + SysRole role = adminMenuService.resolveRole(user.getRoleId(), ROLE_TYPE_ADMIN); | |
| 58 | + List<AdminMenuVO> menus = adminMenuService.getMenus(role, ROLE_TYPE_ADMIN); | |
| 59 | + | |
| 60 | + AdminLoginUserVO loginUser = new AdminLoginUserVO(); | |
| 61 | + loginUser.setId(user.getId()); | |
| 62 | + loginUser.setUserLogin(user.getUserLogin()); | |
| 63 | + loginUser.setUserNickname(user.getUserNickname()); | |
| 64 | + loginUser.setRole(ROLE_TYPE_ADMIN); | |
| 65 | + loginUser.setRoleType(ROLE_TYPE_ADMIN); | |
| 66 | + loginUser.setRoleCode(role.getCode()); | |
| 67 | + | |
| 45 | 68 | AdminLoginVO vo = new AdminLoginVO(); |
| 46 | - vo.setId(user.getId()); | |
| 47 | - vo.setUserLogin(user.getUserLogin()); | |
| 48 | - vo.setUserNickname(user.getUserNickname()); | |
| 49 | - vo.setRole("admin"); | |
| 50 | - vo.setToken(jwtUtil.generateAdminToken(user.getId(), "admin")); | |
| 69 | + vo.setToken(jwtUtil.generateAdminToken(user.getId(), ROLE_TYPE_ADMIN)); | |
| 70 | + vo.setUser(loginUser); | |
| 71 | + vo.setMenus(menus); | |
| 72 | + vo.setHomePath(adminMenuService.resolveHomePath(menus)); | |
| 51 | 73 | return vo; |
| 52 | 74 | } |
| 53 | 75 | |
| ... | ... | @@ -57,14 +79,27 @@ public class AdminAuthServiceImpl { |
| 57 | 79 | if (sub == null) throw new BizException("账号不存在"); |
| 58 | 80 | if (!encryptPass(pass).equals(sub.getUserPass())) throw new BizException("密码错误"); |
| 59 | 81 | if (sub.getUserStatus() == null || sub.getUserStatus() == 0) throw new BizException("账号已被禁用"); |
| 82 | + if (sub.getCityId() == null || sub.getCityId() < 1) throw new BizException("当前分站账号未绑定有效城市"); | |
| 83 | + | |
| 84 | + SysRole role = adminMenuService.resolveRole(sub.getRoleId(), ROLE_TYPE_SUBSTATION); | |
| 85 | + List<AdminMenuVO> menus = adminMenuService.getMenus(role, ROLE_TYPE_SUBSTATION); | |
| 86 | + City city = cityMapper.selectById(sub.getCityId()); | |
| 87 | + | |
| 88 | + AdminLoginUserVO loginUser = new AdminLoginUserVO(); | |
| 89 | + loginUser.setId(sub.getId()); | |
| 90 | + loginUser.setUserLogin(sub.getUserLogin()); | |
| 91 | + loginUser.setUserNickname(sub.getUserNickname()); | |
| 92 | + loginUser.setRole(ROLE_TYPE_SUBSTATION); | |
| 93 | + loginUser.setRoleType(ROLE_TYPE_SUBSTATION); | |
| 94 | + loginUser.setRoleCode(role.getCode()); | |
| 95 | + loginUser.setCityId(sub.getCityId()); | |
| 96 | + loginUser.setCityName(city != null ? city.getName() : null); | |
| 60 | 97 | |
| 61 | 98 | AdminLoginVO vo = new AdminLoginVO(); |
| 62 | - vo.setId(sub.getId()); | |
| 63 | - vo.setUserLogin(sub.getUserLogin()); | |
| 64 | - vo.setUserNickname(sub.getUserNickname()); | |
| 65 | - vo.setRole("substation"); | |
| 66 | - vo.setCityId(sub.getCityId()); | |
| 67 | - vo.setToken(jwtUtil.generateAdminToken(sub.getId(), "substation")); | |
| 99 | + vo.setToken(jwtUtil.generateAdminToken(sub.getId(), ROLE_TYPE_SUBSTATION)); | |
| 100 | + vo.setUser(loginUser); | |
| 101 | + vo.setMenus(menus); | |
| 102 | + vo.setHomePath(adminMenuService.resolveHomePath(menus)); | |
| 68 | 103 | return vo; |
| 69 | 104 | } |
| 70 | 105 | ... | ... |
src/main/java/com/diligrp/rider/service/impl/AdminMenuServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.diligrp.rider.common.enums.AdminRoleScopeEnum; | |
| 5 | +import com.diligrp.rider.common.enums.MenuScopeEnum; | |
| 6 | +import com.diligrp.rider.common.exception.BizException; | |
| 7 | +import com.diligrp.rider.entity.SysMenu; | |
| 8 | +import com.diligrp.rider.entity.SysRole; | |
| 9 | +import com.diligrp.rider.entity.SysRoleMenu; | |
| 10 | +import com.diligrp.rider.mapper.SysMenuMapper; | |
| 11 | +import com.diligrp.rider.mapper.SysRoleMapper; | |
| 12 | +import com.diligrp.rider.mapper.SysRoleMenuMapper; | |
| 13 | +import com.diligrp.rider.service.AdminMenuService; | |
| 14 | +import com.diligrp.rider.vo.AdminMenuVO; | |
| 15 | +import lombok.RequiredArgsConstructor; | |
| 16 | +import org.springframework.stereotype.Service; | |
| 17 | + | |
| 18 | +import java.util.ArrayList; | |
| 19 | +import java.util.LinkedHashMap; | |
| 20 | +import java.util.LinkedHashSet; | |
| 21 | +import java.util.List; | |
| 22 | +import java.util.Map; | |
| 23 | +import java.util.Set; | |
| 24 | + | |
| 25 | +@Service | |
| 26 | +@RequiredArgsConstructor | |
| 27 | +public class AdminMenuServiceImpl implements AdminMenuService { | |
| 28 | + | |
| 29 | + private static final String ROLE_TYPE_ADMIN = "admin"; | |
| 30 | + private static final String ROLE_TYPE_SUBSTATION = "substation"; | |
| 31 | + private static final String ROLE_CODE_PLATFORM = "platform_admin"; | |
| 32 | + private static final String ROLE_CODE_SUBSTATION = "substation_admin"; | |
| 33 | + | |
| 34 | + private final SysRoleMapper sysRoleMapper; | |
| 35 | + private final SysMenuMapper sysMenuMapper; | |
| 36 | + private final SysRoleMenuMapper sysRoleMenuMapper; | |
| 37 | + | |
| 38 | + @Override | |
| 39 | + public SysRole resolveRole(Long roleId, String roleType) { | |
| 40 | + String fallbackCode = ROLE_TYPE_ADMIN.equals(roleType) ? ROLE_CODE_PLATFORM : ROLE_CODE_SUBSTATION; | |
| 41 | + String requiredScope = ROLE_TYPE_ADMIN.equals(roleType) | |
| 42 | + ? AdminRoleScopeEnum.PLATFORM.name() | |
| 43 | + : AdminRoleScopeEnum.SUBSTATION.name(); | |
| 44 | + | |
| 45 | + SysRole role = null; | |
| 46 | + if (roleId != null && roleId > 0) { | |
| 47 | + role = sysRoleMapper.selectById(roleId); | |
| 48 | + } | |
| 49 | + if (role == null) { | |
| 50 | + role = sysRoleMapper.selectOne(new LambdaQueryWrapper<SysRole>() | |
| 51 | + .eq(SysRole::getCode, fallbackCode) | |
| 52 | + .eq(SysRole::getStatus, 1) | |
| 53 | + .last("LIMIT 1")); | |
| 54 | + } | |
| 55 | + if (role == null || role.getStatus() == null || role.getStatus() != 1) { | |
| 56 | + throw new BizException("当前账号未分配有效菜单角色"); | |
| 57 | + } | |
| 58 | + if (!requiredScope.equals(role.getRoleScope())) { | |
| 59 | + throw new BizException("当前账号角色归属不匹配"); | |
| 60 | + } | |
| 61 | + return role; | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public List<AdminMenuVO> getMenus(SysRole role, String roleType) { | |
| 66 | + List<SysRoleMenu> roleMenus = sysRoleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>() | |
| 67 | + .eq(SysRoleMenu::getRoleId, role.getId())); | |
| 68 | + if (roleMenus.isEmpty()) { | |
| 69 | + return List.of(); | |
| 70 | + } | |
| 71 | + | |
| 72 | + List<SysMenu> allMenus = sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>() | |
| 73 | + .eq(SysMenu::getStatus, 1) | |
| 74 | + .eq(SysMenu::getVisible, 1) | |
| 75 | + .orderByAsc(SysMenu::getListOrder) | |
| 76 | + .orderByAsc(SysMenu::getId)); | |
| 77 | + if (allMenus.isEmpty()) { | |
| 78 | + return List.of(); | |
| 79 | + } | |
| 80 | + | |
| 81 | + Map<Long, SysMenu> menuMap = new LinkedHashMap<>(); | |
| 82 | + for (SysMenu menu : allMenus) { | |
| 83 | + if (isScopeAllowed(menu.getMenuScope(), roleType)) { | |
| 84 | + menuMap.put(menu.getId(), menu); | |
| 85 | + } | |
| 86 | + } | |
| 87 | + if (menuMap.isEmpty()) { | |
| 88 | + return List.of(); | |
| 89 | + } | |
| 90 | + | |
| 91 | + Set<Long> includedIds = new LinkedHashSet<>(); | |
| 92 | + for (SysRoleMenu roleMenu : roleMenus) { | |
| 93 | + includeWithParents(roleMenu.getMenuId(), menuMap, includedIds); | |
| 94 | + } | |
| 95 | + | |
| 96 | + Map<Long, AdminMenuVO> voMap = new LinkedHashMap<>(); | |
| 97 | + List<AdminMenuVO> roots = new ArrayList<>(); | |
| 98 | + for (SysMenu menu : allMenus) { | |
| 99 | + if (!includedIds.contains(menu.getId())) { | |
| 100 | + continue; | |
| 101 | + } | |
| 102 | + AdminMenuVO vo = toVO(menu); | |
| 103 | + voMap.put(menu.getId(), vo); | |
| 104 | + Long parentId = menu.getParentId() == null ? 0L : menu.getParentId(); | |
| 105 | + if (parentId > 0 && voMap.containsKey(parentId)) { | |
| 106 | + voMap.get(parentId).getChildren().add(vo); | |
| 107 | + } else { | |
| 108 | + roots.add(vo); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + return roots; | |
| 112 | + } | |
| 113 | + | |
| 114 | + @Override | |
| 115 | + public String resolveHomePath(List<AdminMenuVO> menus) { | |
| 116 | + String homePath = findPathByCode(menus, "dashboard"); | |
| 117 | + if (homePath != null && !homePath.isBlank()) { | |
| 118 | + return homePath; | |
| 119 | + } | |
| 120 | + String firstPath = findFirstPath(menus); | |
| 121 | + return firstPath == null || firstPath.isBlank() ? "/dashboard" : firstPath; | |
| 122 | + } | |
| 123 | + | |
| 124 | + private void includeWithParents(Long menuId, Map<Long, SysMenu> menuMap, Set<Long> includedIds) { | |
| 125 | + SysMenu current = menuMap.get(menuId); | |
| 126 | + while (current != null && includedIds.add(current.getId())) { | |
| 127 | + Long parentId = current.getParentId(); | |
| 128 | + if (parentId == null || parentId < 1) { | |
| 129 | + break; | |
| 130 | + } | |
| 131 | + current = menuMap.get(parentId); | |
| 132 | + } | |
| 133 | + } | |
| 134 | + | |
| 135 | + private boolean isScopeAllowed(String menuScope, String roleType) { | |
| 136 | + if (MenuScopeEnum.BOTH.name().equals(menuScope)) { | |
| 137 | + return true; | |
| 138 | + } | |
| 139 | + if (ROLE_TYPE_ADMIN.equals(roleType)) { | |
| 140 | + return MenuScopeEnum.PLATFORM.name().equals(menuScope); | |
| 141 | + } | |
| 142 | + if (ROLE_TYPE_SUBSTATION.equals(roleType)) { | |
| 143 | + return MenuScopeEnum.SUBSTATION.name().equals(menuScope); | |
| 144 | + } | |
| 145 | + return false; | |
| 146 | + } | |
| 147 | + | |
| 148 | + private AdminMenuVO toVO(SysMenu menu) { | |
| 149 | + AdminMenuVO vo = new AdminMenuVO(); | |
| 150 | + vo.setId(menu.getId()); | |
| 151 | + vo.setCode(menu.getCode()); | |
| 152 | + vo.setName(menu.getName()); | |
| 153 | + vo.setType(menu.getType()); | |
| 154 | + vo.setPath(menu.getPath()); | |
| 155 | + vo.setIcon(menu.getIcon()); | |
| 156 | + return vo; | |
| 157 | + } | |
| 158 | + | |
| 159 | + private String findPathByCode(List<AdminMenuVO> menus, String code) { | |
| 160 | + for (AdminMenuVO menu : menus) { | |
| 161 | + if (code.equals(menu.getCode()) && menu.getPath() != null && !menu.getPath().isBlank()) { | |
| 162 | + return menu.getPath(); | |
| 163 | + } | |
| 164 | + String nested = findPathByCode(menu.getChildren(), code); | |
| 165 | + if (nested != null) { | |
| 166 | + return nested; | |
| 167 | + } | |
| 168 | + } | |
| 169 | + return null; | |
| 170 | + } | |
| 171 | + | |
| 172 | + private String findFirstPath(List<AdminMenuVO> menus) { | |
| 173 | + for (AdminMenuVO menu : menus) { | |
| 174 | + if (menu.getPath() != null && !menu.getPath().isBlank()) { | |
| 175 | + return menu.getPath(); | |
| 176 | + } | |
| 177 | + String nested = findFirstPath(menu.getChildren()); | |
| 178 | + if (nested != null) { | |
| 179 | + return nested; | |
| 180 | + } | |
| 181 | + } | |
| 182 | + return null; | |
| 183 | + } | |
| 184 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/AdminRiderServiceImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package com.diligrp.rider.service.impl; |
| 2 | 2 | |
| 3 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 5 | +import com.diligrp.rider.common.auth.AdminScopeGuard; | |
| 5 | 6 | import com.diligrp.rider.common.exception.BizException; |
| 6 | 7 | import com.diligrp.rider.dto.AdminRiderAddDTO; |
| 7 | 8 | import com.diligrp.rider.entity.Orders; |
| ... | ... | @@ -35,6 +36,7 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 35 | 36 | private final RiderLevelMapper riderLevelMapper; |
| 36 | 37 | private final OrdersMapper ordersMapper; |
| 37 | 38 | private final RiderBalanceMapper balanceMapper; |
| 39 | + private final AdminScopeGuard adminScopeGuard; | |
| 38 | 40 | |
| 39 | 41 | @Override |
| 40 | 42 | public void add(AdminRiderAddDTO dto, Long cityId) { |
| ... | ... | @@ -107,9 +109,10 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 107 | 109 | } |
| 108 | 110 | |
| 109 | 111 | @Override |
| 110 | - public void setStatus(Long riderId, int status) { | |
| 112 | + public void setStatus(Long riderId, int status, Long cityId) { | |
| 111 | 113 | Rider rider = riderMapper.selectById(riderId); |
| 112 | 114 | if (rider == null) throw new BizException("骑手不存在"); |
| 115 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 113 | 116 | riderMapper.update(null, new LambdaUpdateWrapper<Rider>() |
| 114 | 117 | .eq(Rider::getId, riderId) |
| 115 | 118 | .set(Rider::getUserStatus, status)); |
| ... | ... | @@ -135,9 +138,10 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 135 | 138 | } |
| 136 | 139 | |
| 137 | 140 | @Override |
| 138 | - public void setEnableStatus(Long riderId, int status) { | |
| 141 | + public void setEnableStatus(Long riderId, int status, Long cityId) { | |
| 139 | 142 | Rider rider = riderMapper.selectById(riderId); |
| 140 | 143 | if (rider == null) throw new BizException("骑手不存在"); |
| 144 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 141 | 145 | riderMapper.update(null, new LambdaUpdateWrapper<Rider>() |
| 142 | 146 | .eq(Rider::getId, riderId) |
| 143 | 147 | .set(Rider::getStatus, status)); |
| ... | ... | @@ -145,9 +149,10 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 145 | 149 | |
| 146 | 150 | @Override |
| 147 | 151 | @Transactional |
| 148 | - public void setType(Long riderId, int type) { | |
| 152 | + public void setType(Long riderId, int type, Long cityId) { | |
| 149 | 153 | Rider rider = riderMapper.selectById(riderId); |
| 150 | 154 | if (rider == null) throw new BizException("骑手不存在"); |
| 155 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 151 | 156 | if (type == 2) { |
| 152 | 157 | if (rider.getBalance() != null && rider.getBalance().compareTo(BigDecimal.ZERO) > 0) { |
| 153 | 158 | throw new BizException("变更为全职前要保证余额为0"); |
| ... | ... | @@ -164,9 +169,15 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 164 | 169 | |
| 165 | 170 | @Override |
| 166 | 171 | @Transactional |
| 167 | - public void designate(Long orderId, Long riderId) { | |
| 172 | + public void designate(Long orderId, Long riderId, Long cityId) { | |
| 168 | 173 | Orders order = ordersMapper.selectById(orderId); |
| 169 | 174 | if (order == null) throw new BizException("订单不存在"); |
| 175 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 176 | + Rider rider = riderMapper.selectById(riderId); | |
| 177 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 178 | + if (!order.getCityId().equals(rider.getCityId())) { | |
| 179 | + throw new BizException("骑手与订单城市不匹配"); | |
| 180 | + } | |
| 170 | 181 | if (order.getStatus() == 1) throw new BizException("订单未支付,无法指派"); |
| 171 | 182 | if (order.getStatus() == 10) throw new BizException("订单已取消,无法指派"); |
| 172 | 183 | if (order.getStatus() != 2) throw new BizException("订单已服务中,无法指派"); |
| ... | ... | @@ -191,9 +202,10 @@ public class AdminRiderServiceImpl implements AdminRiderService { |
| 191 | 202 | |
| 192 | 203 | @Override |
| 193 | 204 | @Transactional |
| 194 | - public void setTrans(Long orderId, int trans) { | |
| 205 | + public void setTrans(Long orderId, int trans, Long cityId) { | |
| 195 | 206 | Orders order = ordersMapper.selectById(orderId); |
| 196 | 207 | if (order == null) throw new BizException("订单不存在"); |
| 208 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 197 | 209 | if (order.getStatus() != 4) throw new BizException("订单状态错误,无法操作"); |
| 198 | 210 | if (order.getIsTrans() != 2) throw new BizException("订单未申请转单,无法操作"); |
| 199 | 211 | ... | ... |
src/main/java/com/diligrp/rider/service/impl/AdminUserManageServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 5 | +import com.diligrp.rider.common.enums.AdminRoleScopeEnum; | |
| 6 | +import com.diligrp.rider.common.exception.BizException; | |
| 7 | +import com.diligrp.rider.entity.AdminUser; | |
| 8 | +import com.diligrp.rider.mapper.AdminUserMapper; | |
| 9 | +import com.diligrp.rider.service.AdminUserManageService; | |
| 10 | +import com.diligrp.rider.service.RoleScopeGuardService; | |
| 11 | +import lombok.RequiredArgsConstructor; | |
| 12 | +import org.springframework.stereotype.Service; | |
| 13 | +import org.springframework.util.DigestUtils; | |
| 14 | + | |
| 15 | +import java.nio.charset.StandardCharsets; | |
| 16 | +import java.util.List; | |
| 17 | + | |
| 18 | +@Service | |
| 19 | +@RequiredArgsConstructor | |
| 20 | +public class AdminUserManageServiceImpl implements AdminUserManageService { | |
| 21 | + | |
| 22 | + private final AdminUserMapper adminUserMapper; | |
| 23 | + private final RoleScopeGuardService roleScopeGuardService; | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public List<AdminUser> list(String keyword) { | |
| 27 | + LambdaQueryWrapper<AdminUser> wrapper = new LambdaQueryWrapper<AdminUser>() | |
| 28 | + .orderByDesc(AdminUser::getId); | |
| 29 | + if (keyword != null && !keyword.isBlank()) { | |
| 30 | + wrapper.like(AdminUser::getUserLogin, keyword) | |
| 31 | + .or().like(AdminUser::getUserNickname, keyword); | |
| 32 | + } | |
| 33 | + return adminUserMapper.selectList(wrapper); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + public void add(AdminUser adminUser) { | |
| 38 | + Long exists = adminUserMapper.selectCount(new LambdaQueryWrapper<AdminUser>() | |
| 39 | + .eq(AdminUser::getUserLogin, adminUser.getUserLogin())); | |
| 40 | + if (exists > 0) { | |
| 41 | + throw new BizException("账号已存在,请更换"); | |
| 42 | + } | |
| 43 | + roleScopeGuardService.requireRole(adminUser.getRoleId(), AdminRoleScopeEnum.PLATFORM.name()); | |
| 44 | + adminUser.setUserPass(encryptPass(adminUser.getUserPass())); | |
| 45 | + adminUser.setUserStatus(1); | |
| 46 | + adminUser.setCreateTime(System.currentTimeMillis() / 1000); | |
| 47 | + adminUserMapper.insert(adminUser); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public void edit(AdminUser adminUser) { | |
| 52 | + AdminUser existing = adminUserMapper.selectById(adminUser.getId()); | |
| 53 | + if (existing == null) { | |
| 54 | + throw new BizException("平台账号不存在"); | |
| 55 | + } | |
| 56 | + roleScopeGuardService.requireRole(adminUser.getRoleId(), AdminRoleScopeEnum.PLATFORM.name()); | |
| 57 | + if (adminUser.getUserPass() == null || adminUser.getUserPass().isBlank()) { | |
| 58 | + adminUser.setUserPass(null); | |
| 59 | + } else { | |
| 60 | + adminUser.setUserPass(encryptPass(adminUser.getUserPass())); | |
| 61 | + } | |
| 62 | + adminUserMapper.updateById(adminUser); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @Override | |
| 66 | + public void ban(Long id) { | |
| 67 | + adminUserMapper.update(null, new LambdaUpdateWrapper<AdminUser>() | |
| 68 | + .eq(AdminUser::getId, id) | |
| 69 | + .set(AdminUser::getUserStatus, 0)); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Override | |
| 73 | + public void cancelBan(Long id) { | |
| 74 | + adminUserMapper.update(null, new LambdaUpdateWrapper<AdminUser>() | |
| 75 | + .eq(AdminUser::getId, id) | |
| 76 | + .set(AdminUser::getUserStatus, 1)); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @Override | |
| 80 | + public void del(Long id) { | |
| 81 | + adminUserMapper.deleteById(id); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Override | |
| 85 | + public void changePassword(Long id, String oldPassword, String newPassword) { | |
| 86 | + AdminUser user = adminUserMapper.selectById(id); | |
| 87 | + if (user == null) { | |
| 88 | + throw new BizException("平台账号不存在"); | |
| 89 | + } | |
| 90 | + if (!encryptPass(oldPassword).equals(user.getUserPass())) { | |
| 91 | + throw new BizException("原密码不正确"); | |
| 92 | + } | |
| 93 | + if (encryptPass(newPassword).equals(user.getUserPass())) { | |
| 94 | + throw new BizException("新密码不能与原密码相同"); | |
| 95 | + } | |
| 96 | + adminUserMapper.update(null, new LambdaUpdateWrapper<AdminUser>() | |
| 97 | + .eq(AdminUser::getId, id) | |
| 98 | + .set(AdminUser::getUserPass, encryptPass(newPassword))); | |
| 99 | + } | |
| 100 | + | |
| 101 | + private String encryptPass(String pass) { | |
| 102 | + return DigestUtils.md5DigestAsHex(pass.getBytes(StandardCharsets.UTF_8)); | |
| 103 | + } | |
| 104 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/MenuBootstrapServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.diligrp.rider.common.enums.AdminRoleScopeEnum; | |
| 5 | +import com.diligrp.rider.common.enums.MenuScopeEnum; | |
| 6 | +import com.diligrp.rider.entity.SysMenu; | |
| 7 | +import com.diligrp.rider.entity.SysRole; | |
| 8 | +import com.diligrp.rider.entity.SysRoleMenu; | |
| 9 | +import com.diligrp.rider.mapper.SysMenuMapper; | |
| 10 | +import com.diligrp.rider.mapper.SysRoleMapper; | |
| 11 | +import com.diligrp.rider.mapper.SysRoleMenuMapper; | |
| 12 | +import com.diligrp.rider.service.MenuBootstrapService; | |
| 13 | +import jakarta.annotation.PostConstruct; | |
| 14 | +import lombok.RequiredArgsConstructor; | |
| 15 | +import org.springframework.stereotype.Service; | |
| 16 | + | |
| 17 | +import java.util.ArrayList; | |
| 18 | +import java.util.LinkedHashMap; | |
| 19 | +import java.util.List; | |
| 20 | +import java.util.Map; | |
| 21 | + | |
| 22 | +@Service | |
| 23 | +@RequiredArgsConstructor | |
| 24 | +public class MenuBootstrapServiceImpl implements MenuBootstrapService { | |
| 25 | + | |
| 26 | + private final SysRoleMapper sysRoleMapper; | |
| 27 | + private final SysMenuMapper sysMenuMapper; | |
| 28 | + private final SysRoleMenuMapper sysRoleMenuMapper; | |
| 29 | + | |
| 30 | + @PostConstruct | |
| 31 | + @Override | |
| 32 | + public void initializeDefaults() { | |
| 33 | + SysRole platformRole = ensureRole("platform_admin", "平台管理员", AdminRoleScopeEnum.PLATFORM.name()); | |
| 34 | + SysRole substationRole = ensureRole("substation_admin", "分站管理员", AdminRoleScopeEnum.SUBSTATION.name()); | |
| 35 | + | |
| 36 | + List<SysMenu> seededMenus = seedMenus(); | |
| 37 | + bindMenus(platformRole, seededMenus, MenuScopeEnum.PLATFORM, MenuScopeEnum.BOTH); | |
| 38 | + bindMenus(substationRole, seededMenus, MenuScopeEnum.SUBSTATION, MenuScopeEnum.BOTH); | |
| 39 | + } | |
| 40 | + | |
| 41 | + private SysRole ensureRole(String code, String name, String scope) { | |
| 42 | + SysRole role = sysRoleMapper.selectOne(new LambdaQueryWrapper<SysRole>() | |
| 43 | + .eq(SysRole::getCode, code) | |
| 44 | + .last("LIMIT 1")); | |
| 45 | + if (role != null) { | |
| 46 | + return role; | |
| 47 | + } | |
| 48 | + role = new SysRole(); | |
| 49 | + role.setCode(code); | |
| 50 | + role.setName(name); | |
| 51 | + role.setRoleScope(scope); | |
| 52 | + role.setStatus(1); | |
| 53 | + role.setCreateTime(System.currentTimeMillis() / 1000); | |
| 54 | + sysRoleMapper.insert(role); | |
| 55 | + return role; | |
| 56 | + } | |
| 57 | + | |
| 58 | + private List<SysMenu> seedMenus() { | |
| 59 | + List<SysMenu> defaults = new ArrayList<>(); | |
| 60 | + defaults.add(menu("dashboard", "工作台", "MENU", "/dashboard", "HomeOutlined", 0L, MenuScopeEnum.BOTH, 10)); | |
| 61 | + defaults.add(menu("city.manage", "租户管理", "MENU", "/city", "GlobalOutlined", 0L, MenuScopeEnum.PLATFORM, 20)); | |
| 62 | + defaults.add(menu("substation.manage", "分站管理", "MENU", "/substation", "ApartmentOutlined", 0L, MenuScopeEnum.PLATFORM, 30)); | |
| 63 | + defaults.add(menu("merchant.root", "商家管理", "DIR", "", "ShopOutlined", 0L, MenuScopeEnum.PLATFORM, 40)); | |
| 64 | + defaults.add(menu("merchant.enter", "入驻申请", "MENU", "/merchant/enter", "", 0L, MenuScopeEnum.PLATFORM, 41)); | |
| 65 | + defaults.add(menu("merchant.store", "店铺管理", "MENU", "/merchant/store", "", 0L, MenuScopeEnum.PLATFORM, 42)); | |
| 66 | + defaults.add(menu("rider.list", "骑手管理", "MENU", "/rider", "UserOutlined", 0L, MenuScopeEnum.BOTH, 50)); | |
| 67 | + defaults.add(menu("rider.evaluate", "骑手评价", "MENU", "/rider/evaluate", "StarOutlined", 0L, MenuScopeEnum.BOTH, 60)); | |
| 68 | + defaults.add(menu("order.root", "订单管理", "DIR", "", "UnorderedListOutlined", 0L, MenuScopeEnum.BOTH, 70)); | |
| 69 | + defaults.add(menu("order.list", "订单列表", "MENU", "/order", "", 0L, MenuScopeEnum.BOTH, 71)); | |
| 70 | + defaults.add(menu("order.refund", "退款管理", "MENU", "/refund", "", 0L, MenuScopeEnum.BOTH, 72)); | |
| 71 | + defaults.add(menu("order.delivery", "配送订单", "MENU", "/delivery/order", "", 0L, MenuScopeEnum.BOTH, 73)); | |
| 72 | + defaults.add(menu("config.root", "配置中心", "DIR", "", "ControlOutlined", 0L, MenuScopeEnum.BOTH, 80)); | |
| 73 | + defaults.add(menu("fee.plan", "配送费配置", "MENU", "/config/fee-plan", "", 0L, MenuScopeEnum.BOTH, 81)); | |
| 74 | + defaults.add(menu("dispatch.rule", "调度配置", "MENU", "/dispatch/rule", "", 0L, MenuScopeEnum.BOTH, 82)); | |
| 75 | + defaults.add(menu("open.root", "开放平台", "DIR", "", "ApiOutlined", 0L, MenuScopeEnum.PLATFORM, 90)); | |
| 76 | + defaults.add(menu("open.app", "应用管理", "MENU", "/open", "", 0L, MenuScopeEnum.PLATFORM, 91)); | |
| 77 | + defaults.add(menu("open.mock_delivery", "模拟推单", "MENU", "/open/mock-delivery", "", 0L, MenuScopeEnum.PLATFORM, 92)); | |
| 78 | + defaults.add(menu("system.root", "系统管理", "DIR", "", "ControlOutlined", 0L, MenuScopeEnum.PLATFORM, 100)); | |
| 79 | + defaults.add(menu("system.menu", "菜单管理", "MENU", "/system/menu", "", 0L, MenuScopeEnum.PLATFORM, 101)); | |
| 80 | + defaults.add(menu("system.role_menu", "角色菜单", "MENU", "/system/role-menu", "", 0L, MenuScopeEnum.PLATFORM, 102)); | |
| 81 | + defaults.add(menu("admin.user", "平台账号", "MENU", "/admin-user", "", 0L, MenuScopeEnum.PLATFORM, 103)); | |
| 82 | + | |
| 83 | + Map<String, SysMenu> persisted = new LinkedHashMap<>(); | |
| 84 | + for (SysMenu menu : defaults) { | |
| 85 | + SysMenu existing = sysMenuMapper.selectOne(new LambdaQueryWrapper<SysMenu>() | |
| 86 | + .eq(SysMenu::getCode, menu.getCode()) | |
| 87 | + .last("LIMIT 1")); | |
| 88 | + if (existing == null) { | |
| 89 | + menu.setCreateTime(System.currentTimeMillis() / 1000); | |
| 90 | + sysMenuMapper.insert(menu); | |
| 91 | + existing = menu; | |
| 92 | + } | |
| 93 | + persisted.put(existing.getCode(), existing); | |
| 94 | + } | |
| 95 | + | |
| 96 | + persisted.get("merchant.enter").setParentId(persisted.get("merchant.root").getId()); | |
| 97 | + persisted.get("merchant.store").setParentId(persisted.get("merchant.root").getId()); | |
| 98 | + persisted.get("order.list").setParentId(persisted.get("order.root").getId()); | |
| 99 | + persisted.get("order.refund").setParentId(persisted.get("order.root").getId()); | |
| 100 | + persisted.get("order.delivery").setParentId(persisted.get("order.root").getId()); | |
| 101 | + persisted.get("fee.plan").setParentId(persisted.get("config.root").getId()); | |
| 102 | + persisted.get("dispatch.rule").setParentId(persisted.get("config.root").getId()); | |
| 103 | + persisted.get("open.app").setParentId(persisted.get("open.root").getId()); | |
| 104 | + persisted.get("open.mock_delivery").setParentId(persisted.get("open.root").getId()); | |
| 105 | + persisted.get("system.menu").setParentId(persisted.get("system.root").getId()); | |
| 106 | + persisted.get("system.role_menu").setParentId(persisted.get("system.root").getId()); | |
| 107 | + persisted.get("admin.user").setParentId(persisted.get("system.root").getId()); | |
| 108 | + | |
| 109 | + for (SysMenu menu : persisted.values()) { | |
| 110 | + sysMenuMapper.updateById(menu); | |
| 111 | + } | |
| 112 | + return new ArrayList<>(persisted.values()); | |
| 113 | + } | |
| 114 | + | |
| 115 | + private void bindMenus(SysRole role, List<SysMenu> menus, MenuScopeEnum primaryScope, MenuScopeEnum commonScope) { | |
| 116 | + for (SysMenu menu : menus) { | |
| 117 | + if (!primaryScope.name().equals(menu.getMenuScope()) && !commonScope.name().equals(menu.getMenuScope())) { | |
| 118 | + continue; | |
| 119 | + } | |
| 120 | + Long exists = sysRoleMenuMapper.selectCount(new LambdaQueryWrapper<SysRoleMenu>() | |
| 121 | + .eq(SysRoleMenu::getRoleId, role.getId()) | |
| 122 | + .eq(SysRoleMenu::getMenuId, menu.getId())); | |
| 123 | + if (exists > 0) { | |
| 124 | + continue; | |
| 125 | + } | |
| 126 | + SysRoleMenu roleMenu = new SysRoleMenu(); | |
| 127 | + roleMenu.setRoleId(role.getId()); | |
| 128 | + roleMenu.setMenuId(menu.getId()); | |
| 129 | + roleMenu.setCreateTime(System.currentTimeMillis() / 1000); | |
| 130 | + sysRoleMenuMapper.insert(roleMenu); | |
| 131 | + } | |
| 132 | + } | |
| 133 | + | |
| 134 | + private SysMenu menu(String code, String name, String type, String path, String icon, | |
| 135 | + Long parentId, MenuScopeEnum scope, int sort) { | |
| 136 | + SysMenu menu = new SysMenu(); | |
| 137 | + menu.setCode(code); | |
| 138 | + menu.setName(name); | |
| 139 | + menu.setType(type); | |
| 140 | + menu.setPath(path); | |
| 141 | + menu.setIcon(icon); | |
| 142 | + menu.setParentId(parentId); | |
| 143 | + menu.setMenuScope(scope.name()); | |
| 144 | + menu.setListOrder(sort); | |
| 145 | + menu.setVisible(1); | |
| 146 | + menu.setStatus(1); | |
| 147 | + return menu; | |
| 148 | + } | |
| 149 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/RefundServiceImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package com.diligrp.rider.service.impl; |
| 2 | 2 | |
| 3 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 5 | +import com.diligrp.rider.common.auth.AdminScopeGuard; | |
| 5 | 6 | import com.diligrp.rider.common.exception.BizException; |
| 6 | 7 | import com.diligrp.rider.entity.OrderRefundReason; |
| 7 | 8 | import com.diligrp.rider.entity.OrderRefundRecord; |
| ... | ... | @@ -33,6 +34,7 @@ public class RefundServiceImpl implements RefundService { |
| 33 | 34 | private final OrdersMapper ordersMapper; |
| 34 | 35 | private final WebhookService webhookService; |
| 35 | 36 | private final ObjectMapper objectMapper; |
| 37 | + private final AdminScopeGuard adminScopeGuard; | |
| 36 | 38 | |
| 37 | 39 | @Override |
| 38 | 40 | public List<OrderRefundReason> getReasons(int role) { |
| ... | ... | @@ -88,11 +90,16 @@ public class RefundServiceImpl implements RefundService { |
| 88 | 90 | |
| 89 | 91 | @Override |
| 90 | 92 | @Transactional |
| 91 | - public void handleRefund(Long recordId, int status, String remark) { | |
| 93 | + public void handleRefund(Long recordId, int status, String remark, Long cityId) { | |
| 92 | 94 | OrderRefundRecord record = recordMapper.selectById(recordId); |
| 93 | 95 | if (record == null) throw new BizException("退款记录不存在"); |
| 94 | 96 | if (record.getStatus() != 0) throw new BizException("该退款申请已处理"); |
| 95 | 97 | |
| 98 | + Orders order = ordersMapper.selectById(record.getOid()); | |
| 99 | + if (order != null) { | |
| 100 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 101 | + } | |
| 102 | + | |
| 96 | 103 | long now = System.currentTimeMillis() / 1000; |
| 97 | 104 | recordMapper.update(null, new LambdaUpdateWrapper<OrderRefundRecord>() |
| 98 | 105 | .eq(OrderRefundRecord::getId, recordId) |
| ... | ... | @@ -100,7 +107,7 @@ public class RefundServiceImpl implements RefundService { |
| 100 | 107 | .set(OrderRefundRecord::getRemark, remark) |
| 101 | 108 | .set(OrderRefundRecord::getHandleTime, now)); |
| 102 | 109 | |
| 103 | - Orders order = ordersMapper.selectById(record.getOid()); | |
| 110 | + order = ordersMapper.selectById(record.getOid()); | |
| 104 | 111 | if (order == null) return; |
| 105 | 112 | |
| 106 | 113 | if (status == 1) { |
| ... | ... | @@ -119,7 +126,12 @@ public class RefundServiceImpl implements RefundService { |
| 119 | 126 | } |
| 120 | 127 | |
| 121 | 128 | @Override |
| 122 | - public OrderRefundRecord getByOrderId(Long orderId) { | |
| 129 | + public OrderRefundRecord getByOrderId(Long orderId, Long cityId) { | |
| 130 | + Orders order = ordersMapper.selectById(orderId); | |
| 131 | + if (order == null) { | |
| 132 | + return null; | |
| 133 | + } | |
| 134 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 123 | 135 | return recordMapper.selectOne(new LambdaQueryWrapper<OrderRefundRecord>() |
| 124 | 136 | .eq(OrderRefundRecord::getOid, orderId) |
| 125 | 137 | .orderByDesc(OrderRefundRecord::getId) | ... | ... |
src/main/java/com/diligrp/rider/service/impl/RiderEvaluateServiceImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package com.diligrp.rider.service.impl; |
| 2 | 2 | |
| 3 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 5 | +import com.diligrp.rider.common.auth.AdminScopeGuard; | |
| 5 | 6 | import com.diligrp.rider.common.exception.BizException; |
| 6 | 7 | import com.diligrp.rider.entity.Orders; |
| 7 | 8 | import com.diligrp.rider.entity.RiderEvaluate; |
| ... | ... | @@ -25,6 +26,7 @@ public class RiderEvaluateServiceImpl implements RiderEvaluateService { |
| 25 | 26 | private final RiderEvaluateMapper evaluateMapper; |
| 26 | 27 | private final OrdersMapper ordersMapper; |
| 27 | 28 | private final RiderMapper riderMapper; |
| 29 | + private final AdminScopeGuard adminScopeGuard; | |
| 28 | 30 | |
| 29 | 31 | private static final int PAGE_SIZE = 20; |
| 30 | 32 | |
| ... | ... | @@ -68,7 +70,12 @@ public class RiderEvaluateServiceImpl implements RiderEvaluateService { |
| 68 | 70 | } |
| 69 | 71 | |
| 70 | 72 | @Override |
| 71 | - public List<RiderEvaluate> getRiderEvaluates(Long riderId, Integer type, int page) { | |
| 73 | + public List<RiderEvaluate> getRiderEvaluates(Long riderId, Integer type, int page, Long cityId) { | |
| 74 | + Rider rider = riderMapper.selectById(riderId); | |
| 75 | + if (rider == null) { | |
| 76 | + throw new BizException("骑手不存在"); | |
| 77 | + } | |
| 78 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 72 | 79 | LambdaQueryWrapper<RiderEvaluate> wrapper = new LambdaQueryWrapper<RiderEvaluate>() |
| 73 | 80 | .eq(RiderEvaluate::getRid, riderId) |
| 74 | 81 | .orderByDesc(RiderEvaluate::getId); | ... | ... |
src/main/java/com/diligrp/rider/service/impl/RoleScopeGuardServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.diligrp.rider.common.exception.BizException; | |
| 5 | +import com.diligrp.rider.entity.SysRole; | |
| 6 | +import com.diligrp.rider.mapper.SysRoleMapper; | |
| 7 | +import com.diligrp.rider.service.RoleScopeGuardService; | |
| 8 | +import lombok.RequiredArgsConstructor; | |
| 9 | +import org.springframework.stereotype.Service; | |
| 10 | + | |
| 11 | +@Service | |
| 12 | +@RequiredArgsConstructor | |
| 13 | +public class RoleScopeGuardServiceImpl implements RoleScopeGuardService { | |
| 14 | + | |
| 15 | + private final SysRoleMapper sysRoleMapper; | |
| 16 | + | |
| 17 | + @Override | |
| 18 | + public SysRole requireRole(Long roleId, String requiredScope) { | |
| 19 | + if (roleId == null || roleId < 1) { | |
| 20 | + throw new BizException("角色不能为空"); | |
| 21 | + } | |
| 22 | + SysRole role = sysRoleMapper.selectOne(new LambdaQueryWrapper<SysRole>() | |
| 23 | + .eq(SysRole::getId, roleId) | |
| 24 | + .eq(SysRole::getStatus, 1) | |
| 25 | + .last("LIMIT 1")); | |
| 26 | + if (role == null) { | |
| 27 | + throw new BizException("角色不存在或已禁用"); | |
| 28 | + } | |
| 29 | + if (!requiredScope.equals(role.getRoleScope())) { | |
| 30 | + throw new BizException("角色归属不匹配"); | |
| 31 | + } | |
| 32 | + return role; | |
| 33 | + } | |
| 34 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/SubstationServiceImpl.java
| ... | ... | @@ -2,9 +2,11 @@ package com.diligrp.rider.service.impl; |
| 2 | 2 | |
| 3 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 5 | +import com.diligrp.rider.common.enums.AdminRoleScopeEnum; | |
| 5 | 6 | import com.diligrp.rider.common.exception.BizException; |
| 6 | 7 | import com.diligrp.rider.entity.Substation; |
| 7 | 8 | import com.diligrp.rider.mapper.SubstationMapper; |
| 9 | +import com.diligrp.rider.service.RoleScopeGuardService; | |
| 8 | 10 | import com.diligrp.rider.service.SubstationService; |
| 9 | 11 | import lombok.RequiredArgsConstructor; |
| 10 | 12 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -18,6 +20,7 @@ import java.util.List; |
| 18 | 20 | public class SubstationServiceImpl implements SubstationService { |
| 19 | 21 | |
| 20 | 22 | private final SubstationMapper substationMapper; |
| 23 | + private final RoleScopeGuardService roleScopeGuardService; | |
| 21 | 24 | |
| 22 | 25 | @Override |
| 23 | 26 | public List<Substation> list(String keyword) { |
| ... | ... | @@ -41,6 +44,7 @@ public class SubstationServiceImpl implements SubstationService { |
| 41 | 44 | .eq(Substation::getUserLogin, substation.getUserLogin())); |
| 42 | 45 | if (loginExists > 0) throw new BizException("账号已存在,请更换"); |
| 43 | 46 | |
| 47 | + roleScopeGuardService.requireRole(substation.getRoleId(), AdminRoleScopeEnum.SUBSTATION.name()); | |
| 44 | 48 | substation.setUserPass(encryptPass(substation.getUserPass())); |
| 45 | 49 | substation.setUserStatus(1); |
| 46 | 50 | substation.setCreateTime(System.currentTimeMillis() / 1000); |
| ... | ... | @@ -51,6 +55,7 @@ public class SubstationServiceImpl implements SubstationService { |
| 51 | 55 | public void edit(Substation substation) { |
| 52 | 56 | Substation existing = substationMapper.selectById(substation.getId()); |
| 53 | 57 | if (existing == null) throw new BizException("分站管理员不存在"); |
| 58 | + roleScopeGuardService.requireRole(substation.getRoleId(), AdminRoleScopeEnum.SUBSTATION.name()); | |
| 54 | 59 | // 密码为空则不更新 |
| 55 | 60 | if (substation.getUserPass() == null || substation.getUserPass().isBlank()) { |
| 56 | 61 | substation.setUserPass(null); | ... | ... |
src/main/java/com/diligrp/rider/service/impl/SystemMenuServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 5 | +import com.diligrp.rider.common.enums.MenuScopeEnum; | |
| 6 | +import com.diligrp.rider.common.exception.BizException; | |
| 7 | +import com.diligrp.rider.dto.AdminMenuSaveDTO; | |
| 8 | +import com.diligrp.rider.entity.SysMenu; | |
| 9 | +import com.diligrp.rider.mapper.SysMenuMapper; | |
| 10 | +import com.diligrp.rider.service.SystemMenuService; | |
| 11 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 12 | +import lombok.RequiredArgsConstructor; | |
| 13 | +import org.springframework.stereotype.Service; | |
| 14 | +import org.springframework.transaction.annotation.Transactional; | |
| 15 | +import org.springframework.util.StringUtils; | |
| 16 | + | |
| 17 | +import java.util.ArrayList; | |
| 18 | +import java.util.LinkedHashMap; | |
| 19 | +import java.util.List; | |
| 20 | +import java.util.Map; | |
| 21 | + | |
| 22 | +@Service | |
| 23 | +@RequiredArgsConstructor | |
| 24 | +public class SystemMenuServiceImpl implements SystemMenuService { | |
| 25 | + | |
| 26 | + private final SysMenuMapper sysMenuMapper; | |
| 27 | + | |
| 28 | + @Override | |
| 29 | + public List<AdminRoleMenuTreeVO> tree() { | |
| 30 | + List<SysMenu> menus = listAllMenus(); | |
| 31 | + return buildTree(menus, Map.of(), false); | |
| 32 | + } | |
| 33 | + | |
| 34 | + @Override | |
| 35 | + public void add(AdminMenuSaveDTO dto) { | |
| 36 | + validateSave(dto, null); | |
| 37 | + SysMenu menu = new SysMenu(); | |
| 38 | + apply(menu, dto); | |
| 39 | + menu.setCreateTime(System.currentTimeMillis() / 1000); | |
| 40 | + sysMenuMapper.insert(menu); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public void edit(AdminMenuSaveDTO dto) { | |
| 45 | + if (dto.getId() == null || dto.getId() < 1) { | |
| 46 | + throw new BizException("菜单ID不能为空"); | |
| 47 | + } | |
| 48 | + SysMenu existing = requireMenu(dto.getId()); | |
| 49 | + validateSave(dto, existing); | |
| 50 | + apply(existing, dto); | |
| 51 | + sysMenuMapper.updateById(existing); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + @Transactional | |
| 56 | + public void delete(Long id) { | |
| 57 | + SysMenu menu = requireMenu(id); | |
| 58 | + Long childCount = sysMenuMapper.selectCount(new LambdaQueryWrapper<SysMenu>() | |
| 59 | + .eq(SysMenu::getParentId, id)); | |
| 60 | + if (childCount != null && childCount > 0) { | |
| 61 | + throw new BizException("请先删除子菜单"); | |
| 62 | + } | |
| 63 | + sysMenuMapper.deleteById(menu.getId()); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + public void setVisible(Long id, int visible) { | |
| 68 | + requireMenu(id); | |
| 69 | + sysMenuMapper.update(null, new LambdaUpdateWrapper<SysMenu>() | |
| 70 | + .eq(SysMenu::getId, id) | |
| 71 | + .set(SysMenu::getVisible, visible)); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 75 | + public void setStatus(Long id, int status) { | |
| 76 | + requireMenu(id); | |
| 77 | + sysMenuMapper.update(null, new LambdaUpdateWrapper<SysMenu>() | |
| 78 | + .eq(SysMenu::getId, id) | |
| 79 | + .set(SysMenu::getStatus, status)); | |
| 80 | + } | |
| 81 | + | |
| 82 | + public List<SysMenu> listAllMenus() { | |
| 83 | + return sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>() | |
| 84 | + .orderByAsc(SysMenu::getListOrder) | |
| 85 | + .orderByAsc(SysMenu::getId)); | |
| 86 | + } | |
| 87 | + | |
| 88 | + public List<AdminRoleMenuTreeVO> buildTree(List<SysMenu> menus, Map<Long, Boolean> checkedMap, boolean disableByScope) { | |
| 89 | + Map<Long, AdminRoleMenuTreeVO> voMap = new LinkedHashMap<>(); | |
| 90 | + List<AdminRoleMenuTreeVO> roots = new ArrayList<>(); | |
| 91 | + for (SysMenu menu : menus) { | |
| 92 | + AdminRoleMenuTreeVO vo = toVO(menu, checkedMap.get(menu.getId())); | |
| 93 | + if (disableByScope && checkedMap.get(menu.getId()) == null) { | |
| 94 | + vo.setDisabled(true); | |
| 95 | + } | |
| 96 | + voMap.put(menu.getId(), vo); | |
| 97 | + Long parentId = menu.getParentId() == null ? 0L : menu.getParentId(); | |
| 98 | + if (parentId > 0 && voMap.containsKey(parentId)) { | |
| 99 | + voMap.get(parentId).getChildren().add(vo); | |
| 100 | + } else { | |
| 101 | + roots.add(vo); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + return roots; | |
| 105 | + } | |
| 106 | + | |
| 107 | + private void validateSave(AdminMenuSaveDTO dto, SysMenu existing) { | |
| 108 | + validateTypeAndPath(dto); | |
| 109 | + validateScope(dto.getMenuScope()); | |
| 110 | + | |
| 111 | + SysMenu duplicate = sysMenuMapper.selectOne(new LambdaQueryWrapper<SysMenu>() | |
| 112 | + .eq(SysMenu::getCode, dto.getCode()) | |
| 113 | + .ne(existing != null, SysMenu::getId, existing != null ? existing.getId() : null) | |
| 114 | + .last("LIMIT 1")); | |
| 115 | + if (duplicate != null) { | |
| 116 | + throw new BizException("菜单编码已存在"); | |
| 117 | + } | |
| 118 | + | |
| 119 | + Long parentId = dto.getParentId() == null ? 0L : dto.getParentId(); | |
| 120 | + if (parentId > 0) { | |
| 121 | + SysMenu parent = requireMenu(parentId); | |
| 122 | + if (existing != null && existing.getId().equals(parent.getId())) { | |
| 123 | + throw new BizException("父级菜单不能选择自己"); | |
| 124 | + } | |
| 125 | + if (existing != null) { | |
| 126 | + ensureNotDescendant(parent.getId(), existing.getId()); | |
| 127 | + } | |
| 128 | + } | |
| 129 | + } | |
| 130 | + | |
| 131 | + private void validateTypeAndPath(AdminMenuSaveDTO dto) { | |
| 132 | + if (!"DIR".equals(dto.getType()) && !"MENU".equals(dto.getType())) { | |
| 133 | + throw new BizException("菜单类型只能是 DIR 或 MENU"); | |
| 134 | + } | |
| 135 | + if ("MENU".equals(dto.getType()) && !StringUtils.hasText(dto.getPath())) { | |
| 136 | + throw new BizException("菜单路由不能为空"); | |
| 137 | + } | |
| 138 | + } | |
| 139 | + | |
| 140 | + private void validateScope(String scope) { | |
| 141 | + for (MenuScopeEnum value : MenuScopeEnum.values()) { | |
| 142 | + if (value.name().equals(scope)) { | |
| 143 | + return; | |
| 144 | + } | |
| 145 | + } | |
| 146 | + throw new BizException("菜单范围不合法"); | |
| 147 | + } | |
| 148 | + | |
| 149 | + private void ensureNotDescendant(Long parentId, Long selfId) { | |
| 150 | + Long currentId = parentId; | |
| 151 | + while (currentId != null && currentId > 0) { | |
| 152 | + if (currentId.equals(selfId)) { | |
| 153 | + throw new BizException("父级菜单不能选择当前菜单的子节点"); | |
| 154 | + } | |
| 155 | + SysMenu current = sysMenuMapper.selectById(currentId); | |
| 156 | + currentId = current == null ? null : current.getParentId(); | |
| 157 | + } | |
| 158 | + } | |
| 159 | + | |
| 160 | + private SysMenu requireMenu(Long id) { | |
| 161 | + SysMenu menu = sysMenuMapper.selectById(id); | |
| 162 | + if (menu == null) { | |
| 163 | + throw new BizException("菜单不存在"); | |
| 164 | + } | |
| 165 | + return menu; | |
| 166 | + } | |
| 167 | + | |
| 168 | + private void apply(SysMenu menu, AdminMenuSaveDTO dto) { | |
| 169 | + menu.setCode(dto.getCode()); | |
| 170 | + menu.setName(dto.getName()); | |
| 171 | + menu.setType(dto.getType()); | |
| 172 | + menu.setPath(StringUtils.hasText(dto.getPath()) ? dto.getPath().trim() : ""); | |
| 173 | + menu.setIcon(StringUtils.hasText(dto.getIcon()) ? dto.getIcon().trim() : ""); | |
| 174 | + menu.setParentId(dto.getParentId()); | |
| 175 | + menu.setMenuScope(dto.getMenuScope()); | |
| 176 | + menu.setListOrder(dto.getListOrder() == null ? 0 : dto.getListOrder()); | |
| 177 | + menu.setVisible(dto.getVisible() == null ? 1 : dto.getVisible()); | |
| 178 | + menu.setStatus(dto.getStatus() == null ? 1 : dto.getStatus()); | |
| 179 | + } | |
| 180 | + | |
| 181 | + private AdminRoleMenuTreeVO toVO(SysMenu menu, Boolean checked) { | |
| 182 | + AdminRoleMenuTreeVO vo = new AdminRoleMenuTreeVO(); | |
| 183 | + vo.setId(menu.getId()); | |
| 184 | + vo.setCode(menu.getCode()); | |
| 185 | + vo.setName(menu.getName()); | |
| 186 | + vo.setType(menu.getType()); | |
| 187 | + vo.setPath(menu.getPath()); | |
| 188 | + vo.setIcon(menu.getIcon()); | |
| 189 | + vo.setMenuScope(menu.getMenuScope()); | |
| 190 | + vo.setVisible(menu.getVisible()); | |
| 191 | + vo.setStatus(menu.getStatus()); | |
| 192 | + vo.setListOrder(menu.getListOrder()); | |
| 193 | + vo.setChecked(Boolean.TRUE.equals(checked)); | |
| 194 | + return vo; | |
| 195 | + } | |
| 196 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/SystemRoleMenuServiceImpl.java
0 → 100644
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.diligrp.rider.common.enums.AdminRoleScopeEnum; | |
| 5 | +import com.diligrp.rider.common.enums.MenuScopeEnum; | |
| 6 | +import com.diligrp.rider.common.exception.BizException; | |
| 7 | +import com.diligrp.rider.dto.AdminRoleMenuAssignDTO; | |
| 8 | +import com.diligrp.rider.entity.SysMenu; | |
| 9 | +import com.diligrp.rider.entity.SysRole; | |
| 10 | +import com.diligrp.rider.entity.SysRoleMenu; | |
| 11 | +import com.diligrp.rider.mapper.SysRoleMapper; | |
| 12 | +import com.diligrp.rider.mapper.SysRoleMenuMapper; | |
| 13 | +import com.diligrp.rider.service.SystemRoleMenuService; | |
| 14 | +import com.diligrp.rider.vo.AdminRoleMenuTreeVO; | |
| 15 | +import com.diligrp.rider.vo.AdminRoleVO; | |
| 16 | +import lombok.RequiredArgsConstructor; | |
| 17 | +import org.springframework.stereotype.Service; | |
| 18 | +import org.springframework.transaction.annotation.Transactional; | |
| 19 | + | |
| 20 | +import java.util.ArrayList; | |
| 21 | +import java.util.LinkedHashMap; | |
| 22 | +import java.util.List; | |
| 23 | +import java.util.Map; | |
| 24 | +import java.util.Set; | |
| 25 | +import java.util.stream.Collectors; | |
| 26 | + | |
| 27 | +@Service | |
| 28 | +@RequiredArgsConstructor | |
| 29 | +public class SystemRoleMenuServiceImpl implements SystemRoleMenuService { | |
| 30 | + | |
| 31 | + private final SysRoleMapper sysRoleMapper; | |
| 32 | + private final SysRoleMenuMapper sysRoleMenuMapper; | |
| 33 | + private final SystemMenuServiceImpl systemMenuService; | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public List<AdminRoleVO> listRoles() { | |
| 37 | + List<SysRole> roles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>() | |
| 38 | + .eq(SysRole::getStatus, 1) | |
| 39 | + .orderByAsc(SysRole::getId)); | |
| 40 | + List<AdminRoleVO> result = new ArrayList<>(); | |
| 41 | + for (SysRole role : roles) { | |
| 42 | + AdminRoleVO vo = new AdminRoleVO(); | |
| 43 | + vo.setId(role.getId()); | |
| 44 | + vo.setCode(role.getCode()); | |
| 45 | + vo.setName(role.getName()); | |
| 46 | + vo.setRoleScope(role.getRoleScope()); | |
| 47 | + result.add(vo); | |
| 48 | + } | |
| 49 | + return result; | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Override | |
| 53 | + public List<AdminRoleMenuTreeVO> getRoleMenuTree(Long roleId) { | |
| 54 | + SysRole role = requireRole(roleId); | |
| 55 | + List<SysMenu> allowedMenus = filterMenusByScope(systemMenuService.listAllMenus(), role); | |
| 56 | + List<SysRoleMenu> assigned = sysRoleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>() | |
| 57 | + .eq(SysRoleMenu::getRoleId, roleId)); | |
| 58 | + Set<Long> assignedIds = assigned.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toSet()); | |
| 59 | + Map<Long, Boolean> checkedMap = new LinkedHashMap<>(); | |
| 60 | + for (SysMenu menu : allowedMenus) { | |
| 61 | + checkedMap.put(menu.getId(), assignedIds.contains(menu.getId())); | |
| 62 | + } | |
| 63 | + return systemMenuService.buildTree(allowedMenus, checkedMap, false); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + @Transactional | |
| 68 | + public void assignMenus(Long roleId, AdminRoleMenuAssignDTO dto) { | |
| 69 | + SysRole role = requireRole(roleId); | |
| 70 | + List<SysMenu> allowedMenus = filterMenusByScope(systemMenuService.listAllMenus(), role); | |
| 71 | + Set<Long> allowedIds = allowedMenus.stream().map(SysMenu::getId).collect(Collectors.toSet()); | |
| 72 | + List<Long> menuIds = dto.getMenuIds() == null ? List.of() : dto.getMenuIds(); | |
| 73 | + for (Long menuId : menuIds) { | |
| 74 | + if (!allowedIds.contains(menuId)) { | |
| 75 | + throw new BizException("存在不允许分配的菜单"); | |
| 76 | + } | |
| 77 | + } | |
| 78 | + | |
| 79 | + sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>() | |
| 80 | + .eq(SysRoleMenu::getRoleId, roleId)); | |
| 81 | + long now = System.currentTimeMillis() / 1000; | |
| 82 | + for (Long menuId : menuIds) { | |
| 83 | + SysRoleMenu roleMenu = new SysRoleMenu(); | |
| 84 | + roleMenu.setRoleId(roleId); | |
| 85 | + roleMenu.setMenuId(menuId); | |
| 86 | + roleMenu.setCreateTime(now); | |
| 87 | + sysRoleMenuMapper.insert(roleMenu); | |
| 88 | + } | |
| 89 | + } | |
| 90 | + | |
| 91 | + private SysRole requireRole(Long roleId) { | |
| 92 | + SysRole role = sysRoleMapper.selectById(roleId); | |
| 93 | + if (role == null || role.getStatus() == null || role.getStatus() != 1) { | |
| 94 | + throw new BizException("角色不存在或已禁用"); | |
| 95 | + } | |
| 96 | + return role; | |
| 97 | + } | |
| 98 | + | |
| 99 | + private List<SysMenu> filterMenusByScope(List<SysMenu> menus, SysRole role) { | |
| 100 | + return menus.stream().filter(menu -> isScopeAllowed(role, menu)).collect(Collectors.toList()); | |
| 101 | + } | |
| 102 | + | |
| 103 | + private boolean isScopeAllowed(SysRole role, SysMenu menu) { | |
| 104 | + if (MenuScopeEnum.BOTH.name().equals(menu.getMenuScope())) { | |
| 105 | + return true; | |
| 106 | + } | |
| 107 | + if (AdminRoleScopeEnum.PLATFORM.name().equals(role.getRoleScope())) { | |
| 108 | + return MenuScopeEnum.PLATFORM.name().equals(menu.getMenuScope()); | |
| 109 | + } | |
| 110 | + if (AdminRoleScopeEnum.SUBSTATION.name().equals(role.getRoleScope())) { | |
| 111 | + return MenuScopeEnum.SUBSTATION.name().equals(menu.getMenuScope()); | |
| 112 | + } | |
| 113 | + return false; | |
| 114 | + } | |
| 115 | +} | ... | ... |
src/main/java/com/diligrp/rider/vo/AdminLoginUserVO.java
0 → 100644
| 1 | +package com.diligrp.rider.vo; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +@Data | |
| 6 | +public class AdminLoginUserVO { | |
| 7 | + private Long id; | |
| 8 | + private String userLogin; | |
| 9 | + private String userNickname; | |
| 10 | + /** 兼容前端现有判断:admin / substation */ | |
| 11 | + private String role; | |
| 12 | + private String roleType; | |
| 13 | + private String roleCode; | |
| 14 | + private Long cityId; | |
| 15 | + private String cityName; | |
| 16 | +} | ... | ... |
src/main/java/com/diligrp/rider/vo/AdminLoginVO.java
| ... | ... | @@ -2,14 +2,12 @@ package com.diligrp.rider.vo; |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | 4 | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 5 | 7 | @Data |
| 6 | 8 | public class AdminLoginVO { |
| 7 | - private Long id; | |
| 8 | - private String userLogin; | |
| 9 | - private String userNickname; | |
| 10 | - /** 角色:admin / substation */ | |
| 11 | - private String role; | |
| 12 | - /** 分站关联城市ID(substation角色才有) */ | |
| 13 | - private Long cityId; | |
| 14 | 9 | private String token; |
| 10 | + private AdminLoginUserVO user; | |
| 11 | + private List<AdminMenuVO> menus; | |
| 12 | + private String homePath; | |
| 15 | 13 | } | ... | ... |
src/main/java/com/diligrp/rider/vo/AdminMenuVO.java
0 → 100644
| 1 | +package com.diligrp.rider.vo; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +import java.util.ArrayList; | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +@Data | |
| 9 | +public class AdminMenuVO { | |
| 10 | + private Long id; | |
| 11 | + private String code; | |
| 12 | + private String name; | |
| 13 | + private String type; | |
| 14 | + private String path; | |
| 15 | + private String icon; | |
| 16 | + private List<AdminMenuVO> children = new ArrayList<>(); | |
| 17 | +} | ... | ... |
src/main/java/com/diligrp/rider/vo/AdminRoleMenuTreeVO.java
0 → 100644
| 1 | +package com.diligrp.rider.vo; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +import java.util.ArrayList; | |
| 6 | +import java.util.List; | |
| 7 | + | |
| 8 | +@Data | |
| 9 | +public class AdminRoleMenuTreeVO { | |
| 10 | + private Long id; | |
| 11 | + private String code; | |
| 12 | + private String name; | |
| 13 | + private String type; | |
| 14 | + private String path; | |
| 15 | + private String icon; | |
| 16 | + private String menuScope; | |
| 17 | + private Integer visible; | |
| 18 | + private Integer status; | |
| 19 | + private Integer listOrder; | |
| 20 | + private Boolean checked = false; | |
| 21 | + private Boolean disabled = false; | |
| 22 | + private List<AdminRoleMenuTreeVO> children = new ArrayList<>(); | |
| 23 | +} | ... | ... |
src/main/java/com/diligrp/rider/vo/AdminRoleVO.java
0 → 100644
src/main/resources/data-init.sql
| ... | ... | @@ -64,9 +64,43 @@ INSERT INTO `city` (`id`, `pid`, `name`, `area_code`, `status`, `rate`, `list_or |
| 64 | 64 | -- 2. 分站管理员(每个已开通城市一个) |
| 65 | 65 | -- 默认密码均为 admin123(MD5: 0192023a7bbd73250516f069df18b500) |
| 66 | 66 | -- ============================================================ |
| 67 | -INSERT INTO `substation` (`city_id`, `user_login`, `user_nickname`, `user_pass`, `mobile`, `user_status`, `create_time`) VALUES | |
| 68 | -(2, 'gz_admin', '广州分站管理员', '0192023a7bbd73250516f069df18b500', '13800000001', 1, UNIX_TIMESTAMP()), | |
| 69 | -(3, 'sz_admin', '深圳分站管理员', '0192023a7bbd73250516f069df18b500', '13800000002', 1, UNIX_TIMESTAMP()); | |
| 67 | +INSERT INTO `sys_role` (`code`, `name`, `role_scope`, `status`, `create_time`) VALUES | |
| 68 | +('platform_admin', '平台管理员', 'PLATFORM', 1, UNIX_TIMESTAMP()), | |
| 69 | +('substation_admin', '分站管理员', 'SUBSTATION', 1, UNIX_TIMESTAMP()); | |
| 70 | + | |
| 71 | +INSERT INTO `sys_menu` (`code`, `name`, `type`, `path`, `icon`, `parent_id`, `menu_scope`, `list_order`, `visible`, `status`, `create_time`) VALUES | |
| 72 | +('dashboard', '工作台', 'MENU', '/dashboard', 'HomeOutlined', 0, 'BOTH', 10, 1, 1, UNIX_TIMESTAMP()), | |
| 73 | +('city.manage', '租户管理', 'MENU', '/city', 'GlobalOutlined', 0, 'PLATFORM', 20, 1, 1, UNIX_TIMESTAMP()), | |
| 74 | +('substation.manage', '分站管理', 'MENU', '/substation', 'ApartmentOutlined', 0, 'PLATFORM', 30, 1, 1, UNIX_TIMESTAMP()), | |
| 75 | +('merchant.root', '商家管理', 'DIR', '', 'ShopOutlined', 0, 'PLATFORM', 40, 1, 1, UNIX_TIMESTAMP()), | |
| 76 | +('merchant.enter', '入驻申请', 'MENU', '/merchant/enter', '', 4, 'PLATFORM', 41, 1, 1, UNIX_TIMESTAMP()), | |
| 77 | +('merchant.store', '店铺管理', 'MENU', '/merchant/store', '', 4, 'PLATFORM', 42, 1, 1, UNIX_TIMESTAMP()), | |
| 78 | +('rider.list', '骑手管理', 'MENU', '/rider', 'UserOutlined', 0, 'BOTH', 50, 1, 1, UNIX_TIMESTAMP()), | |
| 79 | +('rider.evaluate', '骑手评价', 'MENU', '/rider/evaluate', 'StarOutlined', 0, 'BOTH', 60, 1, 1, UNIX_TIMESTAMP()), | |
| 80 | +('order.root', '订单管理', 'DIR', '', 'UnorderedListOutlined', 0, 'BOTH', 70, 1, 1, UNIX_TIMESTAMP()), | |
| 81 | +('order.list', '订单列表', 'MENU', '/order', '', 9, 'BOTH', 71, 1, 1, UNIX_TIMESTAMP()), | |
| 82 | +('order.refund', '退款管理', 'MENU', '/refund', '', 9, 'BOTH', 72, 1, 1, UNIX_TIMESTAMP()), | |
| 83 | +('order.delivery', '配送订单', 'MENU', '/delivery/order', '', 9, 'BOTH', 73, 1, 1, UNIX_TIMESTAMP()), | |
| 84 | +('config.root', '配置中心', 'DIR', '', 'ControlOutlined', 0, 'BOTH', 80, 1, 1, UNIX_TIMESTAMP()), | |
| 85 | +('fee.plan', '配送费配置', 'MENU', '/config/fee-plan', '', 13, 'BOTH', 81, 1, 1, UNIX_TIMESTAMP()), | |
| 86 | +('dispatch.rule', '调度配置', 'MENU', '/dispatch/rule', '', 13, 'BOTH', 82, 1, 1, UNIX_TIMESTAMP()), | |
| 87 | +('open.root', '开放平台', 'DIR', '', 'ApiOutlined', 0, 'PLATFORM', 90, 1, 1, UNIX_TIMESTAMP()), | |
| 88 | +('open.app', '应用管理', 'MENU', '/open', '', 16, 'PLATFORM', 91, 1, 1, UNIX_TIMESTAMP()), | |
| 89 | +('open.mock_delivery', '模拟推单', 'MENU', '/open/mock-delivery', '', 16, 'PLATFORM', 92, 1, 1, UNIX_TIMESTAMP()), | |
| 90 | +('system.root', '系统管理', 'DIR', '', 'ControlOutlined', 0, 'PLATFORM', 100, 1, 1, UNIX_TIMESTAMP()), | |
| 91 | +('system.menu', '菜单管理', 'MENU', '/system/menu', '', 19, 'PLATFORM', 101, 1, 1, UNIX_TIMESTAMP()), | |
| 92 | +('system.role_menu', '角色菜单', 'MENU', '/system/role-menu', '', 19, 'PLATFORM', 102, 1, 1, UNIX_TIMESTAMP()), | |
| 93 | +('admin.user', '平台账号', 'MENU', '/admin-user', '', 19, 'PLATFORM', 103, 1, 1, UNIX_TIMESTAMP()); | |
| 94 | + | |
| 95 | +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 96 | +SELECT 1, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('PLATFORM', 'BOTH'); | |
| 97 | + | |
| 98 | +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 99 | +SELECT 2, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('SUBSTATION', 'BOTH'); | |
| 100 | + | |
| 101 | +INSERT INTO `substation` (`city_id`, `user_login`, `user_nickname`, `user_pass`, `mobile`, `user_status`, `role_id`, `create_time`) VALUES | |
| 102 | +(2, 'gz_admin', '广州分站管理员', '0192023a7bbd73250516f069df18b500', '13800000001', 1, 2, UNIX_TIMESTAMP()), | |
| 103 | +(3, 'sz_admin', '深圳分站管理员', '0192023a7bbd73250516f069df18b500', '13800000002', 1, 2, UNIX_TIMESTAMP()); | |
| 70 | 104 | |
| 71 | 105 | -- ============================================================ |
| 72 | 106 | -- 3. 骑手等级配置(广州) |
| ... | ... | @@ -135,7 +169,8 @@ SELECT '开放平台 AppKey: TESTAPPKEY00001' AS 开放平台; |
| 135 | 169 | -- 7. 超级管理员账号 |
| 136 | 170 | -- 默认密码:admin123(MD5: 0192023a7bbd73250516f069df18b500) |
| 137 | 171 | -- ============================================================ |
| 138 | -INSERT INTO `admin_user` (`user_login`, `user_pass`, `user_nickname`, `user_status`, `create_time`) VALUES | |
| 139 | -('admin', '0192023a7bbd73250516f069df18b500', '超级管理员', 1, UNIX_TIMESTAMP()); | |
| 172 | +INSERT INTO `admin_user` (`user_login`, `user_pass`, `user_nickname`, `user_status`, `role_id`, `create_time`) VALUES | |
| 173 | +('admin', '0192023a7bbd73250516f069df18b500', '超级管理员', 1, 1, UNIX_TIMESTAMP()); | |
| 140 | 174 | |
| 141 | 175 | SELECT '超级管理员: admin / admin123(role=admin)' AS 超管账号; |
| 176 | +SELECT '系统管理菜单: /system/menu /system/role-menu /admin-user' AS 系统管理; | ... | ... |
src/main/resources/schema.sql
| ... | ... | @@ -258,6 +258,7 @@ CREATE TABLE `substation` ( |
| 258 | 258 | `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', |
| 259 | 259 | `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像', |
| 260 | 260 | `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', |
| 261 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 261 | 262 | `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', |
| 262 | 263 | PRIMARY KEY (`id`), |
| 263 | 264 | UNIQUE KEY `uk_user_login` (`user_login`) |
| ... | ... | @@ -366,11 +367,51 @@ CREATE TABLE `admin_user` ( |
| 366 | 367 | `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', |
| 367 | 368 | `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', |
| 368 | 369 | `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', |
| 370 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 369 | 371 | `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, |
| 370 | 372 | PRIMARY KEY (`id`), |
| 371 | 373 | UNIQUE KEY `uk_user_login` (`user_login`) |
| 372 | 374 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级管理员表'; |
| 373 | 375 | |
| 376 | +CREATE TABLE `sys_role` ( | |
| 377 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 378 | + `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色编码', | |
| 379 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色名称', | |
| 380 | + `role_scope` VARCHAR(32) NOT NULL DEFAULT 'PLATFORM' COMMENT '角色归属:PLATFORM/SUBSTATION', | |
| 381 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 382 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 383 | + PRIMARY KEY (`id`), | |
| 384 | + UNIQUE KEY `uk_role_code` (`code`) | |
| 385 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单角色表'; | |
| 386 | + | |
| 387 | +CREATE TABLE `sys_menu` ( | |
| 388 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 389 | + `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单编码', | |
| 390 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单名称', | |
| 391 | + `type` VARCHAR(16) NOT NULL DEFAULT 'MENU' COMMENT '类型:DIR/MENU', | |
| 392 | + `path` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '前端路由路径', | |
| 393 | + `icon` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '前端图标名', | |
| 394 | + `parent_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父级菜单ID', | |
| 395 | + `menu_scope` VARCHAR(32) NOT NULL DEFAULT 'BOTH' COMMENT '菜单归属:PLATFORM/SUBSTATION/BOTH', | |
| 396 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 397 | + `visible` TINYINT NOT NULL DEFAULT 1 COMMENT '是否显示:0=否 1=是', | |
| 398 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 399 | + KEY `idx_scope_parent` (`menu_scope`, `parent_id`, `list_order`), | |
| 400 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 401 | + PRIMARY KEY (`id`), | |
| 402 | + UNIQUE KEY `uk_menu_code` (`code`), | |
| 403 | + KEY `idx_parent_order` (`parent_id`, `list_order`) | |
| 404 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单表'; | |
| 405 | + | |
| 406 | +CREATE TABLE `sys_role_menu` ( | |
| 407 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 408 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '角色ID', | |
| 409 | + `menu_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单ID', | |
| 410 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 411 | + PRIMARY KEY (`id`), | |
| 412 | + UNIQUE KEY `uk_role_menu` (`role_id`, `menu_id`) | |
| 413 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台角色菜单关系表'; | |
| 414 | + | |
| 374 | 415 | -- orders 表补充字段(如已有 orders 表,执行以下 ALTER) |
| 375 | 416 | ALTER TABLE `orders` ADD COLUMN `out_order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '外部系统订单号' AFTER `order_no`; |
| 376 | 417 | ALTER TABLE `orders` ADD COLUMN `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '接入方AppKey' AFTER `out_order_no`; | ... | ... |