| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- /**
- * 小区管理模块 — 业务服务层
- *
- * 对应规范文档:
- * §5.5「录入小区基础档案」
- * §5.6「维护小区—群—门店对应关系」
- *
- * 业务规则:
- * 1. 小区户数无法从 QiWe 自动获取,需人工录入
- * 2. 群与小区/门店的绑定关系禁止自动猜测——必须人工确认
- * 3. 未绑定群在看板中标记为「待维护」
- * 4. 报表按小区/门店聚合时,通过绑定表 INNER JOIN
- */
- import { AppError } from '../../../../shared/errors/app-error.js';
- import type {
- Community,
- CreateCommunityRequest,
- CommunityRoomBinding,
- CreateBindingRequest,
- } from '../models/community.model.js';
- // ---------- 内存存储(TODO: 替换为数据库)----------
- const communityStore = new Map<number, Community>();
- const bindingStore = new Map<number, CommunityRoomBinding>();
- let nextCommunityId = 1;
- let nextBindingId = 1;
- // ============================================================
- // 小区档案 CRUD
- // ============================================================
- /**
- * 录入小区基础档案
- *
- * 户数是触达率计算的分母,必须准确。
- * 房价、交房时间等字段供看板和经营分析使用。
- */
- export function createCommunity(data: CreateCommunityRequest): Community {
- const now = new Date().toISOString();
- const community: Community = {
- id: nextCommunityId++,
- name: data.name,
- totalHouseholds: data.totalHouseholds,
- avgPrice: data.avgPrice || 0,
- deliveryYear: data.deliveryYear || 0,
- storeId: data.storeId,
- address: data.address || '',
- remark: data.remark || '',
- createdAt: now,
- updatedAt: now,
- };
- communityStore.set(community.id, community);
- return community;
- }
- /**
- * 查询小区列表(支持按门店筛选)
- */
- export function listCommunities(storeId?: number): Community[] {
- let result = Array.from(communityStore.values());
- if (storeId !== undefined) {
- result = result.filter((c) => c.storeId === storeId);
- }
- return result;
- }
- /**
- * 查询单个小区详情
- */
- export function getCommunityById(id: number): Community {
- const community = communityStore.get(id);
- if (!community) {
- throw new AppError(404, 'COMMUNITY_NOT_FOUND', `小区 ID=${id} 不存在`);
- }
- return community;
- }
- /**
- * 更新小区信息
- */
- export function updateCommunity(
- id: number,
- data: Partial<CreateCommunityRequest> = {},
- ): Community {
- const community = getCommunityById(id);
- if (data.name !== undefined) community.name = data.name;
- if (data.totalHouseholds !== undefined) community.totalHouseholds = data.totalHouseholds;
- if (data.avgPrice !== undefined) community.avgPrice = data.avgPrice;
- if (data.deliveryYear !== undefined) community.deliveryYear = data.deliveryYear;
- if (data.storeId !== undefined) community.storeId = data.storeId;
- if (data.address !== undefined) community.address = data.address;
- if (data.remark !== undefined) community.remark = data.remark;
- community.updatedAt = new Date().toISOString();
- communityStore.set(id, community);
- return community;
- }
- /**
- * 删除小区
- */
- export function deleteCommunity(id: number): void {
- getCommunityById(id);
- communityStore.delete(id);
- // 同时清理关联的绑定关系
- for (const [key, binding] of bindingStore) {
- if (binding.communityId === id) {
- bindingStore.delete(key);
- }
- }
- }
- // ============================================================
- // 小区-群-门店绑定
- // ============================================================
- /**
- * 创建小区-群-门店绑定关系
- *
- * 业务约束:
- * - 同一个 roomId 不可重复绑定到不同的小区
- * - 绑定禁止自动猜测,必须人工确认
- *
- * @throws {AppError} 当 roomId 已被绑定时
- */
- export function createBinding(data: CreateBindingRequest): CommunityRoomBinding {
- // 检查 roomId 是否已绑定
- for (const binding of bindingStore.values()) {
- if (binding.roomId === data.roomId) {
- throw new AppError(
- 409,
- 'BINDING_DUPLICATE',
- `群 ${data.roomId} 已绑定到小区 ID=${binding.communityId},不可重复绑定`,
- );
- }
- }
- const binding: CommunityRoomBinding = {
- id: nextBindingId++,
- communityId: data.communityId,
- roomId: data.roomId,
- storeId: data.storeId,
- createdAt: new Date().toISOString(),
- };
- bindingStore.set(binding.id, binding);
- return binding;
- }
- /**
- * 查询绑定关系列表(按小区或门店筛选)
- */
- export function listBindings(communityId?: number, storeId?: number): CommunityRoomBinding[] {
- let result = Array.from(bindingStore.values());
- if (communityId !== undefined) {
- result = result.filter((b) => b.communityId === communityId);
- }
- if (storeId !== undefined) {
- result = result.filter((b) => b.storeId === storeId);
- }
- return result;
- }
- /**
- * 删除绑定关系(解绑)
- */
- export function deleteBinding(id: number): void {
- const binding = bindingStore.get(id);
- if (!binding) {
- throw new AppError(404, 'BINDING_NOT_FOUND', `绑定 ID=${id} 不存在`);
- }
- bindingStore.delete(id);
- }
- /**
- * 按 roomId 查询绑定(用于合规等模块关联查询)
- */
- export function getBindingByRoomId(roomId: string): CommunityRoomBinding | undefined {
- for (const binding of bindingStore.values()) {
- if (binding.roomId === roomId) {
- return binding;
- }
- }
- return undefined;
- }
|