|
|
@@ -3,6 +3,8 @@ package com.etotem.cfc.service;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.entity.SupplyHierarchy;
|
|
|
import com.etotem.cfc.entity.SupplySystem;
|
|
|
import com.etotem.cfc.entity.SupplySystemMember;
|
|
|
import com.etotem.cfc.entity.User;
|
|
|
@@ -14,7 +16,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Random;
|
|
|
import com.etotem.cfc.util.SortUtil;
|
|
|
|
|
|
@Service
|
|
|
@@ -26,6 +31,9 @@ public class SupplySystemService extends ServiceImpl<SupplySystemMapper, SupplyS
|
|
|
@Resource
|
|
|
private UserMapper userMapper;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private SupplyHierarchyService supplyHierarchyService;
|
|
|
+
|
|
|
public Page<SupplySystem> list(String keyword, String status, int page, int size) {
|
|
|
Page<SupplySystem> pageParam = new Page<>(page, size);
|
|
|
LambdaQueryWrapper<SupplySystem> wrapper = new LambdaQueryWrapper<>();
|
|
|
@@ -202,4 +210,182 @@ public class SupplySystemService extends ServiceImpl<SupplySystemMapper, SupplyS
|
|
|
member.setRole(role);
|
|
|
return supplySystemMemberMapper.updateById(member) > 0;
|
|
|
}
|
|
|
+
|
|
|
+ // ========== 团队邀请功能(T38,2026-11-02) ==========
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成/获取我的邀请码(幂等:已生成复用,refresh=true 重置旧码使旧码失效)。
|
|
|
+ * 仅体系 admin/active 成员可生成;返回邀请码 + 团队名(供分享文案)。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public Result<Map<String, Object>> generateInviteCode(Long systemId, Long userId, boolean refresh) {
|
|
|
+ if (systemId == null) {
|
|
|
+ return Result.error("systemId不能为空");
|
|
|
+ }
|
|
|
+ if (userId == null) {
|
|
|
+ return Result.error("未登录");
|
|
|
+ }
|
|
|
+ if (!isMemberOrAdmin(systemId, userId)) {
|
|
|
+ return Result.error("您不是该团队成员,无法生成邀请码");
|
|
|
+ }
|
|
|
+ SupplySystem system = this.getById(systemId);
|
|
|
+ if (system == null) {
|
|
|
+ return Result.error("团队不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ SupplySystemMember member = supplySystemMemberMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getSystemId, systemId)
|
|
|
+ .eq(SupplySystemMember::getUserId, userId));
|
|
|
+ if (member == null) {
|
|
|
+ return Result.error("您不是该团队成员,无法生成邀请码");
|
|
|
+ }
|
|
|
+
|
|
|
+ String code = member.getInviteCode();
|
|
|
+ if (refresh || code == null || code.trim().isEmpty()) {
|
|
|
+ code = generateUniqueInviteCode();
|
|
|
+ member.setInviteCode(code);
|
|
|
+ supplySystemMemberMapper.updateById(member);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("systemId", systemId);
|
|
|
+ data.put("inviteCode", code);
|
|
|
+ data.put("teamName", system.getName());
|
|
|
+ return Result.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 凭邀请码查询邀请信息(确认页展示用):团队名、邀请人昵称/头像、成员数。
|
|
|
+ * 不泄露其他隐私(不返回邀请人手机号等)。
|
|
|
+ */
|
|
|
+ public Result<Map<String, Object>> getInviteInfo(String inviteCode) {
|
|
|
+ if (inviteCode == null || inviteCode.trim().isEmpty()) {
|
|
|
+ return Result.error("邀请码不能为空");
|
|
|
+ }
|
|
|
+ SupplySystemMember inviter = supplySystemMemberMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getInviteCode, inviteCode.trim()));
|
|
|
+ if (inviter == null || !"active".equals(inviter.getStatus())) {
|
|
|
+ return Result.error("邀请码不存在或已失效");
|
|
|
+ }
|
|
|
+ return Result.success(buildInviteInfo(inviter));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 凭邀请码加入团队:校验码有效 + 用户未加入过该体系 →
|
|
|
+ * ① addMember(systemId,userId,role=member,status=active) ② addRelation(systemId, inviterId, userId)
|
|
|
+ * 挂 parentId=邀请人 / level=父level+1。被移除(inactive)后可再申请。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public Result<Map<String, Object>> joinByInviteCode(String inviteCode, Long userId) {
|
|
|
+ if (inviteCode == null || inviteCode.trim().isEmpty()) {
|
|
|
+ return Result.error("邀请码不能为空");
|
|
|
+ }
|
|
|
+ if (userId == null) {
|
|
|
+ return Result.error("未登录");
|
|
|
+ }
|
|
|
+ SupplySystemMember inviter = supplySystemMemberMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getInviteCode, inviteCode.trim()));
|
|
|
+ if (inviter == null || !"active".equals(inviter.getStatus())) {
|
|
|
+ return Result.error("邀请码不存在或已失效");
|
|
|
+ }
|
|
|
+ Long systemId = inviter.getSystemId();
|
|
|
+
|
|
|
+ // 校验用户未加入过该体系(inactive 可再申请:复活成员记录)
|
|
|
+ SupplySystemMember existing = supplySystemMemberMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getSystemId, systemId)
|
|
|
+ .eq(SupplySystemMember::getUserId, userId));
|
|
|
+ if (existing != null && "active".equals(existing.getStatus())) {
|
|
|
+ return Result.error("您已在该团队中,无需重复加入");
|
|
|
+ }
|
|
|
+ if (existing != null) {
|
|
|
+ existing.setStatus("active");
|
|
|
+ existing.setRole("member");
|
|
|
+ existing.setJoinedAt(new Date());
|
|
|
+ supplySystemMemberMapper.updateById(existing);
|
|
|
+ } else {
|
|
|
+ SupplySystemMember member = new SupplySystemMember();
|
|
|
+ member.setSystemId(systemId);
|
|
|
+ member.setUserId(userId);
|
|
|
+ member.setRole("member");
|
|
|
+ member.setStatus("active");
|
|
|
+ member.setJoinedAt(new Date());
|
|
|
+ supplySystemMemberMapper.insert(member);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 挂邀请人为上级(parentId=邀请人, level=父level+1);inactive 旧关系复活
|
|
|
+ // 注:supply_hierarchy 唯一键为 (system_id, parent_id, child_id),历史数据可能同一 child 多行,
|
|
|
+ // 故用 getOne(wrapper, false) 取首行而不抛 TooManyResultsException
|
|
|
+ SupplyHierarchy existingRel = supplyHierarchyService.getOne(
|
|
|
+ new LambdaQueryWrapper<SupplyHierarchy>()
|
|
|
+ .eq(SupplyHierarchy::getSystemId, systemId)
|
|
|
+ .eq(SupplyHierarchy::getMemberId, userId), false);
|
|
|
+ if (existingRel != null) {
|
|
|
+ int level = 1;
|
|
|
+ SupplyHierarchy parentRel = supplyHierarchyService.getOne(
|
|
|
+ new LambdaQueryWrapper<SupplyHierarchy>()
|
|
|
+ .eq(SupplyHierarchy::getSystemId, systemId)
|
|
|
+ .eq(SupplyHierarchy::getMemberId, inviter.getUserId())
|
|
|
+ .eq(SupplyHierarchy::getStatus, "active"), false);
|
|
|
+ if (parentRel != null) {
|
|
|
+ level = parentRel.getLevel() + 1;
|
|
|
+ }
|
|
|
+ existingRel.setParentId(inviter.getUserId());
|
|
|
+ existingRel.setLevel(level);
|
|
|
+ existingRel.setStatus("active");
|
|
|
+ existingRel.setUpdatedAt(new Date());
|
|
|
+ supplyHierarchyService.updateById(existingRel);
|
|
|
+ } else {
|
|
|
+ supplyHierarchyService.addRelation(systemId, inviter.getUserId(), userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return Result.success(buildInviteInfo(inviter));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装邀请信息(确认页/加入结果共用)
|
|
|
+ */
|
|
|
+ private Map<String, Object> buildInviteInfo(SupplySystemMember inviter) {
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("systemId", inviter.getSystemId());
|
|
|
+ SupplySystem system = this.getById(inviter.getSystemId());
|
|
|
+ data.put("teamName", system != null ? system.getName() : null);
|
|
|
+
|
|
|
+ User inviterUser = userMapper.selectById(inviter.getUserId());
|
|
|
+ data.put("inviterUserId", inviter.getUserId());
|
|
|
+ data.put("inviterNickname", inviterUser != null ? inviterUser.getNickname() : null);
|
|
|
+ data.put("inviterAvatar", inviterUser != null ? inviterUser.getAvatar() : null);
|
|
|
+
|
|
|
+ Long memberCount = supplySystemMemberMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getSystemId, inviter.getSystemId())
|
|
|
+ .eq(SupplySystemMember::getStatus, "active"));
|
|
|
+ data.put("memberCount", memberCount != null ? memberCount : 0L);
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成全局唯一的 8 位邀请码(字符集去掉易混淆的 0/O/1/I)
|
|
|
+ */
|
|
|
+ private String generateUniqueInviteCode() {
|
|
|
+ String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
|
+ Random random = new Random();
|
|
|
+ for (int attempt = 0; attempt < 20; attempt++) {
|
|
|
+ StringBuilder code = new StringBuilder();
|
|
|
+ for (int i = 0; i < 8; i++) {
|
|
|
+ code.append(chars.charAt(random.nextInt(chars.length())));
|
|
|
+ }
|
|
|
+ Long cnt = supplySystemMemberMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<SupplySystemMember>()
|
|
|
+ .eq(SupplySystemMember::getInviteCode, code.toString()));
|
|
|
+ if (cnt == null || cnt == 0) {
|
|
|
+ return code.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 极端冲突兜底
|
|
|
+ return "INV" + System.currentTimeMillis();
|
|
|
+ }
|
|
|
}
|