zoujiajian 9 сар өмнө
parent
commit
6e38414e5a

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/chatgpt/WorkspaceInviteRequest.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.dto.chatgpt;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 工作空间邀请请求
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class WorkspaceInviteRequest {
+    
+    /**
+     * 邀请的邮箱列表
+     */
+    private List<String> emailAddresses;
+    
+    /**
+     * 成员角色,可选值: "standard-user", "admin"
+     */
+    private String role = "standard-user";
+    
+    /**
+     * 是否重新发送邮件(默认 true)
+     */
+    @Builder.Default
+    private Boolean resendEmails = true;
+}

+ 53 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/chatgpt/WorkspaceInviteResponse.java

@@ -0,0 +1,53 @@
+package com.cyksj.model.dto.chatgpt;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 工作空间邀请响应
+ */
+@Data
+public class WorkspaceInviteResponse {
+    
+    /**
+     * 成功发送的邀请列表
+     */
+    private List<AccountInvite> accountInvites;
+    
+    /**
+     * 发送失败的邮箱列表
+     */
+    private List<String> erroredEmails;
+    
+    /**
+     * 账号邀请信息
+     */
+    @Data
+    public static class AccountInvite {
+        /**
+         * 邀请ID
+         */
+        private String id;
+        
+        /**
+         * 邀请邮箱
+         */
+        private String emailAddress;
+        
+        /**
+         * 角色
+         */
+        private String role;
+        
+        /**
+         * 创建时间 (ISO 8601 格式)
+         */
+        private String createdTime;
+        
+        /**
+         * 是否由 SCIM 管理
+         */
+        private Boolean isScimManaged;
+    }
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/GptTeamAccount.java

@@ -21,6 +21,11 @@ public class GptTeamAccount extends BaseEntity {
      */
     private String token;
 
+    /**
+     * 工作空间id
+     */
+    private String accountId;
+
     /**
      * 设备ID
      */

+ 14 - 5
netflix-dao/src/main/java/com/cyksj/model/entity/GptTeamUserBindRecord.java

@@ -12,7 +12,6 @@ import java.util.Date;
 @Getter
 @Setter
 public class GptTeamUserBindRecord extends BaseEntity {
-
     /**
      * 用户ID
      */
@@ -26,14 +25,24 @@ public class GptTeamUserBindRecord extends BaseEntity {
     private String bindEmail;
 
     /**
-     * team token
+     * 空间id
+     */
+    private String accountId;
+
+    /**
+     * 换绑前空间id
+     */
+    private String bfAccountId;
+
+    /**
+     * 空间成员ID
      */
-    private String token;
+    private String userKey;
 
     /**
-     * 换绑前的 token
+     * 换绑前空间成员ID
      */
-    private String bfToken;
+    private String bfUserKey;
 
     /**
      * 绑定时间

+ 17 - 0
netflix-service/src/main/java/com/cyksj/service/chatgpt/ChatGptWorkspaceService.java

@@ -0,0 +1,17 @@
+package com.cyksj.service.chatgpt;
+
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteRequest;
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteResponse;
+
+/**
+ * ChatGPT工作空间服务接口
+ */
+public interface ChatGptWorkspaceService {
+    
+    /**
+     * 发送工作空间邀请
+     */
+    WorkspaceInviteResponse sendWorkspaceInvite(String accountId, WorkspaceInviteRequest request);
+
+    Boolean deleteWorkspaceUser(String accountId, String userKey);
+}

+ 99 - 0
netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/ChatGptWorkspaceServiceImpl.java

@@ -0,0 +1,99 @@
+package com.cyksj.service.chatgpt.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.Method;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteRequest;
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteResponse;
+import com.cyksj.service.chatgpt.ChatGptWorkspaceService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ChatGPT工作空间服务实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ChatGptWorkspaceServiceImpl implements ChatGptWorkspaceService {
+
+    private final static String BASE_URL = "https://chat-chanv4-s5z9a5c3.xyhelper.org";
+
+    private final static String HEAD_AUTH_KEY = "AUTHKEY";
+
+    private final static String HEAD_AUTH_KEY_VALUE = "xyhelper";
+
+    private final static int CONNECT_TIME_OUT = 5000;
+    @Override
+    public WorkspaceInviteResponse sendWorkspaceInvite(String accountId, WorkspaceInviteRequest request) {
+        log.info("开始发送工作空间邀请 - accountId: {}, emails: {}, role: {}",
+                accountId, request.getEmailAddresses(), request.getRole());
+
+        // 构建请求URL
+        String url = BASE_URL + "/backend-api/accounts/" + accountId + "/invites";
+        // 构建请求体
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("email_addresses", request.getEmailAddresses());
+        requestBody.put("role", request.getRole());
+        //是否重新发送邮件 false
+        requestBody.put("resend_emails", false);
+        String requestJson = JSONUtil.toJsonStr(requestBody);
+
+        HttpResponse execute = HttpUtil.createPost(url)
+                .header(HEAD_AUTH_KEY, HEAD_AUTH_KEY_VALUE)
+                .setConnectionTimeout(CONNECT_TIME_OUT)
+                .body(requestJson)
+                .execute();
+        int statusCode = execute.getStatus();
+        String responseBody = execute.body();
+
+        log.info("响应状态码: {}, 响应体: {}", statusCode, responseBody);
+
+        // 检查响应状态
+        if (statusCode < 200 || statusCode >= 300) {
+            log.error("发送工作空间邀请失败,状态码: {}", statusCode);
+            throw new BusinessRuntimeException("加入team失败,请重试!");
+        }
+
+        // 解析响应
+        WorkspaceInviteResponse inviteResponse = null;
+        List<WorkspaceInviteResponse.AccountInvite> accountInvites = null;
+        try {
+            JSONObject jsonResponse = JSONUtil.parseObj(responseBody);
+            inviteResponse = JSONUtil.toBean(jsonResponse, WorkspaceInviteResponse.class);
+            accountInvites = inviteResponse.getAccountInvites();
+        } catch (Exception e) {
+            log.error("accountId:{},email:{}解析发送邀请空间body失败-->{}", accountId, request.getEmailAddresses(), StringUtil.getErrorText(e));
+            throw new BusinessRuntimeException("加入team失败,请重试!");
+        }
+
+        if (CollUtil.isEmpty(accountInvites)) {
+            log.error("accountId:{}邀请用户邮箱:{}成功记录为空", accountId, request.getEmailAddresses());
+            throw new BusinessRuntimeException("加入team失败,请重试!");
+        }
+        return inviteResponse;
+    }
+
+    @Override
+    public Boolean deleteWorkspaceUser(String accountId, String userKey) {
+        // 构建请求URL
+        String url = BASE_URL + String.format("/backend-api/accounts/%s/users/%s", accountId, userKey);
+        HttpResponse execute = HttpUtil.createRequest(Method.DELETE, url).header(HEAD_AUTH_KEY, HEAD_AUTH_KEY_VALUE).setConnectionTimeout(CONNECT_TIME_OUT).execute();
+        int statusCode = execute.getStatus();
+        if (statusCode < 200 || statusCode >= 300) {
+            log.info("移除工作空间accountId:{}成员userKey:{}失败,info-->{}", accountId, userKey, execute.body());
+            return Boolean.FALSE;
+        }
+        return Boolean.TRUE;
+    }
+}

+ 44 - 10
netflix-service/src/main/java/com/cyksj/service/gpt/impl/GptTeamUserBindRecordServiceImpl.java

@@ -1,6 +1,7 @@
 package com.cyksj.service.gpt.impl;
 
 import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Validator;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.constant.Constant;
@@ -11,8 +12,11 @@ import com.cyksj.mapper.GptTeamAccountMapper;
 import com.cyksj.mapper.GptTeamUserBindRecordMapper;
 import com.cyksj.mapper.OrderDonMapper;
 import com.cyksj.mapper.sys.SysConfigMapper;
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteRequest;
+import com.cyksj.model.dto.chatgpt.WorkspaceInviteResponse;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.GptTeamBindRequest;
+import com.cyksj.service.chatgpt.ChatGptWorkspaceService;
 import com.cyksj.service.gpt.GptTeamUserBindRecordService;
 import com.cyksj.service.user.UserBindRelationService;
 import lombok.RequiredArgsConstructor;
@@ -44,6 +48,8 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
 
     private final SysConfigMapper sysConfigMapper;
 
+    private final ChatGptWorkspaceService chatGptWorkspaceService;
+
     private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
     @Override
@@ -89,7 +95,7 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         if (teamAccount == null) {
             throw BusinessRuntimeException.getInstance("加入Team失败,请联系客服配置Team后重新加入!");
         }
-        Integer totalNum = bindRecordMapper.selectCount(Wrappers.lambdaQuery(GptTeamUserBindRecord.class).eq(GptTeamUserBindRecord::getToken, teamAccount.getToken()).eq(GptTeamUserBindRecord::getStatus, 1));
+        Integer totalNum = bindRecordMapper.selectCount(Wrappers.lambdaQuery(GptTeamUserBindRecord.class).eq(GptTeamUserBindRecord::getAccountId, teamAccount.getAccountId()).eq(GptTeamUserBindRecord::getStatus, 1));
         int update = gptTeamAccountMapper.update(null, Wrappers.lambdaUpdate(GptTeamAccount.class)
                 .set(GptTeamAccount::getTotalNum, totalNum + 1)
                 .eq(GptTeamAccount::getId, teamAccount.getId())
@@ -97,10 +103,11 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         if (update == 0) {
             throw BusinessRuntimeException.getInstance("系统繁忙,请重试!");
         }
-        bindRecord.setToken(teamAccount.getToken());
+        bindRecord.setAccountId(teamAccount.getAccountId());
         bindRecord.setStatus(Boolean.TRUE);
         bindRecordMapper.updateById(bindRecord);
-        //绑定 Team API TODO
+        //绑定gpt team
+        bindTeam(teamAccount.getAccountId(), email, bindRecord);
     }
 
     @Override
@@ -110,7 +117,7 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         Long userId = request.getUserId();
 
         List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
-        GptTeamUserBindRecord bindRecord = bindRecordMapper.selectOne(Wrappers.lambdaQuery(GptTeamUserBindRecord.class).eq(GptTeamUserBindRecord::getOrderId, orderId).in(GptTeamUserBindRecord::getUserId, userIdList).last("limit 1"));
+        GptTeamUserBindRecord bindRecord = bindRecordMapper.selectOne(Wrappers.lambdaQuery(GptTeamUserBindRecord.class).eq(GptTeamUserBindRecord::getOrderId, orderId).eq(GptTeamUserBindRecord::getStatus, Boolean.TRUE).in(GptTeamUserBindRecord::getUserId, userIdList).last("limit 1"));
         if (bindRecord == null) {
             throw BusinessRuntimeException.getInstance("您还未加入Team");
         }
@@ -126,8 +133,9 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         if (update == 0) {
             throw new BusinessRuntimeException("仅可换绑一次");
         }
-        //换绑前的token
-        String bfToken = bindRecord.getToken();
+        //换绑前的空间id
+        String bfAccountId = bindRecord.getAccountId();
+        String bfUserKey = bindRecord.getUserKey();
         // 查找新的可用账号
         Integer gptTeamInviteLimitNum = getGptTeamInviteLimitNum();
         GptTeamAccount teamAccount = gptTeamAccountMapper.selecAvailableTeam(gptTeamInviteLimitNum);
@@ -141,11 +149,18 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         if (updateTeamCount == 0) {
             throw BusinessRuntimeException.getInstance("系统繁忙,请重试!");
         }
-        bindRecord.setToken(teamAccount.getToken());
-        bindRecord.setBfToken(bfToken);
+        bindRecord.setAccountId(teamAccount.getAccountId());
+        bindRecord.setBfAccountId(bfAccountId);
+        bindRecord.setBfUserKey(bfUserKey);
         bindRecordMapper.updateById(bindRecord);
-
-        //换绑其他Team API TODO
+        try {
+            //换绑
+            bindTeam(teamAccount.getAccountId(), bindRecord.getBindEmail(), bindRecord);
+        } catch (Exception e) {
+            throw BusinessRuntimeException.getInstance("换绑其他Team失败,请重试!");
+        }
+        //解绑
+        chatGptWorkspaceService.deleteWorkspaceUser(bfAccountId, bfUserKey);
     }
 
     public Integer getGptTeamInviteLimitNum() {
@@ -157,4 +172,23 @@ public class GptTeamUserBindRecordServiceImpl implements GptTeamUserBindRecordSe
         }
         return inviteLimitNum;
     }
+
+    /**
+     * 绑定GPT TEAM
+     */
+    public void bindTeam(String accountId, String email, GptTeamUserBindRecord bindRecord) {
+        //绑定 Team API
+        WorkspaceInviteRequest workspaceInviteRequest = new WorkspaceInviteRequest();
+        workspaceInviteRequest.setEmailAddresses(List.of(email));
+        WorkspaceInviteResponse workspaceInviteResponse = chatGptWorkspaceService.sendWorkspaceInvite(accountId, workspaceInviteRequest);
+
+        List<WorkspaceInviteResponse.AccountInvite> accountInvites = workspaceInviteResponse.getAccountInvites();
+        WorkspaceInviteResponse.AccountInvite accountInvite = accountInvites.get(0);
+        //绑定时间
+        String createdTime = accountInvite.getCreatedTime();
+        bindRecord.setBindTime(DateUtil.parseDateTime(createdTime));
+        //成员ID
+        bindRecord.setUserKey(accountInvite.getId());
+        bindRecordMapper.updateById(bindRecord);
+    }
 }