community.service.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import Parse from '../../../../shared/db/parse-client.js';
  2. import { deriveLifecyclePhase, type LifecyclePhase } from '../utils/lifecycle.util.js';
  3. import { listStores } from './organization.service.js';
  4. export interface CommunityDto {
  5. id: string;
  6. name: string;
  7. address: string;
  8. deliveryDate: string;
  9. lifecyclePhase: LifecyclePhase;
  10. totalHouseholds: number;
  11. avgPrice: number;
  12. storeId: string;
  13. storeName: string;
  14. groupCount: number;
  15. status: string;
  16. }
  17. export interface LifecycleSummaryDto {
  18. phase: LifecyclePhase;
  19. label: string;
  20. count: number;
  21. communities: CommunityDto[];
  22. }
  23. const SEED_COMMUNITIES: Array<{
  24. name: string;
  25. address: string;
  26. deliveryDate: string;
  27. totalHouseholds: number;
  28. avgPrice: number;
  29. storeCode: string;
  30. }> = [
  31. { name: '翠湖天地', address: '上海市黄浦区', deliveryDate: '2027-06', totalHouseholds: 1200, avgPrice: 168000, storeCode: 'SH001' },
  32. { name: '万达瑞华公馆', address: '上海市徐汇区', deliveryDate: '2026-08', totalHouseholds: 500, avgPrice: 185000, storeCode: 'SH001' },
  33. { name: '汤臣一品', address: '上海市浦东新区', deliveryDate: '2025-12', totalHouseholds: 800, avgPrice: 250000, storeCode: 'SH001' },
  34. { name: '华侨城苏河湾', address: '上海市静安区', deliveryDate: '2024-03', totalHouseholds: 600, avgPrice: 135000, storeCode: 'SH001' },
  35. { name: '北京壹号院', address: '北京市朝阳区', deliveryDate: '2026-09', totalHouseholds: 900, avgPrice: 180000, storeCode: 'BJ001' },
  36. { name: '万柳书院', address: '北京市海淀区', deliveryDate: '2022-01', totalHouseholds: 650, avgPrice: 155000, storeCode: 'BJ001' },
  37. { name: '深圳湾一号', address: '深圳市南山区', deliveryDate: '2025-06', totalHouseholds: 1000, avgPrice: 200000, storeCode: 'SZ001' },
  38. { name: '保利天珺', address: '深圳市福田区', deliveryDate: '2020-03', totalHouseholds: 600, avgPrice: 160000, storeCode: 'SZ001' },
  39. ];
  40. function toDto(obj: Parse.Object): CommunityDto {
  41. const deliveryDate = obj.get('deliveryDate') || '';
  42. const lifecyclePhase = (obj.get('lifecyclePhase') as LifecyclePhase)
  43. || deriveLifecyclePhase(deliveryDate);
  44. return {
  45. id: obj.id!,
  46. name: obj.get('name') || '',
  47. address: obj.get('address') || '',
  48. deliveryDate,
  49. lifecyclePhase,
  50. totalHouseholds: obj.get('totalHouseholds') ?? 0,
  51. avgPrice: obj.get('avgPrice') ?? 0,
  52. storeId: obj.get('storeId') || '',
  53. storeName: obj.get('storeName') || '',
  54. groupCount: obj.get('groupCount') ?? 0,
  55. status: obj.get('status') || 'active',
  56. };
  57. }
  58. export async function listCommunities(filters: {
  59. storeId?: string;
  60. lifecyclePhase?: LifecyclePhase;
  61. } = {}): Promise<CommunityDto[]> {
  62. const query = new Parse.Query('Community');
  63. query.equalTo('status', 'active');
  64. if (filters.storeId) query.equalTo('storeId', filters.storeId);
  65. if (filters.lifecyclePhase) query.equalTo('lifecyclePhase', filters.lifecyclePhase);
  66. query.ascending('name');
  67. query.limit(500);
  68. const results = await query.find({ useMasterKey: true });
  69. return results.map(toDto);
  70. }
  71. export function groupCommunitiesByLifecycle(communities: CommunityDto[]): LifecycleSummaryDto[] {
  72. const phases: LifecyclePhase[] = ['pre_handover', 'near_handover', 'post_handover', 'legacy'];
  73. const labels: Record<LifecyclePhase, string> = {
  74. pre_handover: '交房前',
  75. near_handover: '临交房',
  76. post_handover: '已交房',
  77. legacy: '老小区',
  78. };
  79. return phases.map((phase) => {
  80. const items = communities.filter((c) => c.lifecyclePhase === phase);
  81. return { phase, label: labels[phase], count: items.length, communities: items };
  82. });
  83. }
  84. export async function getCommunityLifecycleMap(): Promise<Map<string, LifecyclePhase>> {
  85. const communities = await listCommunities();
  86. const map = new Map<string, LifecyclePhase>();
  87. for (const c of communities) {
  88. map.set(c.id, c.lifecyclePhase);
  89. }
  90. return map;
  91. }
  92. export async function bootstrapCommunities(): Promise<void> {
  93. const stores = await listStores();
  94. const storeByCode = new Map(stores.map((s) => [s.code, s]));
  95. let created = 0;
  96. let updated = 0;
  97. for (const seed of SEED_COMMUNITIES) {
  98. const store = storeByCode.get(seed.storeCode);
  99. if (!store) continue;
  100. const lifecyclePhase = deriveLifecyclePhase(seed.deliveryDate);
  101. const query = new Parse.Query('Community');
  102. query.equalTo('name', seed.name);
  103. const existing = await query.first({ useMasterKey: true });
  104. if (existing) {
  105. existing.set('address', seed.address);
  106. existing.set('deliveryDate', seed.deliveryDate);
  107. existing.set('lifecyclePhase', lifecyclePhase);
  108. existing.set('totalHouseholds', seed.totalHouseholds);
  109. existing.set('avgPrice', seed.avgPrice);
  110. existing.set('storeId', store.id);
  111. existing.set('storeName', store.name);
  112. existing.set('status', 'active');
  113. await existing.save(null, { useMasterKey: true });
  114. updated++;
  115. continue;
  116. }
  117. const obj = new Parse.Object('Community');
  118. obj.set('name', seed.name);
  119. obj.set('address', seed.address);
  120. obj.set('deliveryDate', seed.deliveryDate);
  121. obj.set('lifecyclePhase', lifecyclePhase);
  122. obj.set('totalHouseholds', seed.totalHouseholds);
  123. obj.set('avgPrice', seed.avgPrice);
  124. obj.set('storeId', store.id);
  125. obj.set('storeName', store.name);
  126. obj.set('groupCount', 0);
  127. obj.set('status', 'active');
  128. await obj.save(null, { useMasterKey: true });
  129. created++;
  130. }
  131. if (created > 0 || updated > 0) {
  132. console.log(`[Community] 小区种子已同步:新增 ${created},更新 ${updated}`);
  133. }
  134. }