aifastdb 2.2.2 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/aifastdb.win32-x64-msvc.node +0 -0
  2. package/dist/carpool/benchmark.d.ts +9 -0
  3. package/dist/carpool/benchmark.d.ts.map +1 -0
  4. package/dist/carpool/benchmark.js +310 -0
  5. package/dist/carpool/benchmark.js.map +1 -0
  6. package/dist/carpool/carpool-credit.d.ts +124 -0
  7. package/dist/carpool/carpool-credit.d.ts.map +1 -0
  8. package/dist/carpool/carpool-credit.js +202 -0
  9. package/dist/carpool/carpool-credit.js.map +1 -0
  10. package/dist/carpool/carpool-index.d.ts +71 -0
  11. package/dist/carpool/carpool-index.d.ts.map +1 -0
  12. package/dist/carpool/carpool-index.js +203 -0
  13. package/dist/carpool/carpool-index.js.map +1 -0
  14. package/dist/carpool/carpool-matcher.d.ts +48 -0
  15. package/dist/carpool/carpool-matcher.d.ts.map +1 -0
  16. package/dist/carpool/carpool-matcher.js +133 -0
  17. package/dist/carpool/carpool-matcher.js.map +1 -0
  18. package/dist/carpool/carpool-matcher.test.d.ts +9 -0
  19. package/dist/carpool/carpool-matcher.test.d.ts.map +1 -0
  20. package/dist/carpool/carpool-matcher.test.js +403 -0
  21. package/dist/carpool/carpool-matcher.test.js.map +1 -0
  22. package/dist/carpool/carpool-privacy.d.ts +72 -0
  23. package/dist/carpool/carpool-privacy.d.ts.map +1 -0
  24. package/dist/carpool/carpool-privacy.js +171 -0
  25. package/dist/carpool/carpool-privacy.js.map +1 -0
  26. package/dist/carpool/carpool-repo.d.ts +175 -0
  27. package/dist/carpool/carpool-repo.d.ts.map +1 -0
  28. package/dist/carpool/carpool-repo.js +698 -0
  29. package/dist/carpool/carpool-repo.js.map +1 -0
  30. package/dist/carpool/carpool-repo.test.d.ts +9 -0
  31. package/dist/carpool/carpool-repo.test.d.ts.map +1 -0
  32. package/dist/carpool/carpool-repo.test.js +452 -0
  33. package/dist/carpool/carpool-repo.test.js.map +1 -0
  34. package/dist/carpool/carpool-scheduler.d.ts +68 -0
  35. package/dist/carpool/carpool-scheduler.d.ts.map +1 -0
  36. package/dist/carpool/carpool-scheduler.js +114 -0
  37. package/dist/carpool/carpool-scheduler.js.map +1 -0
  38. package/dist/carpool/carpool-service.d.ts +105 -0
  39. package/dist/carpool/carpool-service.d.ts.map +1 -0
  40. package/dist/carpool/carpool-service.js +372 -0
  41. package/dist/carpool/carpool-service.js.map +1 -0
  42. package/dist/carpool/carpool-service.test.d.ts +9 -0
  43. package/dist/carpool/carpool-service.test.d.ts.map +1 -0
  44. package/dist/carpool/carpool-service.test.js +455 -0
  45. package/dist/carpool/carpool-service.test.js.map +1 -0
  46. package/dist/carpool/carpool-test.d.ts +11 -0
  47. package/dist/carpool/carpool-test.d.ts.map +1 -0
  48. package/dist/carpool/carpool-test.js +387 -0
  49. package/dist/carpool/carpool-test.js.map +1 -0
  50. package/dist/carpool/index.d.ts +42 -0
  51. package/dist/carpool/index.d.ts.map +1 -0
  52. package/dist/carpool/index.js +80 -0
  53. package/dist/carpool/index.js.map +1 -0
  54. package/dist/carpool/privacy-guard.d.ts +69 -0
  55. package/dist/carpool/privacy-guard.d.ts.map +1 -0
  56. package/dist/carpool/privacy-guard.js +211 -0
  57. package/dist/carpool/privacy-guard.js.map +1 -0
  58. package/dist/carpool/report-system.d.ts +106 -0
  59. package/dist/carpool/report-system.d.ts.map +1 -0
  60. package/dist/carpool/report-system.js +212 -0
  61. package/dist/carpool/report-system.js.map +1 -0
  62. package/dist/carpool/trip-index.d.ts +73 -0
  63. package/dist/carpool/trip-index.d.ts.map +1 -0
  64. package/dist/carpool/trip-index.js +269 -0
  65. package/dist/carpool/trip-index.js.map +1 -0
  66. package/dist/carpool/trip-scheduler.d.ts +71 -0
  67. package/dist/carpool/trip-scheduler.d.ts.map +1 -0
  68. package/dist/carpool/trip-scheduler.js +126 -0
  69. package/dist/carpool/trip-scheduler.js.map +1 -0
  70. package/dist/carpool/trust-evaluator.d.ts +27 -0
  71. package/dist/carpool/trust-evaluator.d.ts.map +1 -0
  72. package/dist/carpool/trust-evaluator.js +117 -0
  73. package/dist/carpool/trust-evaluator.js.map +1 -0
  74. package/dist/carpool/types.d.ts +353 -0
  75. package/dist/carpool/types.d.ts.map +1 -0
  76. package/dist/carpool/types.js +71 -0
  77. package/dist/carpool/types.js.map +1 -0
  78. package/dist/carpool/validators.d.ts +25 -0
  79. package/dist/carpool/validators.d.ts.map +1 -0
  80. package/dist/carpool/validators.js +192 -0
  81. package/dist/carpool/validators.js.map +1 -0
  82. package/dist/carpool/vehicle.test.d.ts +8 -0
  83. package/dist/carpool/vehicle.test.d.ts.map +1 -0
  84. package/dist/carpool/vehicle.test.js +540 -0
  85. package/dist/carpool/vehicle.test.js.map +1 -0
  86. package/dist/dev-plan-store.d.ts +104 -0
  87. package/dist/dev-plan-store.d.ts.map +1 -1
  88. package/dist/dev-plan-store.js +331 -34
  89. package/dist/dev-plan-store.js.map +1 -1
  90. package/dist/federation/FederatedDb.d.ts +138 -1
  91. package/dist/federation/FederatedDb.d.ts.map +1 -1
  92. package/dist/federation/FederatedDb.js +158 -0
  93. package/dist/federation/FederatedDb.js.map +1 -1
  94. package/dist/federation/index.d.ts +1 -1
  95. package/dist/federation/index.d.ts.map +1 -1
  96. package/dist/federation/index.js.map +1 -1
  97. package/dist/federation/types.d.ts +136 -4
  98. package/dist/federation/types.d.ts.map +1 -1
  99. package/dist/index.d.ts +1 -1
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +41 -9
  102. package/dist/index.js.map +1 -1
  103. package/dist/mcp-server/index.d.ts +5 -16
  104. package/dist/mcp-server/index.d.ts.map +1 -1
  105. package/dist/mcp-server/index.js +6 -689
  106. package/dist/mcp-server/index.js.map +1 -1
  107. package/dist/security/server/routes.d.ts +46 -0
  108. package/dist/security/server/routes.d.ts.map +1 -1
  109. package/dist/security/server/routes.js +568 -1
  110. package/dist/security/server/routes.js.map +1 -1
  111. package/dist/social-graph-v2.d.ts +7 -0
  112. package/dist/social-graph-v2.d.ts.map +1 -1
  113. package/dist/social-graph-v2.js +10 -0
  114. package/dist/social-graph-v2.js.map +1 -1
  115. package/dist/social-graph.d.ts.map +1 -1
  116. package/dist/social-graph.js +2 -0
  117. package/dist/social-graph.js.map +1 -1
  118. package/dist/social-types.d.ts +4 -0
  119. package/dist/social-types.d.ts.map +1 -1
  120. package/package.json +3 -1
@@ -0,0 +1,698 @@
1
+ "use strict";
2
+ /**
3
+ * Carpool Repository - Data Access Layer
4
+ *
5
+ * 拼车数据访问层,封装对 SocialGraphV2 的 Entity/Relation 操作。
6
+ * 提供语义化的拼车数据访问接口。
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CarpoolRepository = void 0;
10
+ const types_1 = require("./types");
11
+ const trip_index_1 = require("./trip-index");
12
+ // ============================================================================
13
+ // Repository Class
14
+ // ============================================================================
15
+ class CarpoolRepository {
16
+ constructor(graph) {
17
+ this.graph = graph;
18
+ /** 内存索引(可选,通过 enableIndex 启用) */
19
+ this.index = null;
20
+ }
21
+ /**
22
+ * 启用内存索引以加速搜索(T20.1)
23
+ * 首次调用会从存储中加载所有行程构建索引。
24
+ */
25
+ enableIndex() {
26
+ if (!this.index) {
27
+ this.index = new trip_index_1.TripIndex();
28
+ this.rebuildIndex();
29
+ }
30
+ return this.index;
31
+ }
32
+ /** 获取当前索引实例(可能为 null) */
33
+ getIndex() {
34
+ return this.index;
35
+ }
36
+ /** 重建索引 */
37
+ rebuildIndex() {
38
+ if (this.index) {
39
+ const all = this.graph.listEntitiesByType(types_1.ENTITY_TYPE_CARPOOL_TRIP);
40
+ this.index.buildFrom(all.map(e => this.entityToTrip(e)));
41
+ }
42
+ }
43
+ // ==========================================================================
44
+ // Trip CRUD (T16.2)
45
+ // ==========================================================================
46
+ /**
47
+ * 创建行程并建立发布者关系
48
+ * @param publisherId - 发布者 person ID
49
+ * @param input - 行程输入
50
+ * @returns 创建的行程
51
+ */
52
+ createTrip(publisherId, input) {
53
+ // 检查发布者是否存在
54
+ const publisher = this.graph.getEntity(publisherId);
55
+ if (!publisher) {
56
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.PERSON_NOT_FOUND, `发布者不存在: ${publisherId}`);
57
+ }
58
+ // V2: 如果提供了 vehicle_id,验证车辆并自动填充 vehicle_info
59
+ let vehicleInfo = input.vehicle_info;
60
+ if (input.vehicle_id) {
61
+ const vehicle = this.getVehicle(input.vehicle_id);
62
+ if (!vehicle) {
63
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.VEHICLE_NOT_FOUND, `车辆不存在: ${input.vehicle_id}`);
64
+ }
65
+ // 验证车辆所有权
66
+ const owner = this.getVehicleOwner(input.vehicle_id);
67
+ if (!owner || owner.id !== publisherId) {
68
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.NOT_VEHICLE_OWNER, '非车辆所有者,无法使用该车辆');
69
+ }
70
+ // 验证车辆状态
71
+ if (vehicle.properties.status !== 'active') {
72
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.VEHICLE_INACTIVE, `车辆状态为 "${vehicle.properties.status}",无法使用`);
73
+ }
74
+ // 从 Vehicle 实体自动填充 vehicle_info 快照
75
+ vehicleInfo = {
76
+ brand: vehicle.properties.brand,
77
+ color: vehicle.properties.color,
78
+ seats: vehicle.properties.seats,
79
+ };
80
+ }
81
+ // 构造行程名称
82
+ const tripName = `${input.origin.city}→${input.destination.city} ${input.departure_date}`;
83
+ // 构造 properties
84
+ const properties = {
85
+ trip_type: input.trip_type,
86
+ departure_date: input.departure_date,
87
+ departure_time: input.departure_time,
88
+ origin: input.origin,
89
+ destination: input.destination,
90
+ max_passengers: input.max_passengers,
91
+ current_passengers: 0,
92
+ gender_requirement: input.gender_requirement ?? 'any',
93
+ price_type: input.price_type ?? 'share_cost',
94
+ price_per_person: input.price_per_person,
95
+ vehicle_info: vehicleInfo,
96
+ requirements: input.requirements,
97
+ contact_type: input.contact_type,
98
+ contact_value: input.contact_value,
99
+ notes: input.notes,
100
+ status: 'open',
101
+ };
102
+ // 创建行程实体
103
+ const entity = this.graph.addEntity(tripName, types_1.ENTITY_TYPE_CARPOOL_TRIP, properties);
104
+ // 创建发布者 → 行程的关系
105
+ this.graph.putRelation(publisherId, entity.id, types_1.RELATION_TYPES.PUBLISHED_TRIP, 1.0, false);
106
+ // V2: 如果使用了车辆,建立 uses_vehicle 关系
107
+ if (input.vehicle_id) {
108
+ this.graph.putRelation(entity.id, input.vehicle_id, types_1.RELATION_TYPES.USES_VEHICLE, 1.0, false);
109
+ }
110
+ const trip = this.entityToTrip(entity);
111
+ // 维护索引(传入 vehicleId 供索引使用)
112
+ if (this.index)
113
+ this.index.upsert(trip, input.vehicle_id);
114
+ return trip;
115
+ }
116
+ /**
117
+ * 获取行程
118
+ */
119
+ getTrip(tripId) {
120
+ const entity = this.graph.getEntity(tripId);
121
+ if (!entity || entity.entity_type !== types_1.ENTITY_TYPE_CARPOOL_TRIP) {
122
+ return null;
123
+ }
124
+ return this.entityToTrip(entity);
125
+ }
126
+ /**
127
+ * 获取行程(不存在则抛异常)
128
+ */
129
+ getTripOrThrow(tripId) {
130
+ const trip = this.getTrip(tripId);
131
+ if (!trip) {
132
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.TRIP_NOT_FOUND, `行程不存在: ${tripId}`);
133
+ }
134
+ return trip;
135
+ }
136
+ /**
137
+ * 更新行程属性
138
+ */
139
+ updateTrip(tripId, updates) {
140
+ const existing = this.getTripOrThrow(tripId);
141
+ // 合并属性
142
+ const newProperties = {
143
+ ...existing.properties,
144
+ };
145
+ if (updates.departure_date !== undefined)
146
+ newProperties.departure_date = updates.departure_date;
147
+ if (updates.departure_time !== undefined)
148
+ newProperties.departure_time = updates.departure_time;
149
+ if (updates.origin !== undefined)
150
+ newProperties.origin = updates.origin;
151
+ if (updates.destination !== undefined)
152
+ newProperties.destination = updates.destination;
153
+ if (updates.max_passengers !== undefined)
154
+ newProperties.max_passengers = updates.max_passengers;
155
+ if (updates.gender_requirement !== undefined)
156
+ newProperties.gender_requirement = updates.gender_requirement;
157
+ if (updates.price_type !== undefined)
158
+ newProperties.price_type = updates.price_type;
159
+ if (updates.price_per_person !== undefined)
160
+ newProperties.price_per_person = updates.price_per_person;
161
+ if (updates.vehicle_info !== undefined)
162
+ newProperties.vehicle_info = updates.vehicle_info;
163
+ if (updates.requirements !== undefined)
164
+ newProperties.requirements = updates.requirements;
165
+ if (updates.contact_type !== undefined)
166
+ newProperties.contact_type = updates.contact_type;
167
+ if (updates.contact_value !== undefined)
168
+ newProperties.contact_value = updates.contact_value;
169
+ if (updates.notes !== undefined)
170
+ newProperties.notes = updates.notes;
171
+ // 更新名称(如果起终点或日期变了)
172
+ const newName = `${newProperties.origin.city}→${newProperties.destination.city} ${newProperties.departure_date}`;
173
+ const updated = this.graph.updateEntity(tripId, {
174
+ name: newName,
175
+ properties: newProperties,
176
+ });
177
+ if (updated) {
178
+ const trip = this.entityToTrip(updated);
179
+ if (this.index)
180
+ this.index.upsert(trip);
181
+ return trip;
182
+ }
183
+ return null;
184
+ }
185
+ /**
186
+ * 更新行程状态
187
+ */
188
+ updateTripStatus(tripId, status) {
189
+ const existing = this.getTripOrThrow(tripId);
190
+ const newProperties = { ...existing.properties, status };
191
+ const updated = this.graph.updateEntity(tripId, {
192
+ properties: newProperties,
193
+ });
194
+ const result = this.entityToTrip(updated);
195
+ if (this.index)
196
+ this.index.upsert(result);
197
+ return result;
198
+ }
199
+ /**
200
+ * 删除行程(及其所有关联关系)
201
+ */
202
+ deleteTrip(tripId) {
203
+ const trip = this.getTrip(tripId);
204
+ if (!trip)
205
+ return false;
206
+ // 删除所有与行程相关的关系
207
+ const relations = this.graph.getEntityRelations(tripId);
208
+ for (const rel of relations) {
209
+ this.graph.deleteRelation(rel.id);
210
+ }
211
+ // 维护索引
212
+ if (this.index)
213
+ this.index.remove(tripId);
214
+ return this.graph.deleteEntity(tripId);
215
+ }
216
+ // ==========================================================================
217
+ // Query Methods (T16.3)
218
+ // ==========================================================================
219
+ /**
220
+ * 获取所有拼车行程
221
+ */
222
+ listAllTrips() {
223
+ const entities = this.graph.listEntitiesByType(types_1.ENTITY_TYPE_CARPOOL_TRIP);
224
+ return entities.map(e => this.entityToTrip(e));
225
+ }
226
+ /**
227
+ * 按条件搜索行程(启用索引时走索引快速路径)
228
+ */
229
+ searchTrips(filter) {
230
+ if (this.index) {
231
+ return this.searchTripsWithIndex(filter);
232
+ }
233
+ const all = this.listAllTrips();
234
+ return all.filter(trip => this.matchesFilter(trip, filter));
235
+ }
236
+ /**
237
+ * 使用索引加速的搜索路径(T20.1)
238
+ */
239
+ searchTripsWithIndex(filter) {
240
+ const candidateIds = this.index.queryCandidateIds(filter);
241
+ if (candidateIds.length === 0)
242
+ return [];
243
+ // 从 graph 获取完整实体并做细粒度过滤
244
+ const results = [];
245
+ for (const id of candidateIds) {
246
+ const trip = this.getTrip(id);
247
+ if (trip && this.matchesFilter(trip, filter)) {
248
+ results.push(trip);
249
+ }
250
+ }
251
+ return results;
252
+ }
253
+ /**
254
+ * 获取开放中的行程
255
+ */
256
+ listOpenTrips() {
257
+ return this.searchTrips({ status: 'open' });
258
+ }
259
+ /**
260
+ * 按行程类型获取开放行程
261
+ */
262
+ listTripsByType(tripType) {
263
+ return this.searchTrips({ trip_type: tripType, status: 'open' });
264
+ }
265
+ /**
266
+ * 获取某人发布的行程
267
+ */
268
+ getPersonPublishedTrips(personId, status) {
269
+ const relations = this.graph.getEntityRelations(personId);
270
+ const publishedRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.PUBLISHED_TRIP && r.source === personId);
271
+ const trips = [];
272
+ for (const rel of publishedRelations) {
273
+ const trip = this.getTrip(rel.target);
274
+ if (trip && (!status || trip.properties.status === status)) {
275
+ trips.push(trip);
276
+ }
277
+ }
278
+ return trips;
279
+ }
280
+ /**
281
+ * 获取某人参与的行程(已加入的)
282
+ */
283
+ getPersonJoinedTrips(personId) {
284
+ const relations = this.graph.getEntityRelations(personId);
285
+ const joinedRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.JOINED_TRIP && r.source === personId);
286
+ const trips = [];
287
+ for (const rel of joinedRelations) {
288
+ const trip = this.getTrip(rel.target);
289
+ if (trip)
290
+ trips.push(trip);
291
+ }
292
+ return trips;
293
+ }
294
+ /**
295
+ * 获取行程的发布者
296
+ */
297
+ getTripPublisher(tripId) {
298
+ const relations = this.graph.getEntityRelations(tripId);
299
+ const publishRelation = relations.find(r => r.relation_type === types_1.RELATION_TYPES.PUBLISHED_TRIP && r.target === tripId);
300
+ if (!publishRelation)
301
+ return null;
302
+ return this.graph.getEntity(publishRelation.source);
303
+ }
304
+ // ==========================================================================
305
+ // Application & Relation Operations (T16.4)
306
+ // ==========================================================================
307
+ /**
308
+ * 创建申请关系
309
+ */
310
+ createApplication(personId, tripId, message) {
311
+ // 检查是否已申请
312
+ const existing = this.getApplicationRelation(personId, tripId);
313
+ if (existing) {
314
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.ALREADY_APPLIED, '已申请过该行程');
315
+ }
316
+ // 检查是否已加入
317
+ const joined = this.getJoinedRelation(personId, tripId);
318
+ if (joined) {
319
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.ALREADY_JOINED, '已加入该行程');
320
+ }
321
+ // 创建 applied_trip 关系(weight=0.5 表示待确认)
322
+ const relationId = this.graph.putRelation(personId, tripId, types_1.RELATION_TYPES.APPLIED_TRIP, 0.5, false);
323
+ return relationId;
324
+ }
325
+ /**
326
+ * 接受申请:删除 applied_trip → 创建 joined_trip → 更新座位数
327
+ */
328
+ acceptApplication(tripId, applicantId) {
329
+ const trip = this.getTripOrThrow(tripId);
330
+ // 检查座位
331
+ if (trip.properties.current_passengers >= trip.properties.max_passengers) {
332
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.TRIP_FULL, '行程已满');
333
+ }
334
+ // 查找申请关系
335
+ const appRelation = this.getApplicationRelation(applicantId, tripId);
336
+ if (!appRelation) {
337
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.APPLICATION_NOT_FOUND, '未找到申请记录');
338
+ }
339
+ // 删除申请关系
340
+ this.graph.deleteRelation(appRelation.id);
341
+ // 创建 joined_trip 关系
342
+ this.graph.putRelation(applicantId, tripId, types_1.RELATION_TYPES.JOINED_TRIP, 1.0, false);
343
+ // 更新座位数和状态
344
+ const newPassengers = trip.properties.current_passengers + 1;
345
+ const newStatus = newPassengers >= trip.properties.max_passengers ? 'full' : 'open';
346
+ this.graph.updateEntity(tripId, {
347
+ properties: {
348
+ ...trip.properties,
349
+ current_passengers: newPassengers,
350
+ status: newStatus,
351
+ },
352
+ });
353
+ return true;
354
+ }
355
+ /**
356
+ * 拒绝申请:删除 applied_trip 关系
357
+ */
358
+ rejectApplication(tripId, applicantId) {
359
+ const appRelation = this.getApplicationRelation(applicantId, tripId);
360
+ if (!appRelation) {
361
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.APPLICATION_NOT_FOUND, '未找到申请记录');
362
+ }
363
+ this.graph.deleteRelation(appRelation.id);
364
+ return true;
365
+ }
366
+ /**
367
+ * 乘客退出行程
368
+ */
369
+ removePassenger(tripId, passengerId) {
370
+ const joinRelation = this.getJoinedRelation(passengerId, tripId);
371
+ if (!joinRelation)
372
+ return false;
373
+ // 删除 joined_trip 关系
374
+ this.graph.deleteRelation(joinRelation.id);
375
+ // 更新座位数
376
+ const trip = this.getTripOrThrow(tripId);
377
+ const newPassengers = Math.max(0, trip.properties.current_passengers - 1);
378
+ const newStatus = trip.properties.status === 'full' ? 'open' : trip.properties.status;
379
+ this.graph.updateEntity(tripId, {
380
+ properties: {
381
+ ...trip.properties,
382
+ current_passengers: newPassengers,
383
+ status: newStatus,
384
+ },
385
+ });
386
+ return true;
387
+ }
388
+ /**
389
+ * 获取行程的已确认乘客列表
390
+ */
391
+ getTripPassengers(tripId) {
392
+ const relations = this.graph.getEntityRelations(tripId);
393
+ const joinedRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.JOINED_TRIP && r.target === tripId);
394
+ const passengers = [];
395
+ for (const rel of joinedRelations) {
396
+ const person = this.graph.getEntity(rel.source);
397
+ if (person)
398
+ passengers.push(person);
399
+ }
400
+ return passengers;
401
+ }
402
+ /**
403
+ * 获取行程的待处理申请列表
404
+ */
405
+ getTripApplications(tripId) {
406
+ const relations = this.graph.getEntityRelations(tripId);
407
+ const appRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.APPLIED_TRIP && r.target === tripId);
408
+ const applications = [];
409
+ for (const rel of appRelations) {
410
+ const applicant = this.graph.getEntity(rel.source);
411
+ if (applicant) {
412
+ applications.push({
413
+ applicant,
414
+ relation: rel,
415
+ status: 'pending',
416
+ message: rel.metadata?.message,
417
+ applied_at: rel.created_at,
418
+ });
419
+ }
420
+ }
421
+ return applications;
422
+ }
423
+ /**
424
+ * 创建评价关系
425
+ */
426
+ createRating(personId, tripId, rating, comment) {
427
+ return this.graph.putRelation(personId, tripId, types_1.RELATION_TYPES.RATED_TRIP, rating / 5.0, // weight = rating 归一化到 0~1
428
+ false);
429
+ }
430
+ /**
431
+ * 获取行程的所有评价
432
+ */
433
+ getTripRatings(tripId) {
434
+ const relations = this.graph.getEntityRelations(tripId);
435
+ const ratingRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.RATED_TRIP && r.target === tripId);
436
+ const ratings = [];
437
+ for (const rel of ratingRelations) {
438
+ const person = this.graph.getEntity(rel.source);
439
+ if (person) {
440
+ ratings.push({
441
+ person,
442
+ rating: rel.weight * 5.0,
443
+ comment: rel.metadata?.comment,
444
+ relation: rel,
445
+ });
446
+ }
447
+ }
448
+ return ratings;
449
+ }
450
+ // ==========================================================================
451
+ // Vehicle CRUD (V2 - T21.3)
452
+ // ==========================================================================
453
+ /**
454
+ * 注册新车辆并建立所有权关系
455
+ * @param ownerId - 车主 person ID
456
+ * @param input - 车辆注册信息
457
+ * @returns 创建的车辆实体
458
+ */
459
+ createVehicle(ownerId, input) {
460
+ // 检查车主是否存在
461
+ const owner = this.graph.getEntity(ownerId);
462
+ if (!owner) {
463
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.PERSON_NOT_FOUND, `车主不存在: ${ownerId}`);
464
+ }
465
+ // 检查车牌号是否已注册
466
+ const existing = this.findVehicleByPlate(input.license_plate);
467
+ if (existing) {
468
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.VEHICLE_ALREADY_EXISTS, `车牌号已注册: ${input.license_plate}`);
469
+ }
470
+ const now = Date.now();
471
+ const vehicleName = `${input.brand} ${input.model ?? ''} ${input.license_plate}`.trim();
472
+ const properties = {
473
+ license_plate: input.license_plate.trim().toUpperCase(),
474
+ brand: input.brand.trim(),
475
+ model: input.model?.trim(),
476
+ color: input.color.trim(),
477
+ year: input.year,
478
+ seats: input.seats,
479
+ available_seats: input.seats - 1,
480
+ fuel_type: input.fuel_type,
481
+ status: 'active',
482
+ verified: false,
483
+ image_url: input.image_url,
484
+ created_at: now,
485
+ updated_at: now,
486
+ };
487
+ // 创建车辆实体
488
+ const entity = this.graph.addEntity(vehicleName, types_1.ENTITY_TYPE_VEHICLE, properties);
489
+ // 创建 owns_vehicle 关系: Person → Vehicle
490
+ this.graph.putRelation(ownerId, entity.id, types_1.RELATION_TYPES.OWNS_VEHICLE, 1.0, false);
491
+ return this.entityToVehicle(entity);
492
+ }
493
+ /**
494
+ * 获取车辆
495
+ */
496
+ getVehicle(vehicleId) {
497
+ const entity = this.graph.getEntity(vehicleId);
498
+ if (!entity || entity.entity_type !== types_1.ENTITY_TYPE_VEHICLE) {
499
+ return null;
500
+ }
501
+ return this.entityToVehicle(entity);
502
+ }
503
+ /**
504
+ * 获取车辆(不存在则抛异常)
505
+ */
506
+ getVehicleOrThrow(vehicleId) {
507
+ const vehicle = this.getVehicle(vehicleId);
508
+ if (!vehicle) {
509
+ throw new types_1.CarpoolError(types_1.CarpoolErrorCode.VEHICLE_NOT_FOUND, `车辆不存在: ${vehicleId}`);
510
+ }
511
+ return vehicle;
512
+ }
513
+ /**
514
+ * 更新车辆信息
515
+ */
516
+ updateVehicle(vehicleId, updates) {
517
+ const existing = this.getVehicleOrThrow(vehicleId);
518
+ const now = Date.now();
519
+ const newProperties = {
520
+ ...existing.properties,
521
+ updated_at: now,
522
+ };
523
+ if (updates.brand !== undefined)
524
+ newProperties.brand = updates.brand.trim();
525
+ if (updates.model !== undefined)
526
+ newProperties.model = updates.model?.trim();
527
+ if (updates.color !== undefined)
528
+ newProperties.color = updates.color.trim();
529
+ if (updates.year !== undefined)
530
+ newProperties.year = updates.year;
531
+ if (updates.seats !== undefined) {
532
+ newProperties.seats = updates.seats;
533
+ newProperties.available_seats = updates.seats - 1;
534
+ }
535
+ if (updates.fuel_type !== undefined)
536
+ newProperties.fuel_type = updates.fuel_type;
537
+ if (updates.image_url !== undefined)
538
+ newProperties.image_url = updates.image_url;
539
+ const newName = `${newProperties.brand} ${newProperties.model ?? ''} ${newProperties.license_plate}`.trim();
540
+ const updated = this.graph.updateEntity(vehicleId, {
541
+ name: newName,
542
+ properties: newProperties,
543
+ });
544
+ return this.entityToVehicle(updated);
545
+ }
546
+ /**
547
+ * 停用车辆
548
+ */
549
+ deactivateVehicle(vehicleId) {
550
+ const existing = this.getVehicleOrThrow(vehicleId);
551
+ const now = Date.now();
552
+ const newProperties = {
553
+ ...existing.properties,
554
+ status: 'inactive',
555
+ updated_at: now,
556
+ };
557
+ const updated = this.graph.updateEntity(vehicleId, {
558
+ properties: newProperties,
559
+ });
560
+ return this.entityToVehicle(updated);
561
+ }
562
+ /**
563
+ * 按车牌号查找车辆
564
+ */
565
+ findVehicleByPlate(licensePlate) {
566
+ const normalizedPlate = licensePlate.trim().toUpperCase();
567
+ const allVehicles = this.graph.listEntitiesByType(types_1.ENTITY_TYPE_VEHICLE);
568
+ for (const entity of allVehicles) {
569
+ const props = entity.properties;
570
+ if (props.license_plate === normalizedPlate) {
571
+ return this.entityToVehicle(entity);
572
+ }
573
+ }
574
+ return null;
575
+ }
576
+ /**
577
+ * 获取某人拥有的所有车辆
578
+ */
579
+ getVehiclesByOwner(ownerId) {
580
+ const relations = this.graph.getEntityRelations(ownerId);
581
+ const ownsRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.OWNS_VEHICLE && r.source === ownerId);
582
+ const vehicles = [];
583
+ for (const rel of ownsRelations) {
584
+ const vehicle = this.getVehicle(rel.target);
585
+ if (vehicle)
586
+ vehicles.push(vehicle);
587
+ }
588
+ return vehicles;
589
+ }
590
+ /**
591
+ * 获取车辆的车主
592
+ */
593
+ getVehicleOwner(vehicleId) {
594
+ const relations = this.graph.getEntityRelations(vehicleId);
595
+ const ownsRelation = relations.find(r => r.relation_type === types_1.RELATION_TYPES.OWNS_VEHICLE && r.target === vehicleId);
596
+ if (!ownsRelation)
597
+ return null;
598
+ return this.graph.getEntity(ownsRelation.source);
599
+ }
600
+ /**
601
+ * 获取行程关联的车辆
602
+ */
603
+ getTripVehicle(tripId) {
604
+ const relations = this.graph.getEntityRelations(tripId);
605
+ const usesRelation = relations.find(r => r.relation_type === types_1.RELATION_TYPES.USES_VEHICLE && r.source === tripId);
606
+ if (!usesRelation)
607
+ return null;
608
+ return this.getVehicle(usesRelation.target);
609
+ }
610
+ /**
611
+ * 获取车辆的历史行程
612
+ */
613
+ getVehicleTrips(vehicleId) {
614
+ const relations = this.graph.getEntityRelations(vehicleId);
615
+ const usesRelations = relations.filter(r => r.relation_type === types_1.RELATION_TYPES.USES_VEHICLE && r.target === vehicleId);
616
+ const trips = [];
617
+ for (const rel of usesRelations) {
618
+ const trip = this.getTrip(rel.source);
619
+ if (trip)
620
+ trips.push(trip);
621
+ }
622
+ return trips;
623
+ }
624
+ // ==========================================================================
625
+ // Internal Helpers
626
+ // ==========================================================================
627
+ /** Entity → CarpoolVehicle 类型转换 */
628
+ entityToVehicle(entity) {
629
+ return {
630
+ ...entity,
631
+ entity_type: types_1.ENTITY_TYPE_VEHICLE,
632
+ properties: entity.properties,
633
+ };
634
+ }
635
+ /** Entity → CarpoolTrip 类型转换 */
636
+ entityToTrip(entity) {
637
+ return {
638
+ ...entity,
639
+ entity_type: types_1.ENTITY_TYPE_CARPOOL_TRIP,
640
+ properties: entity.properties,
641
+ };
642
+ }
643
+ /** 检查行程是否匹配过滤条件 */
644
+ matchesFilter(trip, filter) {
645
+ const p = trip.properties;
646
+ // 状态过滤(默认 open)
647
+ const targetStatus = filter.status ?? 'open';
648
+ if (p.status !== targetStatus)
649
+ return false;
650
+ // 行程类型
651
+ if (filter.trip_type && p.trip_type !== filter.trip_type)
652
+ return false;
653
+ // 精确日期
654
+ if (filter.departure_date && p.departure_date !== filter.departure_date)
655
+ return false;
656
+ // 日期范围
657
+ if (filter.departure_date_range) {
658
+ if (p.departure_date < filter.departure_date_range.from)
659
+ return false;
660
+ if (p.departure_date > filter.departure_date_range.to)
661
+ return false;
662
+ }
663
+ // 出发省份
664
+ if (filter.origin_province && p.origin.province !== filter.origin_province)
665
+ return false;
666
+ // 出发城市
667
+ if (filter.origin_city && p.origin.city !== filter.origin_city)
668
+ return false;
669
+ // 目的省份
670
+ if (filter.destination_province && p.destination.province !== filter.destination_province)
671
+ return false;
672
+ // 目的城市
673
+ if (filter.destination_city && p.destination.city !== filter.destination_city)
674
+ return false;
675
+ // 性别要求
676
+ if (filter.gender_requirement && p.gender_requirement !== 'any' && p.gender_requirement !== filter.gender_requirement)
677
+ return false;
678
+ // 费用类型
679
+ if (filter.price_type && p.price_type !== filter.price_type)
680
+ return false;
681
+ // 是否有空座
682
+ if (filter.has_seats_available && p.current_passengers >= p.max_passengers)
683
+ return false;
684
+ return true;
685
+ }
686
+ /** 查找申请关系 */
687
+ getApplicationRelation(personId, tripId) {
688
+ const relations = this.graph.getEntityRelations(personId);
689
+ return relations.find(r => r.relation_type === types_1.RELATION_TYPES.APPLIED_TRIP && r.source === personId && r.target === tripId) ?? null;
690
+ }
691
+ /** 查找已加入关系 */
692
+ getJoinedRelation(personId, tripId) {
693
+ const relations = this.graph.getEntityRelations(personId);
694
+ return relations.find(r => r.relation_type === types_1.RELATION_TYPES.JOINED_TRIP && r.source === personId && r.target === tripId) ?? null;
695
+ }
696
+ }
697
+ exports.CarpoolRepository = CarpoolRepository;
698
+ //# sourceMappingURL=carpool-repo.js.map