From 754af2f5408fd941e1fa86848935cb2f7f108bea Mon Sep 17 00:00:00 2001 From: irving Date: Fri, 31 Oct 2025 22:49:09 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=A6=BB=E8=81=8C?= =?UTF-8?q?=E5=BA=97=E5=91=98=E7=99=BB=E5=BD=95=E4=B8=8E=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=EF=BC=8C=E4=BC=98=E5=8C=96=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E4=B8=8E=E5=BE=AE=E4=BF=A1=E6=B5=81=E7=A8=8B=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=BA=97=E5=91=98=E7=8A=B6=E6=80=81=E4=B8=8E=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClerkUserLoginAspect: 修复登录拦截,禁止离职/禁用店员继续访问 IPlayClerkUserInfoService/Impl: 调整权限校验与查询逻辑 PlayOrderInfoServiceImpl: 订单创建/生命周期兼容店员状态 WxOauthController/WxCustomController/WxOauthService/WxCustomMpService: 完善 OAuth 与消息处理流程 新增枚举: ClerkRoleStatus、ListingStatus、OnboardingStatus --- .../common/aspect/ClerkUserLoginAspect.java | 7 ++ .../clerk/module/enums/ClerkRoleStatus.java | 50 +++++++++++ .../clerk/module/enums/ListingStatus.java | 54 +++++++++++ .../clerk/module/enums/OnboardingStatus.java | 54 +++++++++++ .../service/IPlayClerkUserInfoService.java | 24 +++++ .../impl/PlayClerkUserInfoServiceImpl.java | 89 +++++++++++++++++-- .../impl/PlayOrderInfoServiceImpl.java | 2 + .../controller/WxCustomController.java | 7 +- .../weichat/controller/WxOauthController.java | 9 ++ .../weichat/service/WxCustomMpService.java | 17 ++-- .../weichat/service/WxOauthService.java | 46 ++++++++-- 11 files changed, 339 insertions(+), 20 deletions(-) create mode 100644 play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ClerkRoleStatus.java create mode 100644 play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ListingStatus.java create mode 100644 play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/OnboardingStatus.java diff --git a/play-admin/src/main/java/com/starry/admin/common/aspect/ClerkUserLoginAspect.java b/play-admin/src/main/java/com/starry/admin/common/aspect/ClerkUserLoginAspect.java index 0c493ef..0fcc47b 100644 --- a/play-admin/src/main/java/com/starry/admin/common/aspect/ClerkUserLoginAspect.java +++ b/play-admin/src/main/java/com/starry/admin/common/aspect/ClerkUserLoginAspect.java @@ -1,6 +1,7 @@ package com.starry.admin.common.aspect; import com.starry.admin.common.conf.ThreadLocalRequestDetail; +import com.starry.admin.common.exception.CustomException; import com.starry.admin.common.exception.ServiceException; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; import com.starry.admin.modules.clerk.service.impl.PlayClerkUserInfoServiceImpl; @@ -56,6 +57,12 @@ public class ClerkUserLoginAspect { if (Objects.isNull(entity)) { throw new ServiceException("未查询到有效用户", HttpStatus.UNAUTHORIZED); } + try { + clerkUserInfoService.ensureClerkSessionIsValid(entity); + } catch (CustomException e) { + log.warn("Clerk token rejected due to status change, clerkId={} message={}", entity.getId(), e.getMessage()); + throw new ServiceException(e.getMessage(), HttpStatus.UNAUTHORIZED); + } if (!userToken.equals(entity.getToken())) { throw new ServiceException("token异常", HttpStatus.UNAUTHORIZED); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ClerkRoleStatus.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ClerkRoleStatus.java new file mode 100644 index 0000000..4b514d7 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ClerkRoleStatus.java @@ -0,0 +1,50 @@ +package com.starry.admin.modules.clerk.module.enums; + +import cn.hutool.core.util.StrUtil; + +/** + * 员工状态【1:是陪聊,0:不是陪聊】 + */ +public enum ClerkRoleStatus { + CLERK("1"), + NON_CLERK("0"); + + private final String code; + + ClerkRoleStatus(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public boolean matches(String value) { + return StrUtil.equals(code, value); + } + + public boolean isClerk() { + return this == CLERK; + } + + public static ClerkRoleStatus fromCode(String value) { + if (CLERK.matches(value)) { + return CLERK; + } + if (NON_CLERK.matches(value)) { + return NON_CLERK; + } + return NON_CLERK; + } + + public static boolean isClerk(String value) { + return fromCode(value).isClerk(); + } + + public static boolean transitionedToNonClerk(String newValue, String originalValue) { + if (StrUtil.isBlank(newValue)) { + return false; + } + return !isClerk(newValue) && isClerk(originalValue); + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ListingStatus.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ListingStatus.java new file mode 100644 index 0000000..47b1461 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/ListingStatus.java @@ -0,0 +1,54 @@ +package com.starry.admin.modules.clerk.module.enums; + +import cn.hutool.core.util.StrUtil; + +/** + * 上架状态【1:上架,0:下架】 + */ +public enum ListingStatus { + LISTED("1"), + DELISTED("0"); + + private final String code; + + ListingStatus(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public boolean matches(String value) { + return StrUtil.equals(code, value); + } + + public boolean isListed() { + return this == LISTED; + } + + public static ListingStatus fromCode(String value) { + if (LISTED.matches(value)) { + return LISTED; + } + if (DELISTED.matches(value)) { + return DELISTED; + } + return LISTED; + } + + public static boolean isListed(String value) { + return fromCode(value).isListed(); + } + + public static boolean isDelisted(String value) { + return !isListed(value); + } + + public static boolean transitionedToDelisted(String newValue, String originalValue) { + if (StrUtil.isBlank(newValue)) { + return false; + } + return isDelisted(newValue) && !isDelisted(originalValue); + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/OnboardingStatus.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/OnboardingStatus.java new file mode 100644 index 0000000..12abdb7 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/module/enums/OnboardingStatus.java @@ -0,0 +1,54 @@ +package com.starry.admin.modules.clerk.module.enums; + +import cn.hutool.core.util.StrUtil; + +/** + * 在职状态(1:在职,0:离职) + */ +public enum OnboardingStatus { + ACTIVE("1"), + OFFBOARDED("0"); + + private final String code; + + OnboardingStatus(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public boolean matches(String value) { + return StrUtil.equals(code, value); + } + + public boolean isActive() { + return this == ACTIVE; + } + + public static OnboardingStatus fromCode(String value) { + if (ACTIVE.matches(value)) { + return ACTIVE; + } + if (OFFBOARDED.matches(value)) { + return OFFBOARDED; + } + return ACTIVE; + } + + public static boolean isActive(String value) { + return fromCode(value).isActive(); + } + + public static boolean isOffboarded(String value) { + return !isActive(value); + } + + public static boolean transitionedToOffboarded(String newValue, String originalValue) { + if (StrUtil.isBlank(newValue)) { + return false; + } + return isOffboarded(newValue) && !isOffboarded(originalValue); + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/IPlayClerkUserInfoService.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/IPlayClerkUserInfoService.java index 14a4d29..6507fe7 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/IPlayClerkUserInfoService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/IPlayClerkUserInfoService.java @@ -190,6 +190,30 @@ public interface IPlayClerkUserInfoService extends IService selectPlayClerkUserInfoByPage(PlayClerkUserInfoQueryVo vo); + /** + * 确认店员处于可用状态,否则抛出异常 + * + * @param clerkUserInfoEntity + * 店员信息 + */ + void ensureClerkIsActive(PlayClerkUserInfoEntity clerkUserInfoEntity); + + /** + * 确认店员登录态仍然有效(未被离职/下架/删除),否则抛出异常 + * + * @param clerkUserInfoEntity + * 店员信息 + */ + void ensureClerkSessionIsValid(PlayClerkUserInfoEntity clerkUserInfoEntity); + + /** + * 使店员当前登录态失效 + * + * @param clerkId + * 店员ID + */ + void invalidateClerkSession(String clerkId); + /** * 新增店员 * diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java index 49e2851..6152dbe 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java @@ -3,7 +3,9 @@ package com.starry.admin.modules.clerk.service.impl; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.yulichang.wrapper.MPJLambdaWrapper; @@ -18,6 +20,9 @@ import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserQueryVo; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserReturnVo; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserReviewInfoEntity; +import com.starry.admin.modules.clerk.module.enums.ClerkRoleStatus; +import com.starry.admin.modules.clerk.module.enums.ListingStatus; +import com.starry.admin.modules.clerk.module.enums.OnboardingStatus; import com.starry.admin.modules.clerk.module.vo.PlayClerkCommodityQueryVo; import com.starry.admin.modules.clerk.module.vo.PlayClerkUnsettledWagesInfoQueryVo; import com.starry.admin.modules.clerk.module.vo.PlayClerkUnsettledWagesInfoReturnVo; @@ -67,6 +72,10 @@ import org.springframework.stereotype.Service; public class PlayClerkUserInfoServiceImpl extends ServiceImpl implements IPlayClerkUserInfoService { + + private static final String OFFBOARD_MESSAGE = "你已离职,需要复职请联系店铺管理员"; + private static final String DELISTED_MESSAGE = "你已被下架,没有权限访问"; + private static final String INVALID_CLERK_MESSAGE = "你不是有效店员,无法执行该操作"; @Resource private PlayClerkUserInfoMapper playClerkUserInfoMapper; @Resource @@ -179,13 +188,13 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl wrapper = Wrappers.lambdaUpdate(PlayClerkUserInfoEntity.class) + .eq(PlayClerkUserInfoEntity::getId, clerkId) + .set(PlayClerkUserInfoEntity::getToken, "empty") + .set(PlayClerkUserInfoEntity::getOnlineState, "0"); + this.baseMapper.update(null, wrapper); + } + @Override public void updateTokenById(String id, String token) { PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity(); @@ -297,7 +346,7 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl listAll() { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); - lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getClerkState, "1"); + lambdaQueryWrapper.eq(PlayClerkUserInfoEntity::getClerkState, ClerkRoleStatus.CLERK.getCode()); return this.baseMapper.selectList(lambdaQueryWrapper); } @@ -308,7 +357,7 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl clerkIdList = playClerkGroupInfoService.getValidClerkIdList(SecurityUtils.getLoginUser(), null); lambdaQueryWrapper.in(PlayClerkUserInfoEntity::getId, clerkIdList); @@ -500,7 +549,19 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl selectRandomOrderByPage(PlayOrderInfoRandomQueryVo vo, String clerkId) { PlayClerkUserInfoEntity entity = playClerkUserInfoService.getById(clerkId); + playClerkUserInfoService.ensureClerkIsActive(entity); LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "1"); lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.PENDING.getCode()); @@ -561,6 +562,7 @@ public class PlayOrderInfoServiceImpl extends ServiceImpl clerkList = clerkUserInfoService.list(Wrappers.lambdaQuery(PlayClerkUserInfoEntity.class) .isNotNull(PlayClerkUserInfoEntity::getOpenid) - .eq(PlayClerkUserInfoEntity::getClerkState, "1") + .eq(PlayClerkUserInfoEntity::getClerkState, ClerkRoleStatus.CLERK.getCode()) + .eq(PlayClerkUserInfoEntity::getOnboardingState, OnboardingStatus.ACTIVE.getCode()) + .eq(PlayClerkUserInfoEntity::getListingState, ListingStatus.LISTED.getCode()) .eq(PlayClerkUserInfoEntity::getOnlineState, "1") .eq(PlayClerkUserInfoEntity::getSex, vo.getSex())); wxCustomMpService.sendCreateOrderMessageBatch(clerkList, orderNo, netAmount.toString(), commodityInfo.getCommodityName(),order.getId()); diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxOauthController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxOauthController.java index 98ecdb2..752bc0d 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxOauthController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxOauthController.java @@ -105,6 +105,7 @@ public class WxOauthController { if (entity == null) { throw new CustomException("用户不存在"); } + clerkUserInfoService.ensureClerkSessionIsValid(entity); // 缓存租户信息 String redisKey = "TENANT_INFO:" + entity.getId(); redisCache.setCacheObject(redisKey, entity.getTenantId()); @@ -116,6 +117,9 @@ public class WxOauthController { jsonObject.put("pcData", clerkUserInfoService.getPcData(entity)); clerkUserInfoService.updateTokenById(entity.getId(), tokenValue); return R.ok(jsonObject); + } catch (CustomException e) { + log.warn("店员登录失败,code={}, message={}", vo.getCode(), e.getMessage()); + return R.unauthorized().message(e.getMessage()); } catch (Exception e) { log.error("顾客登录失败,", e); return R.unauthorized(); @@ -133,6 +137,7 @@ public class WxOauthController { if (entity == null) { throw new CustomException("用户不存在"); } + clerkUserInfoService.ensureClerkSessionIsValid(entity); // 缓存租户信息 String redisKey = "TENANT_INFO:" + entity.getId(); redisCache.setCacheObject(redisKey, entity.getTenantId()); @@ -144,6 +149,9 @@ public class WxOauthController { jsonObject.put("pcData", clerkUserInfoService.getPcData(entity)); clerkUserInfoService.updateTokenById(entity.getId(), tokenValue); return R.ok(jsonObject); + } catch (CustomException e) { + log.warn("店员开发登录失败,message={}", e.getMessage()); + return R.unauthorized().message(e.getMessage()); } catch (Exception e) { log.error("顾客登录失败,", e); return R.unauthorized(); @@ -159,6 +167,7 @@ public class WxOauthController { if (entity == null) { throw new CustomException("用户不存在"); } + clerkUserInfoService.ensureClerkIsActive(entity); // 缓存租户信息 String redisKey = "TENANT_INFO:" + entity.getId(); redisCache.setCacheObject(redisKey, entity.getTenantId()); diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java index 0a65f27..3a3eac2 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java @@ -12,6 +12,8 @@ import com.starry.admin.common.exception.CustomException; import com.starry.admin.common.exception.ServiceException; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserReviewInfoEntity; +import com.starry.admin.modules.clerk.module.enums.ListingStatus; +import com.starry.admin.modules.clerk.module.enums.OnboardingStatus; import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService; @@ -146,13 +148,16 @@ public class WxCustomMpService { } public void sendCreateOrderMessageBatch(List clerkList, String orderNo, String string, String commodityName, String orderId) { - if (CollectionUtils.isEmpty(clerkList)) return; + if (CollectionUtils.isEmpty(clerkList)) { + return; + } - executor.execute(() -> { - clerkList.parallelStream().forEach(ca -> { - sendCreateOrderMessage(ca.getTenantId(), ca.getOpenid(), orderNo, string, commodityName, orderId); - }); - }); + executor.execute(() -> clerkList.parallelStream() + .filter(Objects::nonNull) + .filter(ca -> OnboardingStatus.isActive(ca.getOnboardingState())) + .filter(ca -> ListingStatus.isListed(ca.getListingState())) + .forEach(ca -> sendCreateOrderMessage(ca.getTenantId(), ca.getOpenid(), orderNo, string, commodityName, + orderId))); } /** diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxOauthService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxOauthService.java index 013c1e2..04a6bc3 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxOauthService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxOauthService.java @@ -7,6 +7,9 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.starry.admin.common.exception.ServiceException; import com.starry.admin.common.oss.service.IOssFileService; import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity; +import com.starry.admin.modules.clerk.module.enums.ClerkRoleStatus; +import com.starry.admin.modules.clerk.module.enums.ListingStatus; +import com.starry.admin.modules.clerk.module.enums.OnboardingStatus; import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService; import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; @@ -72,15 +75,46 @@ public class WxOauthService { entity.setWeiChatAvatar(entity.getAvatar()); entity.setId(IdUtils.getUuid()); entity.setLevelId(playClerkLevelInfoService.getDefaultLevel().getId()); + entity.setClerkState(ClerkRoleStatus.NON_CLERK.getCode()); + entity.setOnboardingState(OnboardingStatus.ACTIVE.getCode()); + entity.setListingState(ListingStatus.LISTED.getCode()); clerkUserInfoService.create(entity); return entity.getId(); - } else { - if (StrUtil.isEmpty(item.getAvatar())) { - clerkUserInfoService.update(Wrappers.lambdaUpdate(PlayClerkUserInfoEntity.class).eq(PlayClerkUserInfoEntity::getId, item.getId()) - .set(PlayClerkUserInfoEntity::getAvatar, generateAvatar(userInfo.getHeadImgUrl()))); - } - return item.getId(); } + + if (Boolean.TRUE.equals(item.getDeleted())) { + clerkUserInfoService.update(null, Wrappers.lambdaUpdate(PlayClerkUserInfoEntity.class) + .eq(PlayClerkUserInfoEntity::getId, item.getId()) + .set(PlayClerkUserInfoEntity::getDeleted, Boolean.FALSE) + .set(StrUtil.isBlank(item.getOnboardingState()), PlayClerkUserInfoEntity::getOnboardingState, + OnboardingStatus.ACTIVE.getCode()) + .set(StrUtil.isBlank(item.getListingState()), PlayClerkUserInfoEntity::getListingState, + ListingStatus.LISTED.getCode()) + .set(StrUtil.isBlank(item.getClerkState()), PlayClerkUserInfoEntity::getClerkState, + ClerkRoleStatus.NON_CLERK.getCode()) + .set(PlayClerkUserInfoEntity::getToken, "empty") + .set(PlayClerkUserInfoEntity::getOnlineState, "0")); + item.setDeleted(false); + if (StrUtil.isBlank(item.getOnboardingState())) { + item.setOnboardingState(OnboardingStatus.ACTIVE.getCode()); + } + if (StrUtil.isBlank(item.getListingState())) { + item.setListingState(ListingStatus.LISTED.getCode()); + } + if (StrUtil.isBlank(item.getClerkState())) { + item.setClerkState(ClerkRoleStatus.NON_CLERK.getCode()); + } + item.setToken("empty"); + item.setOnlineState("0"); + } + + if (StrUtil.isEmpty(item.getAvatar())) { + clerkUserInfoService.update(Wrappers.lambdaUpdate(PlayClerkUserInfoEntity.class) + .eq(PlayClerkUserInfoEntity::getId, item.getId()) + .set(PlayClerkUserInfoEntity::getAvatar, generateAvatar(userInfo.getHeadImgUrl()))); + } + clerkUserInfoService.ensureClerkSessionIsValid(item); + return item.getId(); } private String generateAvatar(String imageUrl) {