Commit 146da27e04d663c833f373b4484b4ead1a508cc9

Authored by shaofan
1 parent 33eee1ba

新增多城市支持逻辑,完善店铺管理功能并新增商家管理相关接口

src/main/java/com/diligrp/rider/controller/AdminMerchantController.java 0 → 100644
  1 +package com.diligrp.rider.controller;
  2 +
  3 +import com.diligrp.rider.common.exception.BizException;
  4 +import com.diligrp.rider.common.result.Result;
  5 +import com.diligrp.rider.dto.MerchantStoreDTO;
  6 +import com.diligrp.rider.entity.MerchantStore;
  7 +import com.diligrp.rider.service.MerchantService;
  8 +import jakarta.servlet.http.HttpServletRequest;
  9 +import jakarta.validation.Valid;
  10 +import lombok.RequiredArgsConstructor;
  11 +import org.springframework.web.bind.annotation.*;
  12 +
  13 +import java.math.BigDecimal;
  14 +import java.util.List;
  15 +
  16 +@RestController
  17 +@RequestMapping("/api/admin/merchant")
  18 +@RequiredArgsConstructor
  19 +public class AdminMerchantController {
  20 +
  21 + private final MerchantService merchantService;
  22 +
  23 + @GetMapping("/store/list")
  24 + public Result<List<MerchantStore>> storeList(@RequestParam(required = false) Long cityId,
  25 + @RequestParam(required = false) String keyword,
  26 + @RequestParam(defaultValue = "1") int page,
  27 + HttpServletRequest request) {
  28 + return Result.success(merchantService.storeList(resolveQueryCityId(request, cityId), keyword, page));
  29 + }
  30 +
  31 + @PostMapping("/store/add")
  32 + public Result<Long> addStore(@Valid @RequestBody MerchantStoreDTO dto, HttpServletRequest request) {
  33 + return Result.success(merchantService.addStore(dto, resolveScopedCityId(request)));
  34 + }
  35 +
  36 + @PutMapping("/store/edit")
  37 + public Result<Void> editStore(@Valid @RequestBody MerchantStoreDTO dto, HttpServletRequest request) {
  38 + merchantService.editStore(dto, resolveScopedCityId(request));
  39 + return Result.success();
  40 + }
  41 +
  42 + @GetMapping("/store/detail")
  43 + public Result<MerchantStore> storeDetail(@RequestParam Long storeId, HttpServletRequest request) {
  44 + return Result.success(merchantService.getStore(storeId, resolveScopedCityId(request)));
  45 + }
  46 +
  47 + @PostMapping("/store/setOperatingState")
  48 + public Result<Void> setOperatingState(@RequestParam Long storeId,
  49 + @RequestParam int state,
  50 + HttpServletRequest request) {
  51 + merchantService.setOperatingState(storeId, state, resolveScopedCityId(request));
  52 + return Result.success();
  53 + }
  54 +
  55 + @PostMapping("/store/setAutoOrder")
  56 + public Result<Void> setAutoOrder(@RequestParam Long storeId,
  57 + @RequestParam int auto,
  58 + HttpServletRequest request) {
  59 + merchantService.setAutoOrder(storeId, auto);
  60 + return Result.success();
  61 + }
  62 +
  63 + @PostMapping("/store/updateFeeConfig")
  64 + public Result<Void> updateFeeConfig(@RequestParam Long storeId,
  65 + @RequestParam(defaultValue = "0") BigDecimal freeShipping,
  66 + @RequestParam(defaultValue = "0") BigDecimal upToSend,
  67 + HttpServletRequest request) {
  68 + merchantService.updateFeeConfig(storeId, freeShipping, upToSend, resolveScopedCityId(request));
  69 + return Result.success();
  70 + }
  71 +
  72 + @DeleteMapping("/store/del")
  73 + public Result<Void> delStore(@RequestParam Long storeId, HttpServletRequest request) {
  74 + merchantService.delStore(storeId, resolveScopedCityId(request));
  75 + return Result.success();
  76 + }
  77 +
  78 + private Long resolveScopedCityId(HttpServletRequest request) {
  79 + if (!"substation".equals(request.getAttribute("role"))) {
  80 + return null;
  81 + }
  82 + Long cityId = (Long) request.getAttribute("cityId");
  83 + if (cityId == null || cityId < 1) {
  84 + throw new BizException("当前账号未绑定有效城市");
  85 + }
  86 + return cityId;
  87 + }
  88 +
  89 + private Long resolveQueryCityId(HttpServletRequest request, Long queryCityId) {
  90 + Long scopedCityId = resolveScopedCityId(request);
  91 + return scopedCityId != null ? scopedCityId : queryCityId;
  92 + }
  93 +}
... ...
src/main/java/com/diligrp/rider/dto/AdminRiderAddDTO.java
... ... @@ -14,5 +14,7 @@ public class AdminRiderAddDTO {
14 14 @NotBlank(message = "密码不能为空")
15 15 private String password;
16 16  
  17 + private String idNo;
  18 +
17 19 private Long cityId;
18 20 }
... ...
src/main/java/com/diligrp/rider/service/MerchantService.java
... ... @@ -3,32 +3,24 @@ package com.diligrp.rider.service;
3 3 import com.diligrp.rider.dto.MerchantStoreDTO;
4 4 import com.diligrp.rider.entity.MerchantStore;
5 5  
  6 +import java.math.BigDecimal;
6 7 import java.util.List;
7 8  
8 9 public interface MerchantService {
9   - // ---- 店铺管理 ----
10   - /** 新增店铺(平台后台直接新建,无需审核) */
11 10 Long addStore(MerchantStoreDTO dto);
12   - /** 编辑店铺 */
  11 + Long addStore(MerchantStoreDTO dto, Long scopedCityId);
13 12 void editStore(MerchantStoreDTO dto);
14   - /** 店铺列表 */
  13 + void editStore(MerchantStoreDTO dto, Long scopedCityId);
15 14 List<MerchantStore> storeList(Long cityId, String keyword, int page);
16   - /** 获取店铺详情 */
17 15 MerchantStore getStore(Long storeId);
18   - /** 设置营业/打烊 */
  16 + MerchantStore getStore(Long storeId, Long scopedCityId);
19 17 void setOperatingState(Long storeId, int state);
20   - /** 设置自动接单 */
  18 + void setOperatingState(Long storeId, int state, Long scopedCityId);
21 19 void setAutoOrder(Long storeId, int auto);
22   - /** 更新免运费和起送金额 */
23   - void updateFeeConfig(Long storeId, java.math.BigDecimal freeShipping, java.math.BigDecimal upToSend);
24   - /** 删除店铺 */
  20 + void updateFeeConfig(Long storeId, BigDecimal freeShipping, BigDecimal upToSend);
  21 + void updateFeeConfig(Long storeId, BigDecimal freeShipping, BigDecimal upToSend, Long scopedCityId);
25 22 void delStore(Long storeId);
26   - /**
27   - * 外部系统同步门店(新增或更新,以 appKey+outStoreId 为唯一键)
28   - */
  23 + void delStore(Long storeId, Long scopedCityId);
29 24 MerchantStore syncStore(String appKey, MerchantStoreDTO dto);
30   - /**
31   - * 根据 appKey + outStoreId 查询门店
32   - */
33 25 MerchantStore getByOutStoreId(String appKey, String outStoreId);
34 26 }
... ...
src/main/java/com/diligrp/rider/service/impl/AdminRiderServiceImpl.java
... ... @@ -57,6 +57,7 @@ public class AdminRiderServiceImpl implements AdminRiderService {
57 57 Rider rider = new Rider();
58 58 rider.setUserNickname(dto.getUserNickname());
59 59 rider.setMobile(dto.getMobile());
  60 + rider.setIdNo(dto.getIdNo());
60 61 rider.setUserPass(encryptPass(dto.getPassword()));
61 62 rider.setCityId(cityId);
62 63 rider.setUserStatus(1);
... ...
src/main/java/com/diligrp/rider/service/impl/MenuBootstrapServiceImpl.java
... ... @@ -61,9 +61,9 @@ public class MenuBootstrapServiceImpl implements MenuBootstrapService {
61 61 defaults.add(menu("city.manage", "租户管理", "MENU", "/city", "GlobalOutlined", 0L, MenuScopeEnum.PLATFORM, 20));
62 62 defaults.add(menu("substation.manage", "分站管理", "MENU", "/substation", "ApartmentOutlined", 0L, MenuScopeEnum.PLATFORM, 30));
63 63 defaults.add(menu("substation.user", "分站账号", "MENU", "/substation/user", "TeamOutlined", 0L, MenuScopeEnum.SUBSTATION, 31));
64   - defaults.add(menu("merchant.root", "商家管理", "DIR", "", "ShopOutlined", 0L, MenuScopeEnum.PLATFORM, 40));
65   - defaults.add(menu("merchant.enter", "入驻申请", "MENU", "/merchant/enter", "", 0L, MenuScopeEnum.PLATFORM, 41));
66   - defaults.add(menu("merchant.store", "店铺管理", "MENU", "/merchant/store", "", 0L, MenuScopeEnum.PLATFORM, 42));
  64 + defaults.add(menu("merchant.root", "商家管理", "DIR", "", "ShopOutlined", 0L, MenuScopeEnum.BOTH, 40));
  65 + defaults.add(menu("merchant.enter", "入驻申请", "MENU", "/merchant/enter", "", 0L, MenuScopeEnum.BOTH, 41));
  66 + defaults.add(menu("merchant.store", "店铺管理", "MENU", "/merchant/store", "", 0L, MenuScopeEnum.BOTH, 42));
67 67 defaults.add(menu("rider.list", "骑手管理", "MENU", "/rider", "UserOutlined", 0L, MenuScopeEnum.BOTH, 50));
68 68 defaults.add(menu("rider.level", "骑手等级", "MENU", "/rider/level", "TrophyOutlined", 0L, MenuScopeEnum.BOTH, 55));
69 69 defaults.add(menu("rider.evaluate", "骑手评价", "MENU", "/rider/evaluate", "StarOutlined", 0L, MenuScopeEnum.BOTH, 60));
... ...
src/main/java/com/diligrp/rider/service/impl/MerchantServiceImpl.java
... ... @@ -30,8 +30,15 @@ public class MerchantServiceImpl implements MerchantService {
30 30 @Override
31 31 @Transactional
32 32 public Long addStore(MerchantStoreDTO dto) {
33   - var city = cityService.getById(dto.getCityId());
34   - if (city == null) throw new BizException("城市不存在");
  33 + return addStore(dto, null);
  34 + }
  35 +
  36 + @Override
  37 + @Transactional
  38 + public Long addStore(MerchantStoreDTO dto, Long scopedCityId) {
  39 + Long effectiveCityId = resolveCityId(dto.getCityId(), scopedCityId);
  40 + dto.setCityId(effectiveCityId);
  41 + ensureCityExists(effectiveCityId);
35 42  
36 43 MerchantStore store = toEntity(dto);
37 44 store.setAddTime(System.currentTimeMillis() / 1000);
... ... @@ -56,8 +63,18 @@ public class MerchantServiceImpl implements MerchantService {
56 63  
57 64 @Override
58 65 public void editStore(MerchantStoreDTO dto) {
59   - MerchantStore existing = storeMapper.selectById(dto.getId());
60   - if (existing == null) throw new BizException("店铺不存在");
  66 + editStore(dto, null);
  67 + }
  68 +
  69 + @Override
  70 + public void editStore(MerchantStoreDTO dto, Long scopedCityId) {
  71 + MerchantStore existing = requireStore(dto.getId(), scopedCityId);
  72 + Long effectiveCityId = resolveCityId(dto.getCityId(), scopedCityId);
  73 + if (scopedCityId != null && !scopedCityId.equals(existing.getCityId())) {
  74 + throw new BizException("无权操作其他租户的店铺");
  75 + }
  76 + dto.setCityId(effectiveCityId);
  77 + ensureCityExists(effectiveCityId);
61 78 MerchantStore store = toEntity(dto);
62 79 store.setId(dto.getId());
63 80 storeMapper.updateById(store);
... ... @@ -71,42 +88,67 @@ public class MerchantServiceImpl implements MerchantService {
71 88 .orderByDesc(MerchantStore::getId);
72 89 if (cityId != null && cityId > 0) wrapper.eq(MerchantStore::getCityId, cityId);
73 90 if (keyword != null && !keyword.isBlank()) wrapper.like(MerchantStore::getName, keyword);
74   - int offset = (page - 1) * PAGE_SIZE;
  91 + int currentPage = Math.max(page, 1);
  92 + int offset = (currentPage - 1) * PAGE_SIZE;
75 93 wrapper.last("LIMIT " + offset + "," + PAGE_SIZE);
76 94 return storeMapper.selectList(wrapper);
77 95 }
78 96  
79 97 @Override
80 98 public MerchantStore getStore(Long storeId) {
81   - return storeMapper.selectById(storeId);
  99 + return getStore(storeId, null);
  100 + }
  101 +
  102 + @Override
  103 + public MerchantStore getStore(Long storeId, Long scopedCityId) {
  104 + return requireStore(storeId, scopedCityId);
82 105 }
83 106  
84 107 @Override
85 108 public void setOperatingState(Long storeId, int state) {
  109 + setOperatingState(storeId, state, null);
  110 + }
  111 +
  112 + @Override
  113 + public void setOperatingState(Long storeId, int state, Long scopedCityId) {
  114 + MerchantStore store = requireStore(storeId, scopedCityId);
86 115 storeMapper.update(null, new LambdaUpdateWrapper<MerchantStore>()
87   - .eq(MerchantStore::getId, storeId)
  116 + .eq(MerchantStore::getId, store.getId())
88 117 .set(MerchantStore::getOperatingState, state));
89 118 }
90 119  
91 120 @Override
92 121 public void setAutoOrder(Long storeId, int auto) {
  122 + MerchantStore store = requireStore(storeId, null);
93 123 storeMapper.update(null, new LambdaUpdateWrapper<MerchantStore>()
94   - .eq(MerchantStore::getId, storeId)
  124 + .eq(MerchantStore::getId, store.getId())
95 125 .set(MerchantStore::getAutomaticOrder, auto));
96 126 }
97 127  
98 128 @Override
99 129 public void updateFeeConfig(Long storeId, BigDecimal freeShipping, BigDecimal upToSend) {
  130 + updateFeeConfig(storeId, freeShipping, upToSend, null);
  131 + }
  132 +
  133 + @Override
  134 + public void updateFeeConfig(Long storeId, BigDecimal freeShipping, BigDecimal upToSend, Long scopedCityId) {
  135 + MerchantStore store = requireStore(storeId, scopedCityId);
100 136 storeMapper.update(null, new LambdaUpdateWrapper<MerchantStore>()
101   - .eq(MerchantStore::getId, storeId)
  137 + .eq(MerchantStore::getId, store.getId())
102 138 .set(MerchantStore::getFreeShipping, freeShipping)
103 139 .set(MerchantStore::getUpToSend, upToSend));
104 140 }
105 141  
106 142 @Override
107 143 public void delStore(Long storeId) {
  144 + delStore(storeId, null);
  145 + }
  146 +
  147 + @Override
  148 + public void delStore(Long storeId, Long scopedCityId) {
  149 + MerchantStore store = requireStore(storeId, scopedCityId);
108 150 storeMapper.update(null, new LambdaUpdateWrapper<MerchantStore>()
109   - .eq(MerchantStore::getId, storeId)
  151 + .eq(MerchantStore::getId, store.getId())
110 152 .set(MerchantStore::getIsDel, 1));
111 153 }
112 154  
... ... @@ -115,6 +157,7 @@ public class MerchantServiceImpl implements MerchantService {
115 157 public MerchantStore syncStore(String appKey, MerchantStoreDTO dto) {
116 158 if (appKey == null || appKey.isBlank()) throw new BizException("AppKey不能为空");
117 159 if (dto.getOutStoreId() == null || dto.getOutStoreId().isBlank()) throw new BizException("外部门店编号不能为空");
  160 + ensureCityExists(dto.getCityId());
118 161  
119 162 long now = System.currentTimeMillis() / 1000;
120 163 MerchantStore existing = getByOutStoreId(appKey, dto.getOutStoreId());
... ... @@ -144,6 +187,40 @@ public class MerchantServiceImpl implements MerchantService {
144 187 .last("LIMIT 1"));
145 188 }
146 189  
  190 + private void ensureCityExists(Long cityId) {
  191 + if (cityId == null || cityId < 1) {
  192 + throw new BizException("城市不能为空");
  193 + }
  194 + var city = cityService.getById(cityId);
  195 + if (city == null) {
  196 + throw new BizException("城市不存在");
  197 + }
  198 + }
  199 +
  200 + private Long resolveCityId(Long dtoCityId, Long scopedCityId) {
  201 + if (scopedCityId != null) {
  202 + return scopedCityId;
  203 + }
  204 + if (dtoCityId == null || dtoCityId < 1) {
  205 + throw new BizException("城市不能为空");
  206 + }
  207 + return dtoCityId;
  208 + }
  209 +
  210 + private MerchantStore requireStore(Long storeId, Long scopedCityId) {
  211 + MerchantStore store = storeMapper.selectOne(new LambdaQueryWrapper<MerchantStore>()
  212 + .eq(MerchantStore::getId, storeId)
  213 + .eq(MerchantStore::getIsDel, 0)
  214 + .last("LIMIT 1"));
  215 + if (store == null) {
  216 + throw new BizException("店铺不存在");
  217 + }
  218 + if (scopedCityId != null && !scopedCityId.equals(store.getCityId())) {
  219 + throw new BizException("无权操作其他租户的店铺");
  220 + }
  221 + return store;
  222 + }
  223 +
147 224 private MerchantStore toEntity(MerchantStoreDTO dto) {
148 225 MerchantStore store = new MerchantStore();
149 226 store.setName(dto.getName());
... ...
src/main/resources/data-init.sql
... ... @@ -72,9 +72,9 @@ INSERT INTO `sys_menu` (`code`, `name`, `type`, `path`, `icon`, `parent_id`, `me
72 72 ('dashboard', '工作台', 'MENU', '/dashboard', 'HomeOutlined', 0, 'BOTH', 10, 1, 1, UNIX_TIMESTAMP()),
73 73 ('city.manage', '租户管理', 'MENU', '/city', 'GlobalOutlined', 0, 'PLATFORM', 20, 1, 1, UNIX_TIMESTAMP()),
74 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()),
  75 +('merchant.root', '商家管理', 'DIR', '', 'ShopOutlined', 0, 'BOTH', 40, 1, 1, UNIX_TIMESTAMP()),
  76 +('merchant.enter', '入驻申请', 'MENU', '/merchant/enter', '', 4, 'BOTH', 41, 1, 1, UNIX_TIMESTAMP()),
  77 +('merchant.store', '店铺管理', 'MENU', '/merchant/store', '', 4, 'BOTH', 42, 1, 1, UNIX_TIMESTAMP()),
78 78 ('rider.list', '骑手管理', 'MENU', '/rider', 'UserOutlined', 0, 'BOTH', 50, 1, 1, UNIX_TIMESTAMP()),
79 79 ('rider.evaluate', '骑手评价', 'MENU', '/rider/evaluate', 'StarOutlined', 0, 'BOTH', 60, 1, 1, UNIX_TIMESTAMP()),
80 80 ('order.root', '订单管理', 'DIR', '', 'UnorderedListOutlined', 0, 'BOTH', 70, 1, 1, UNIX_TIMESTAMP()),
... ...