Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

session.service.ts 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import { Injectable, OnDestroy, Injector } from '@angular/core';
  2. import { HttpClient } from '@angular/common/http';
  3. import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
  4. import { map, tap } from 'rxjs/operators';
  5. import {
  6. Session,
  7. SessionCreateRequest,
  8. SessionCreateApiResponse,
  9. SessionListResponse,
  10. ProjectCreateRequest,
  11. ProjectCreateApiResponse
  12. } from '../models/session.model';
  13. import { AuthService } from './auth.service';
  14. import { EventService } from './event.service';
  15. import { SessionUpdatedEvent } from '../models/event.model';
  16. @Injectable({
  17. providedIn: 'root'
  18. })
  19. export class SessionService implements OnDestroy {
  20. private activeSession = new BehaviorSubject<Session | null>(null);
  21. activeSession$ = this.activeSession.asObservable();
  22. private sessions: Session[] = [];
  23. private sessionsSubject = new BehaviorSubject<Session[]>([]);
  24. sessions$ = this.sessionsSubject.asObservable();
  25. private eventSubscription: Subscription = new Subscription();
  26. private injector: Injector;
  27. private _eventService: EventService | null = null;
  28. constructor(
  29. private http: HttpClient,
  30. private authService: AuthService,
  31. injector: Injector
  32. ) {
  33. this.injector = injector;
  34. // 延迟设置事件订阅,在EventService可用时进行
  35. setTimeout(() => this.setupEventSubscriptions(), 0);
  36. }
  37. private get eventService(): EventService | null {
  38. if (!this._eventService) {
  39. try {
  40. this._eventService = this.injector.get(EventService, null);
  41. } catch (error) {
  42. console.warn('无法获取EventService:', error);
  43. return null;
  44. }
  45. }
  46. return this._eventService;
  47. }
  48. // 获取会话列表(需要认证)- 使用新API端点
  49. getSessions(): Observable<Session[]> {
  50. // 检查用户是否已认证
  51. if (!this.authService.isAuthenticated()) {
  52. console.warn('用户未认证,无法获取会话列表,返回空数组');
  53. return of([]);
  54. }
  55. return this.http.get<any>('/api/sessions').pipe(
  56. map(response => {
  57. console.log('🔍 [SessionService] 获取会话列表响应:', response);
  58. if (response.success && response.data) {
  59. // 处理嵌套的 sessions 字段(后端返回 { data: { sessions: [], ...pagination } })
  60. let sessionsArray: any[] = [];
  61. if (response.data.sessions && Array.isArray(response.data.sessions)) {
  62. // 新格式:response.data.sessions 是数组
  63. sessionsArray = response.data.sessions;
  64. console.log('🔍 [SessionService] 从嵌套 sessions 字段获取数据,数量:', sessionsArray.length);
  65. } else if (Array.isArray(response.data)) {
  66. // 兼容旧格式:response.data 直接是数组
  67. sessionsArray = response.data;
  68. console.log('🔍 [SessionService] 从 data 字段直接获取数组,数量:', sessionsArray.length);
  69. } else {
  70. console.warn('🔍 [SessionService] 无法识别响应数据格式,使用空数组');
  71. sessionsArray = [];
  72. }
  73. // 确保每个会话都有 project_id 字段(向后兼容)
  74. const sessions = sessionsArray.map(session => ({
  75. ...session,
  76. project_id: session.project_id || session.id // 如果没有 project_id,使用 id 作为默认
  77. }));
  78. this.sessions = sessions;
  79. this.sessionsSubject.next(this.sessions);
  80. console.log('🔍 [SessionService] 会话列表处理完成,数量:', sessions.length);
  81. return sessions;
  82. } else {
  83. console.error('🔍 [SessionService] 获取会话列表失败:', response.message);
  84. throw new Error(response.message || '获取会话列表失败');
  85. }
  86. }),
  87. tap(sessions => {
  88. // 如果没有活动会话,设置第一个为活动会话
  89. if (sessions.length > 0 && !this.activeSession.value) {
  90. this.setActiveSession(sessions[0]);
  91. }
  92. })
  93. );
  94. }
  95. // 创建新会话(向后兼容,建议使用createProject)
  96. createSession(title: string, menuItemId: string): Observable<Session> {
  97. console.warn('createSession已过时,请使用createProject方法');
  98. // 尝试使用默认智能体创建项目
  99. const defaultAgent = 'report'; // 默认智能体
  100. return this.createProject({
  101. title,
  102. agent_name: defaultAgent,
  103. description: `由旧会话创建,菜单项: ${menuItemId}`
  104. });
  105. }
  106. // 创建新项目
  107. createProject(request: ProjectCreateRequest): Observable<Session> {
  108. // 检查用户是否已认证
  109. if (!this.authService.isAuthenticated()) {
  110. console.error('用户未认证,无法创建项目');
  111. throw new Error('用户未认证,请先登录');
  112. }
  113. // 生成项目ID(如果未提供)
  114. const projectRequest = {
  115. ...request,
  116. project_id: request.project_id || `proj_${crypto.randomUUID()}`
  117. };
  118. return this.http.post<ProjectCreateApiResponse>('/api/session/create', projectRequest).pipe(
  119. map(response => {
  120. console.log('🔍 [SessionService] 创建项目响应:', response);
  121. if (response.success && response.data) {
  122. const sessionData = response.data;
  123. // 确保有project_id字段
  124. const newSession: Session = {
  125. ...sessionData,
  126. project_id: sessionData.project_id || projectRequest.project_id
  127. };
  128. this.sessions.unshift(newSession);
  129. this.sessionsSubject.next([...this.sessions]);
  130. this.setActiveSession(newSession);
  131. return newSession;
  132. } else {
  133. throw new Error(response.message || '创建项目失败');
  134. }
  135. })
  136. );
  137. }
  138. // 设置活动会话
  139. setActiveSession(session: Session | null) {
  140. this.activeSession.next(session);
  141. }
  142. // 获取当前活动会话
  143. getActiveSession(): Session | null {
  144. return this.activeSession.value;
  145. }
  146. // 根据ID获取会话
  147. getSessionById(id: string): Session | undefined {
  148. return this.sessions.find(session => session.id === id);
  149. }
  150. // 加载会话列表(公共方法,供组件调用)
  151. loadSessions(): void {
  152. if (!this.authService.isAuthenticated()) {
  153. console.warn('用户未认证,跳过会话列表加载');
  154. this.sessionsSubject.next([]);
  155. return;
  156. }
  157. console.log('🔍 [SessionService] 开始加载会话列表...');
  158. this.getSessions().subscribe({
  159. next: (sessions) => {
  160. console.log('🔍 [SessionService] 会话列表加载成功,数量:', sessions.length);
  161. },
  162. error: (error) => {
  163. console.error('🔍 [SessionService] 加载会话列表失败:', error);
  164. // 如果API不可用,使用空列表
  165. this.sessionsSubject.next([]);
  166. }
  167. });
  168. }
  169. // 删除会话(如果后端支持)
  170. deleteSession(sessionId: string): Observable<boolean> {
  171. // 注意:后端可能没有删除API,这里只是示例
  172. return of(true).pipe(
  173. tap(() => {
  174. this.sessions = this.sessions.filter(s => s.id !== sessionId);
  175. this.sessionsSubject.next([...this.sessions]);
  176. // 如果删除的是活动会话,清空活动会话
  177. if (this.activeSession.value?.id === sessionId) {
  178. this.setActiveSession(null);
  179. }
  180. })
  181. );
  182. }
  183. // 设置事件订阅
  184. private setupEventSubscriptions() {
  185. const eventService = this.eventService;
  186. if (!eventService) {
  187. console.warn('EventService不可用,跳过事件订阅设置');
  188. return;
  189. }
  190. // 订阅会话更新事件
  191. this.eventSubscription.add(
  192. eventService.sessionUpdated$.subscribe(event => {
  193. this.handleSessionUpdated(event);
  194. })
  195. );
  196. }
  197. // 处理会话更新事件
  198. private handleSessionUpdated(event: SessionUpdatedEvent) {
  199. const sessionInfo = event.properties.info;
  200. console.log('SessionService: 处理会话更新事件', sessionInfo.id, sessionInfo.title);
  201. // 查找现有会话
  202. const existingSessionIndex = this.sessions.findIndex(s => s.id === sessionInfo.id);
  203. if (existingSessionIndex !== -1) {
  204. // 更新现有会话
  205. const updatedSession: Session = {
  206. ...this.sessions[existingSessionIndex],
  207. title: sessionInfo.title,
  208. agent_name: sessionInfo.agent_name || this.sessions[existingSessionIndex].agent_name,
  209. description: sessionInfo.description || this.sessions[existingSessionIndex].description,
  210. status: sessionInfo.status || this.sessions[existingSessionIndex].status,
  211. // 保留其他字段
  212. };
  213. this.sessions[existingSessionIndex] = updatedSession;
  214. console.log('SessionService: 更新现有会话', updatedSession.title);
  215. } else {
  216. // 添加新会话到列表开头
  217. const newSession: Session = {
  218. id: sessionInfo.id,
  219. project_id: sessionInfo.project_id || sessionInfo.projectID || sessionInfo.id,
  220. title: sessionInfo.title,
  221. agent_name: sessionInfo.agent_name || 'report',
  222. description: sessionInfo.description || '',
  223. status: sessionInfo.status || 'requirement_document',
  224. // port和baseURL可能在后端创建会话时提供,这里使用默认值
  225. // 实际使用中,可以从事件或后续API调用中获取
  226. port: 8020, // 默认端口,可根据需要调整
  227. baseURL: `http://localhost:8020`, // 默认baseURL
  228. };
  229. this.sessions.unshift(newSession);
  230. console.log('SessionService: 添加新会话', newSession.title);
  231. }
  232. // 通知订阅者
  233. this.sessionsSubject.next([...this.sessions]);
  234. }
  235. ngOnDestroy() {
  236. this.eventSubscription.unsubscribe();
  237. }
  238. }