Explorar el Código

会员管理代码

wwh hace 1 semana
padre
commit
5c3c0c84c4

+ 152 - 3
baqing-shop/src/main/java/com/ruoyi/web/modules/entry/service/impl/MerchantEntryApplyServiceImpl.java

@@ -39,7 +39,11 @@ import com.ruoyi.web.modules.merchant.dto.MerchantCreateDTO;
39 39
 import com.ruoyi.web.modules.merchant.mapper.BizMerchantMapper;
40 40
 import com.ruoyi.web.modules.merchant.service.IMerchantAccountBindService;
41 41
 import com.ruoyi.web.modules.merchant.support.BizCompleteEvaluator;
42
+import com.ruoyi.web.modules.merchant.vo.MerchantShopBriefVO;
42 43
 import com.ruoyi.web.modules.store.dto.ShopCreateDTO;
44
+import com.ruoyi.web.modules.store.domain.BizMerchantAccount;
45
+import com.ruoyi.web.modules.store.domain.BizShop;
46
+import com.ruoyi.web.modules.store.mapper.BizMerchantAccountMapper;
43 47
 import com.ruoyi.web.modules.store.mapper.BizShopMapper;
44 48
 import com.ruoyi.web.modules.store.service.IShopService;
45 49
 
@@ -57,6 +61,9 @@ public class MerchantEntryApplyServiceImpl implements IMerchantEntryApplyService
57 61
     @Autowired
58 62
     private BizShopMapper shopMapper;
59 63
 
64
+    @Autowired
65
+    private BizMerchantAccountMapper merchantAccountMapper;
66
+
60 67
     @Autowired
61 68
     private BizMemberMapper memberMapper;
62 69
 
@@ -255,7 +262,29 @@ public class MerchantEntryApplyServiceImpl implements IMerchantEntryApplyService
255 262
 
256 263
     private Map<String, Object> finalizeEntry(BizMerchantEntryApply apply, String operator)
257 264
     {
265
+        if (apply.getMerchantId() != null && apply.getShopId() != null)
266
+        {
267
+            return markApplyApproved(apply, apply.getMerchantId(), apply.getShopId(), operator, false);
268
+        }
269
+
258 270
         EntryApplySubmitDTO submitDto = buildSubmitFromApply(apply);
271
+
272
+        BizMerchant memberAdminMerchant = findMerchantByMemberAdmin(apply.getMemberId());
273
+        if (memberAdminMerchant != null)
274
+        {
275
+            log.info("会员已是商户管理员,复用商户并按申请创建店铺完成入驻,applyId={},merchantId={}",
276
+                    apply.getApplyId(), memberAdminMerchant.getMerchantId());
277
+            return reconcileWithExistingMerchant(apply, memberAdminMerchant, submitDto, operator, true);
278
+        }
279
+
280
+        BizMerchant existingMerchant = findExistingMerchant(submitDto, apply.getMerchantType());
281
+        if (existingMerchant != null && canReconcileExistingMerchant(existingMerchant, apply.getMemberId()))
282
+        {
283
+            log.info("入驻申请关联已有商户建档,applyId={},merchantId={}", apply.getApplyId(),
284
+                    existingMerchant.getMerchantId());
285
+            return reconcileWithExistingMerchant(apply, existingMerchant, submitDto, operator, false);
286
+        }
287
+
259 288
         assertUniqueOnSubmit(submitDto, apply.getMerchantType(), apply.getApplyId());
260 289
         EntryApplyFormValidator.validateMobileSubmit(submitDto);
261 290
 
@@ -291,22 +320,142 @@ public class MerchantEntryApplyServiceImpl implements IMerchantEntryApplyService
291 320
         shopFundService.seedContactBankAccountFromEntry(shopId, merchant.getContactName(),
292 321
                 merchant.getBankAccount(), operator);
293 322
 
323
+        return markApplyApproved(apply, merchant.getMerchantId(), shopId, operator, true);
324
+    }
325
+
326
+    private Map<String, Object> markApplyApproved(BizMerchantEntryApply apply, Long merchantId, Long shopId,
327
+            String operator, boolean notifyMember)
328
+    {
294 329
         BizMerchantEntryApply update = new BizMerchantEntryApply();
295 330
         update.setApplyId(apply.getApplyId());
296 331
         update.setApplyStatus(MerchantEntryApplyConstants.STATUS_APPROVED);
297
-        update.setMerchantId(merchant.getMerchantId());
332
+        update.setMerchantId(merchantId);
298 333
         update.setShopId(shopId);
299 334
         update.setUpdateBy(operator);
300 335
         applyMapper.updateAuditResult(update);
301 336
 
302
-        messageSupport.sendPass(apply.getMemberId(), apply.getApplyId(), apply.getApplyNo());
337
+        if (notifyMember)
338
+        {
339
+            messageSupport.sendPass(apply.getMemberId(), apply.getApplyId(), apply.getApplyNo());
340
+        }
303 341
 
304 342
         Map<String, Object> data = new HashMap<>();
305
-        data.put("merchantId", merchant.getMerchantId());
343
+        data.put("merchantId", merchantId);
306 344
         data.put("shopId", shopId);
307 345
         return data;
308 346
     }
309 347
 
348
+    private Map<String, Object> reconcileWithExistingMerchant(BizMerchantEntryApply apply, BizMerchant merchant,
349
+            EntryApplySubmitDTO submitDto, String operator, boolean createShopFromApply)
350
+    {
351
+        requireMember(apply.getMemberId());
352
+        EntryApplyFormValidator.validateMobileSubmit(submitDto);
353
+
354
+        BizMerchantAccount account = merchantAccountMapper.selectByMerchantId(merchant.getMerchantId());
355
+        if (account == null)
356
+        {
357
+            MerchantCreateDTO bindDto = EntryApplyFormMapper.toMerchantCreateDTO(merchant, apply.getMemberId());
358
+            accountBindService.bindAccountOnMerchantCreate(merchant.getMerchantId(), bindDto, operator);
359
+        }
360
+
361
+        Long shopId = resolveShopForReconcile(apply, merchant, submitDto, operator, createShopFromApply);
362
+        shopFundService.seedContactBankAccountFromEntry(shopId, merchant.getContactName(),
363
+                merchant.getBankAccount(), operator);
364
+
365
+        return markApplyApproved(apply, merchant.getMerchantId(), shopId, operator, true);
366
+    }
367
+
368
+    private Long resolveShopForReconcile(BizMerchantEntryApply apply, BizMerchant merchant,
369
+            EntryApplySubmitDTO submitDto, String operator, boolean createShopFromApply)
370
+    {
371
+        if (apply.getShopId() != null)
372
+        {
373
+            return apply.getShopId();
374
+        }
375
+        String shopName = submitDto.getShop().getShopName();
376
+        if (createShopFromApply)
377
+        {
378
+            Long matchedShopId = findShopIdByNameUnderMerchant(merchant.getMerchantId(), shopName);
379
+            if (matchedShopId != null)
380
+            {
381
+                return matchedShopId;
382
+            }
383
+            if (shopMapper.countByShopName(shopName, null) > 0)
384
+            {
385
+                throw new ServiceException("店铺名称已存在");
386
+            }
387
+            ShopCreateDTO shopDto = EntryApplyFormMapper.toShopCreateDTO(apply.getFormJson(), merchant.getMerchantId());
388
+            return shopService.createShop(shopDto, operator);
389
+        }
390
+
391
+        BizShop earliest = shopMapper.selectEarliestByMerchantId(merchant.getMerchantId());
392
+        if (earliest != null)
393
+        {
394
+            return earliest.getShopId();
395
+        }
396
+        if (shopMapper.countByShopName(shopName, null) > 0)
397
+        {
398
+            throw new ServiceException("店铺名称已存在");
399
+        }
400
+        ShopCreateDTO shopDto = EntryApplyFormMapper.toShopCreateDTO(apply.getFormJson(), merchant.getMerchantId());
401
+        return shopService.createShop(shopDto, operator);
402
+    }
403
+
404
+    private Long findShopIdByNameUnderMerchant(Long merchantId, String shopName)
405
+    {
406
+        if (StringUtils.isEmpty(shopName))
407
+        {
408
+            return null;
409
+        }
410
+        for (MerchantShopBriefVO shop : shopMapper.selectBriefByMerchantId(merchantId))
411
+        {
412
+            if (shopName.equals(shop.getShopName()))
413
+            {
414
+                return shop.getShopId();
415
+            }
416
+        }
417
+        return null;
418
+    }
419
+
420
+    private BizMerchant findMerchantByMemberAdmin(Long memberId)
421
+    {
422
+        if (memberId == null)
423
+        {
424
+            return null;
425
+        }
426
+        BizMerchantAccount account = merchantAccountMapper.selectMerchantAdminByUserId(memberId);
427
+        if (account == null || account.getMerchantId() == null)
428
+        {
429
+            return null;
430
+        }
431
+        return merchantMapper.selectById(account.getMerchantId());
432
+    }
433
+
434
+    private BizMerchant findExistingMerchant(EntryApplySubmitDTO dto, String merchantType)
435
+    {
436
+        if (MerchantConstants.TYPE_PERSON.equals(merchantType) && dto.getSubject() != null
437
+                && StringUtils.isNotEmpty(dto.getSubject().getIdCardNo()))
438
+        {
439
+            return merchantMapper.selectByIdCardNo(dto.getSubject().getIdCardNo());
440
+        }
441
+        if (MerchantConstants.TYPE_ENTERPRISE.equals(merchantType) && dto.getSubject() != null
442
+                && StringUtils.isNotEmpty(dto.getSubject().getCreditCode()))
443
+        {
444
+            return merchantMapper.selectByCreditCode(dto.getSubject().getCreditCode());
445
+        }
446
+        return null;
447
+    }
448
+
449
+    private boolean canReconcileExistingMerchant(BizMerchant merchant, Long memberId)
450
+    {
451
+        BizMerchantAccount account = merchantAccountMapper.selectByMerchantId(merchant.getMerchantId());
452
+        if (account == null)
453
+        {
454
+            return true;
455
+        }
456
+        return memberId != null && memberId.equals(account.getAccountId());
457
+    }
458
+
310 459
     @Override
311 460
     @Transactional(rollbackFor = Exception.class)
312 461
     public void reject(EntryApplyRejectDTO dto, String operator)

+ 4 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/merchant/mapper/BizMerchantMapper.java

@@ -19,6 +19,10 @@ public interface BizMerchantMapper
19 19
 
20 20
     int countByIdCardNo(@Param("idCardNo") String idCardNo, @Param("excludeId") Long excludeId);
21 21
 
22
+    BizMerchant selectByIdCardNo(@Param("idCardNo") String idCardNo);
23
+
24
+    BizMerchant selectByCreditCode(@Param("creditCode") String creditCode);
25
+
22 26
     int countByMerchantName(@Param("merchantName") String merchantName, @Param("excludeId") Long excludeId);
23 27
 
24 28
     int insert(BizMerchant merchant);

+ 5 - 4
baqing-shop/src/main/java/com/ruoyi/web/modules/store/facade/impl/MerchantShopFacadeImpl.java

@@ -3,6 +3,7 @@ package com.ruoyi.web.modules.store.facade.impl;
3 3
 import java.util.List;
4 4
 import org.springframework.beans.factory.annotation.Autowired;
5 5
 import org.springframework.stereotype.Service;
6
+import com.ruoyi.common.exception.ServiceException;
6 7
 import com.ruoyi.web.modules.merchant.facade.IMerchantShopFacade;
7 8
 import com.ruoyi.web.modules.merchant.mapper.BizMerchantMapper;
8 9
 import com.ruoyi.web.modules.merchant.vo.MerchantShopBriefVO;
@@ -29,18 +30,18 @@ public class MerchantShopFacadeImpl implements IMerchantShopFacade
29 30
     @Override
30 31
     public void incrementShopCount(Long merchantId)
31 32
     {
32
-        if (merchantId != null)
33
+        if (merchantId != null && merchantMapper.incrementShopCount(merchantId) <= 0)
33 34
         {
34
-            merchantMapper.incrementShopCount(merchantId);
35
+            throw new ServiceException("更新商户店铺数量失败");
35 36
         }
36 37
     }
37 38
 
38 39
     @Override
39 40
     public void decrementShopCount(Long merchantId)
40 41
     {
41
-        if (merchantId != null)
42
+        if (merchantId != null && merchantMapper.decrementShopCount(merchantId) <= 0)
42 43
         {
43
-            merchantMapper.decrementShopCount(merchantId);
44
+            throw new ServiceException("更新商户店铺数量失败");
44 45
         }
45 46
     }
46 47
 }

+ 4 - 1
baqing-shop/src/main/java/com/ruoyi/web/modules/store/service/impl/ShopServiceImpl.java

@@ -140,7 +140,10 @@ public class ShopServiceImpl implements IShopService
140 140
         shop.setDelFlag(ShopConstants.DEL_NORMAL);
141 141
         shop.setCreateBy(operator);
142 142
         shopMapper.insert(shop);
143
-        merchantShopFacade.incrementShopCount(dto.getMerchantId());
143
+        if (merchantMapper.incrementShopCount(dto.getMerchantId()) <= 0)
144
+        {
145
+            throw new ServiceException("更新商户店铺数量失败");
146
+        }
144 147
         return shop.getShopId();
145 148
     }
146 149
 

+ 14 - 2
baqing-shop/src/main/resources/mapper/merchant/BizMerchantMapper.xml

@@ -115,6 +115,18 @@
115 115
         <if test="excludeId != null">and merchant_id != #{excludeId}</if>
116 116
     </select>
117 117
 
118
+    <select id="selectByIdCardNo" resultMap="BizMerchantResult">
119
+        <include refid="selectVo"/>
120
+        where id_card_no = #{idCardNo} and del_flag = '0'
121
+        limit 1
122
+    </select>
123
+
124
+    <select id="selectByCreditCode" resultMap="BizMerchantResult">
125
+        <include refid="selectVo"/>
126
+        where credit_code = #{creditCode} and del_flag = '0'
127
+        limit 1
128
+    </select>
129
+
118 130
     <select id="countByMerchantName" resultType="int">
119 131
         select count(1) from biz_merchant
120 132
         where merchant_name = #{merchantName} and del_flag = '0'
@@ -215,12 +227,12 @@
215 227
     </update>
216 228
 
217 229
     <update id="incrementShopCount">
218
-        update biz_merchant set shop_count = shop_count + 1, update_time = sysdate()
230
+        update biz_merchant set shop_count = ifnull(shop_count, 0) + 1, update_time = sysdate()
219 231
         where merchant_id = #{merchantId} and del_flag = '0'
220 232
     </update>
221 233
 
222 234
     <update id="decrementShopCount">
223
-        update biz_merchant set shop_count = case when shop_count > 0 then shop_count - 1 else 0 end,
235
+        update biz_merchant set shop_count = case when ifnull(shop_count, 0) > 0 then shop_count - 1 else 0 end,
224 236
             update_time = sysdate()
225 237
         where merchant_id = #{merchantId} and del_flag = '0'
226 238
     </update>

+ 88 - 0
baqing-shop/src/test/java/com/ruoyi/web/modules/entry/service/MerchantEntryApplyServiceImplTest.java

@@ -34,7 +34,10 @@ import com.ruoyi.web.modules.merchant.constant.MerchantConstants;
34 34
 import com.ruoyi.web.modules.merchant.domain.BizMerchant;
35 35
 import com.ruoyi.web.modules.merchant.mapper.BizMerchantMapper;
36 36
 import com.ruoyi.web.modules.merchant.service.IMerchantAccountBindService;
37
+import com.ruoyi.web.modules.store.mapper.BizMerchantAccountMapper;
37 38
 import com.ruoyi.web.modules.store.mapper.BizShopMapper;
39
+import com.ruoyi.web.modules.store.domain.BizMerchantAccount;
40
+import com.ruoyi.web.modules.store.domain.BizShop;
38 41
 import com.ruoyi.web.modules.store.service.IShopService;
39 42
 import com.ruoyi.web.modules.entry.support.MemberEntryMessageSupport;
40 43
 
@@ -53,6 +56,9 @@ class MerchantEntryApplyServiceImplTest
53 56
     @Mock
54 57
     private BizShopMapper shopMapper;
55 58
 
59
+    @Mock
60
+    private BizMerchantAccountMapper merchantAccountMapper;
61
+
56 62
     @Mock
57 63
     private BizMemberMapper memberMapper;
58 64
 
@@ -236,6 +242,88 @@ class MerchantEntryApplyServiceImplTest
236 242
                 eq("admin"));
237 243
     }
238 244
 
245
+    @Test
246
+    void completePublicity_reconcileExistingMerchantForSameMember()
247
+    {
248
+        EntryApplySubmitDTO dto = buildValidPersonDto();
249
+        String formJson = EntryApplyFormMapper.toFormJson(dto);
250
+
251
+        BizMerchantEntryApply apply = new BizMerchantEntryApply();
252
+        apply.setApplyId(2L);
253
+        apply.setApplyNo("EA20260101002");
254
+        apply.setApplyStatus(MerchantEntryApplyConstants.STATUS_PUBLICITY);
255
+        apply.setMerchantType(MerchantConstants.TYPE_PERSON);
256
+        apply.setMemberId(100L);
257
+        apply.setFormJson(formJson);
258
+        apply.setPublicityEndTime(new Date(System.currentTimeMillis() - 1000));
259
+
260
+        BizMerchant existing = new BizMerchant();
261
+        existing.setMerchantId(200L);
262
+        existing.setIdCardNo("110101199001011234");
263
+        existing.setContactName("张三");
264
+        existing.setBankAccount("6222020000000000");
265
+
266
+        BizMerchantAccount account = new BizMerchantAccount();
267
+        account.setMerchantId(200L);
268
+        account.setAccountId(100L);
269
+
270
+        when(applyMapper.selectById(2L)).thenReturn(apply);
271
+        when(memberMapper.selectById(100L)).thenReturn(member);
272
+        when(merchantAccountMapper.selectMerchantAdminByUserId(100L)).thenReturn(account);
273
+        when(merchantMapper.selectById(200L)).thenReturn(existing);
274
+        when(merchantAccountMapper.selectByMerchantId(200L)).thenReturn(account);
275
+        when(shopMapper.selectBriefByMerchantId(200L)).thenReturn(java.util.Collections.emptyList());
276
+        when(shopMapper.countByShopName(any(), eq(null))).thenReturn(0);
277
+        when(shopService.createShop(any(), eq("system"))).thenReturn(301L);
278
+
279
+        applyService.completePublicity(2L, "system");
280
+
281
+        verify(merchantMapper, never()).insert(any());
282
+        verify(shopService).createShop(any(), eq("system"));
283
+        verify(applyMapper).updateAuditResult(any());
284
+        verify(messageSupport).sendPass(eq(100L), eq(2L), eq("EA20260101002"));
285
+    }
286
+
287
+    @Test
288
+    void completePublicity_memberAdminCreatesShopDespiteDuplicateIdCard()
289
+    {
290
+        EntryApplySubmitDTO dto = buildValidPersonDto();
291
+        String formJson = EntryApplyFormMapper.toFormJson(dto);
292
+
293
+        BizMerchantEntryApply apply = new BizMerchantEntryApply();
294
+        apply.setApplyId(2L);
295
+        apply.setApplyNo("EA20260101002");
296
+        apply.setApplyStatus(MerchantEntryApplyConstants.STATUS_PUBLICITY);
297
+        apply.setMerchantType(MerchantConstants.TYPE_PERSON);
298
+        apply.setMemberId(100L);
299
+        apply.setFormJson(formJson);
300
+        apply.setPublicityEndTime(new Date(System.currentTimeMillis() - 1000));
301
+
302
+        BizMerchant adminMerchant = new BizMerchant();
303
+        adminMerchant.setMerchantId(200L);
304
+        adminMerchant.setContactName("张三");
305
+        adminMerchant.setBankAccount("6222020000000000");
306
+
307
+        BizMerchantAccount account = new BizMerchantAccount();
308
+        account.setMerchantId(200L);
309
+        account.setAccountId(100L);
310
+
311
+        when(applyMapper.selectById(2L)).thenReturn(apply);
312
+        when(memberMapper.selectById(100L)).thenReturn(member);
313
+        when(merchantAccountMapper.selectMerchantAdminByUserId(100L)).thenReturn(account);
314
+        when(merchantMapper.selectById(200L)).thenReturn(adminMerchant);
315
+        when(merchantAccountMapper.selectByMerchantId(200L)).thenReturn(account);
316
+        when(shopMapper.selectBriefByMerchantId(200L)).thenReturn(java.util.Collections.emptyList());
317
+        when(shopMapper.countByShopName(any(), eq(null))).thenReturn(0);
318
+        when(shopService.createShop(any(), eq("system"))).thenReturn(301L);
319
+
320
+        applyService.completePublicity(2L, "system");
321
+
322
+        verify(merchantMapper, never()).insert(any());
323
+        verify(merchantMapper, never()).countByIdCardNo(any(), any());
324
+        verify(shopService).createShop(any(), eq("system"));
325
+    }
326
+
239 327
     private EntryApplySubmitDTO buildValidPersonDto()
240 328
     {
241 329
         EntryApplySubmitDTO dto = new EntryApplySubmitDTO();

+ 3 - 1
baqing-shop/src/test/java/com/ruoyi/web/modules/store/service/ShopServiceImplTest.java

@@ -86,13 +86,14 @@ class ShopServiceImplTest
86 86
             s.setShopId(10L);
87 87
             return 1;
88 88
         });
89
+        when(merchantMapper.incrementShopCount(1L)).thenReturn(1);
89 90
 
90 91
         Long shopId = shopService.createShop(dto, "admin");
91 92
 
92 93
         assertEquals(10L, shopId);
93 94
         verify(accountMapper, never()).insert(any());
94 95
         verify(shopMapper).insert(any(BizShop.class));
95
-        verify(merchantShopFacade).incrementShopCount(1L);
96
+        verify(merchantMapper).incrementShopCount(1L);
96 97
     }
97 98
 
98 99
     @Test
@@ -129,6 +130,7 @@ class ShopServiceImplTest
129 130
             assertEquals(ShopConstants.STATUS_OPEN, s.getShopStatus());
130 131
             return 1;
131 132
         });
133
+        when(merchantMapper.incrementShopCount(1L)).thenReturn(1);
132 134
 
133 135
         shopService.createShop(dto, "admin");
134 136
 

+ 3 - 4
sql/biz_shop.sql

@@ -1,19 +1,17 @@
1 1
 -- =============================================================================
2 2
 -- 商户经营账号 biz_merchant_account
3
+-- 用途:商家端登录账号,一商户一商户管理员(shop_id=NULL)
3 4
 -- 创建时机:平台新增商户绑定时 / C端入驻公示完成后绑定申请会员/商户管理员添加员工账号时
4 5
 -- =============================================================================
5 6
 CREATE TABLE IF NOT EXISTS `biz_merchant_account` (
6 7
   `account_id` bigint(20) NOT NULL COMMENT '经营账号ID,与 sys_user.user_id 一致',
7
-  `merchant_id` bigint(20) NOT NULL COMMENT '所属商户ID(biz_merchant.merchant_id);一商户一条(del_flag=0)',
8
+  `merchant_id` bigint(20) NOT NULL COMMENT '所属商户ID(biz_merchant.merchant_id)',
8 9
   `shop_id` bigint(20) DEFAULT NULL COMMENT '店铺ID;NULL=商户管理员',
9 10
   `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志:0存在 2逻辑删除(末店删除后一般仍保留)',
10 11
   `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
11 12
   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
12 13
   `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
13
-  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
14
-  PRIMARY KEY (`account_id`),
15
-  KEY `idx_merchant_id` (`merchant_id`,`del_flag`)
14
+  `update_time` datetime DEFAULT NULL COMMENT '更新时间'
16 15
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户经营账号(商家端登录,一商户一套)';
17 16
 
18 17
 -- =============================================================================