|
|
@@ -0,0 +1,267 @@
|
|
|
+package com.ruoyi.app.user;
|
|
|
+
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountCreateRequest;
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountPasswordRequest;
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountStatusRequest;
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountStoreView;
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountUpdateRequest;
|
|
|
+import com.ruoyi.app.user.dto.MerchantSubaccountView;
|
|
|
+import com.ruoyi.common.exception.ServiceException;
|
|
|
+import com.ruoyi.common.utils.MessageUtils;
|
|
|
+import com.ruoyi.system.domain.InfoUser;
|
|
|
+import com.ruoyi.system.domain.PosStore;
|
|
|
+import com.ruoyi.system.domain.constants.MerchantAccountConstants;
|
|
|
+import com.ruoyi.system.mapper.PosStoreMapper;
|
|
|
+import com.ruoyi.system.service.IInfoUserService;
|
|
|
+import com.ruoyi.system.service.IMerchantSubaccountStoreService;
|
|
|
+import com.ruoyi.system.service.MerchantStoreAccessService;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.LinkedHashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class MerchantSubaccountApplicationService {
|
|
|
+
|
|
|
+ private final IInfoUserService infoUserService;
|
|
|
+ private final IMerchantSubaccountStoreService relationService;
|
|
|
+ private final MerchantStoreAccessService accessService;
|
|
|
+ private final PosStoreMapper posStoreMapper;
|
|
|
+ private final MerchantTokenSessionService tokenSessionService;
|
|
|
+
|
|
|
+ public MerchantSubaccountApplicationService(IInfoUserService infoUserService,
|
|
|
+ IMerchantSubaccountStoreService relationService,
|
|
|
+ MerchantStoreAccessService accessService,
|
|
|
+ PosStoreMapper posStoreMapper,
|
|
|
+ MerchantTokenSessionService tokenSessionService) {
|
|
|
+ this.infoUserService = infoUserService;
|
|
|
+ this.relationService = relationService;
|
|
|
+ this.accessService = accessService;
|
|
|
+ this.posStoreMapper = posStoreMapper;
|
|
|
+ this.tokenSessionService = tokenSessionService;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<MerchantSubaccountView> listForOwner(Long ownerUserId) {
|
|
|
+ accessService.requireOwner(ownerUserId);
|
|
|
+ return listOwnedSubaccounts(ownerUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<MerchantSubaccountView> listForPlatform(Long ownerUserId) {
|
|
|
+ InfoUser owner = infoUserService.selectInfoUserByUserId(ownerUserId);
|
|
|
+ if (owner == null || !MerchantAccountConstants.OWNER_USER_TYPE.equals(owner.getUserType())
|
|
|
+ || !MerchantAccountConstants.NOT_DELETED.equals(owner.getDelFlag())) {
|
|
|
+ throw error("merchant.owner.not.found");
|
|
|
+ }
|
|
|
+ return listOwnedSubaccounts(ownerUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public MerchantSubaccountView create(Long ownerUserId, MerchantSubaccountCreateRequest request) {
|
|
|
+ accessService.requireOwner(ownerUserId);
|
|
|
+ if (request == null) {
|
|
|
+ throw error("merchant.subaccount.request.required");
|
|
|
+ }
|
|
|
+ String phone = normalizePhone(request.getPhone());
|
|
|
+ String name = normalizeRequired(request.getName(), "merchant.subaccount.name.required");
|
|
|
+ String password = normalizeRequired(request.getPassword(), "merchant.subaccount.password.required");
|
|
|
+ List<Long> storeIds = validateStores(ownerUserId, request.getStoreIds());
|
|
|
+ if (infoUserService.getinfouserName(phone) != null || infoUserService.getinfoPhone(phone) != null) {
|
|
|
+ throw error("merchant.subaccount.phone.exists");
|
|
|
+ }
|
|
|
+
|
|
|
+ InfoUser user = new InfoUser();
|
|
|
+ user.setUserName(phone);
|
|
|
+ user.setPhone(phone);
|
|
|
+ user.setNickName(name);
|
|
|
+ user.setPassword(password);
|
|
|
+ user.setUserType(MerchantAccountConstants.SUBACCOUNT_USER_TYPE);
|
|
|
+ user.setMerchantOwnerId(ownerUserId);
|
|
|
+ user.setStatus(MerchantAccountConstants.STATUS_ENABLED);
|
|
|
+ user.setSubaccountStatus(MerchantAccountConstants.STATUS_ENABLED);
|
|
|
+ user.setAuditStatus("1");
|
|
|
+ user.setDelFlag(MerchantAccountConstants.NOT_DELETED);
|
|
|
+ if (infoUserService.insertInfoUser(user) != 1 || user.getUserId() == null) {
|
|
|
+ throw error("merchant.subaccount.create.failed");
|
|
|
+ }
|
|
|
+ relationService.replaceRelations(user.getUserId(), storeIds);
|
|
|
+ return toView(user, ownerUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public MerchantSubaccountView update(Long ownerUserId, Long subaccountUserId,
|
|
|
+ MerchantSubaccountUpdateRequest request) {
|
|
|
+ accessService.requireOwner(ownerUserId);
|
|
|
+ InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
|
|
|
+ if (request == null) {
|
|
|
+ throw error("merchant.subaccount.request.required");
|
|
|
+ }
|
|
|
+ String name = normalizeRequired(request.getName(), "merchant.subaccount.name.required");
|
|
|
+ List<Long> storeIds = validateStores(ownerUserId, request.getStoreIds());
|
|
|
+
|
|
|
+ InfoUser update = new InfoUser();
|
|
|
+ update.setUserId(target.getUserId());
|
|
|
+ update.setNickName(name);
|
|
|
+ if (infoUserService.updateInfoUser(update) != 1) {
|
|
|
+ throw error("merchant.subaccount.update.failed");
|
|
|
+ }
|
|
|
+ relationService.replaceRelations(target.getUserId(), storeIds);
|
|
|
+ target.setNickName(name);
|
|
|
+ return toView(target, ownerUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updatePassword(Long ownerUserId, Long subaccountUserId,
|
|
|
+ MerchantSubaccountPasswordRequest request) {
|
|
|
+ accessService.requireOwner(ownerUserId);
|
|
|
+ InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
|
|
|
+ String password = normalizeRequired(request == null ? null : request.getPassword(),
|
|
|
+ "merchant.subaccount.password.required");
|
|
|
+ InfoUser update = new InfoUser();
|
|
|
+ update.setUserId(target.getUserId());
|
|
|
+ update.setPassword(password);
|
|
|
+ if (infoUserService.updateInfoUser(update) != 1) {
|
|
|
+ throw error("merchant.subaccount.update.failed");
|
|
|
+ }
|
|
|
+ tokenSessionService.revokeAll(target.getUserId());
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updateOwnerStatus(Long ownerUserId, Long subaccountUserId,
|
|
|
+ MerchantSubaccountStatusRequest request) {
|
|
|
+ accessService.requireOwner(ownerUserId);
|
|
|
+ InfoUser target = requireOwnedSubaccount(ownerUserId, subaccountUserId);
|
|
|
+ boolean enabled = requireEnabled(request);
|
|
|
+ InfoUser update = new InfoUser();
|
|
|
+ update.setUserId(target.getUserId());
|
|
|
+ update.setSubaccountStatus(enabled
|
|
|
+ ? MerchantAccountConstants.STATUS_ENABLED : MerchantAccountConstants.STATUS_DISABLED);
|
|
|
+ if (infoUserService.updateInfoUser(update) != 1) {
|
|
|
+ throw error("merchant.subaccount.update.failed");
|
|
|
+ }
|
|
|
+ if (!enabled) {
|
|
|
+ tokenSessionService.revokeAll(target.getUserId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void updatePlatformStatus(Long subaccountUserId, MerchantSubaccountStatusRequest request) {
|
|
|
+ InfoUser target = requireSubaccount(subaccountUserId);
|
|
|
+ boolean enabled = requireEnabled(request);
|
|
|
+ InfoUser update = new InfoUser();
|
|
|
+ update.setUserId(target.getUserId());
|
|
|
+ update.setStatus(enabled
|
|
|
+ ? MerchantAccountConstants.STATUS_ENABLED : MerchantAccountConstants.STATUS_DISABLED);
|
|
|
+ if (infoUserService.updateInfoUser(update) != 1) {
|
|
|
+ throw error("merchant.subaccount.update.failed");
|
|
|
+ }
|
|
|
+ if (!enabled) {
|
|
|
+ tokenSessionService.revokeAll(target.getUserId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<MerchantSubaccountView> listOwnedSubaccounts(Long ownerUserId) {
|
|
|
+ InfoUser filter = new InfoUser();
|
|
|
+ filter.setUserType(MerchantAccountConstants.SUBACCOUNT_USER_TYPE);
|
|
|
+ filter.setMerchantOwnerId(ownerUserId);
|
|
|
+ filter.setDelFlag(MerchantAccountConstants.NOT_DELETED);
|
|
|
+ return infoUserService.selectInfoUserList(filter).stream()
|
|
|
+ .map(user -> toView(user, ownerUserId))
|
|
|
+ .toList();
|
|
|
+ }
|
|
|
+
|
|
|
+ private MerchantSubaccountView toView(InfoUser user, Long ownerUserId) {
|
|
|
+ MerchantSubaccountView view = new MerchantSubaccountView();
|
|
|
+ view.setUserId(user.getUserId());
|
|
|
+ view.setName(firstNonBlank(user.getNickName(), user.getFullName(), user.getUserName()));
|
|
|
+ view.setPhone(user.getPhone());
|
|
|
+ view.setMerchantOwnerId(ownerUserId);
|
|
|
+ view.setOwnerEnabled(MerchantAccountConstants.STATUS_ENABLED.equals(user.getSubaccountStatus()));
|
|
|
+ view.setPlatformEnabled(MerchantAccountConstants.STATUS_ENABLED.equals(user.getStatus()));
|
|
|
+ view.setOnline(tokenSessionService.isOnline(user.getUserId()));
|
|
|
+ view.setLastLoginAt(user.getLastLoginAt());
|
|
|
+ view.setCreatedAt(user.getCreatedAt());
|
|
|
+ List<MerchantSubaccountStoreView> stores = new ArrayList<>();
|
|
|
+ for (Long storeId : relationService.selectStoreIdsBySubaccountUserId(user.getUserId())) {
|
|
|
+ PosStore store = posStoreMapper.selectPosStoreById(storeId);
|
|
|
+ if (store != null && Objects.equals(ownerUserId, store.getUserId())
|
|
|
+ && MerchantAccountConstants.NOT_DELETED.equals(store.getDelFlag())) {
|
|
|
+ stores.add(new MerchantSubaccountStoreView(storeId, store.getPosName()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ view.setStores(stores);
|
|
|
+ return view;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Long> validateStores(Long ownerUserId, List<Long> requested) {
|
|
|
+ if (requested == null || requested.isEmpty()) {
|
|
|
+ throw error("merchant.subaccount.store.required");
|
|
|
+ }
|
|
|
+ Set<Long> storeIds = new LinkedHashSet<>();
|
|
|
+ for (Long storeId : requested) {
|
|
|
+ if (storeId == null) {
|
|
|
+ throw error("merchant.store.access.denied");
|
|
|
+ }
|
|
|
+ storeIds.add(storeId);
|
|
|
+ }
|
|
|
+ if (!accessService.getAccessibleStoreIds(ownerUserId).containsAll(storeIds)) {
|
|
|
+ throw error("merchant.store.access.denied");
|
|
|
+ }
|
|
|
+ return List.copyOf(storeIds);
|
|
|
+ }
|
|
|
+
|
|
|
+ private InfoUser requireOwnedSubaccount(Long ownerUserId, Long subaccountUserId) {
|
|
|
+ InfoUser target = requireSubaccount(subaccountUserId);
|
|
|
+ if (!Objects.equals(ownerUserId, target.getMerchantOwnerId())) {
|
|
|
+ throw error("merchant.subaccount.access.denied");
|
|
|
+ }
|
|
|
+ return target;
|
|
|
+ }
|
|
|
+
|
|
|
+ private InfoUser requireSubaccount(Long subaccountUserId) {
|
|
|
+ InfoUser target = subaccountUserId == null ? null
|
|
|
+ : infoUserService.selectInfoUserByUserId(subaccountUserId);
|
|
|
+ if (target == null
|
|
|
+ || !MerchantAccountConstants.SUBACCOUNT_USER_TYPE.equals(target.getUserType())
|
|
|
+ || !MerchantAccountConstants.NOT_DELETED.equals(target.getDelFlag())) {
|
|
|
+ throw error("merchant.subaccount.not.found");
|
|
|
+ }
|
|
|
+ return target;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean requireEnabled(MerchantSubaccountStatusRequest request) {
|
|
|
+ if (request == null || request.getEnabled() == null) {
|
|
|
+ throw error("merchant.subaccount.status.required");
|
|
|
+ }
|
|
|
+ return request.getEnabled();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String normalizePhone(String value) {
|
|
|
+ String phone = normalizeRequired(value, "merchant.subaccount.phone.required")
|
|
|
+ .replaceAll("\\s+", "");
|
|
|
+ if (phone.length() > 32) {
|
|
|
+ throw error("merchant.subaccount.phone.invalid");
|
|
|
+ }
|
|
|
+ return phone;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String normalizeRequired(String value, String key) {
|
|
|
+ if (value == null || value.isBlank()) {
|
|
|
+ throw error(key);
|
|
|
+ }
|
|
|
+ return value.strip();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String firstNonBlank(String... values) {
|
|
|
+ for (String value : values) {
|
|
|
+ if (value != null && !value.isBlank()) {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ private ServiceException error(String key) {
|
|
|
+ return new ServiceException(MessageUtils.message(key));
|
|
|
+ }
|
|
|
+}
|