Commit 6147cdb2 authored by lixuan's avatar lixuan

feat: 需求

parent 87f99dc3
Pipeline #147202 failed with stages
in 0 seconds
...@@ -4,12 +4,48 @@ import lombok.Data; ...@@ -4,12 +4,48 @@ import lombok.Data;
import java.util.List; import java.util.List;
/**
* 时间窗内的新增 / 修改 / 删除汇总。数据来源于 {@code data_change_record}。
*
* <h3>房源(HOUSE_RESOURCE)维度</h3>
* <ul>
* <li>{@link #houseResourceInsert} / {@link #houseResourceUpdate} / {@link #houseResourceDelete}:
* 按 {@code house_resource.type} 分组,固定补齐 type = 1/4/5/6/7 五档;</li>
* <li>{@code insert} / {@code update} 桶的每个档位会带上 {@code ids} 列表,
* 前端可以直接把这组 id 传给 {@code /api/house/page} 的 {@code ids} 参数做 drilldown,
* 计数和列表天然一致;</li>
* <li>{@code delete} 桶**只给 count 不给 ids**:drilldown 列表本身会过滤掉逻辑删除的
* 房源,所以给 id 也没法展示,只保留数量用于汇总展示。</li>
* </ul>
*
* <h3>经营主体(BUSINESS_ENTITY_INFO)维度</h3>
* <ul>
* <li>{@link #businessEntityInsertCount} / {@link #businessEntityInsertIds}:
* 新增经营主体的数量和 id 列表(可传给 drilldown);</li>
* <li>{@link #businessEntityUpdateCount} / {@link #businessEntityUpdateIds}:同上;</li>
* <li>{@link #businessEntityDeleteCount}:只给 count,无 id。</li>
* </ul>
*
* <h3>计数口径</h3>
* <p>insert / update 桶会过滤 {@code delete_flag = 0},保证和 pageHouseResources 的 drilldown
* 列表能一一对上;delete 桶不过滤 {@code delete_flag},用于统计窗内发生过删除事件的数量。</p>
*/
@Data @Data
public class HouseResourceDataCollection { public class HouseResourceDataCollection {
private List<HouseResourceDataCollectionSimpleObject> newObject; private List<HouseResourceDataCollectionSimpleObject> houseResourceInsert;
private List<HouseResourceDataCollectionSimpleObject> editObject; private List<HouseResourceDataCollectionSimpleObject> houseResourceUpdate;
private List<HouseResourceDataCollectionSimpleObject> deleteObject; private List<HouseResourceDataCollectionSimpleObject> houseResourceDelete;
private long businessEntityInsertCount;
private List<String> businessEntityInsertIds;
private long businessEntityUpdateCount;
private List<String> businessEntityUpdateIds;
private long businessEntityDeleteCount;
} }
package com.ruoyi.system.domain.house.vo;
import lombok.Data;
/**
* 汇总查询 insert / update 分支的 SQL 行结构:按 (type, operationType, subjectId) 展开。
* 仅 Service 内部使用。
*
* <ul>
* <li>HOUSE_RESOURCE 维度:{@link #type} = house_resource.type。</li>
* <li>BUSINESS_ENTITY_INFO 维度:{@link #type} 恒为 0 / 无意义,只使用
* {@link #operationType} + {@link #subjectId}。</li>
* </ul>
*/
@Data
public class HouseResourceDataCollectionDetailRow {
private int type;
private int operationType;
private String subjectId;
}
...@@ -4,25 +4,39 @@ import lombok.Data; ...@@ -4,25 +4,39 @@ import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date; import java.util.Date;
import java.util.List;
/**
* 房源 & 经营主体变更汇总查询入参。
*
* <p>时间窗口基于 {@code data_change_record.operation_time} BETWEEN [start, end],
* 只统计 {@code operation_type} 为新增(1)/ 修改(2)的记录,删除(3)不纳入展示。</p>
*
* <p>{@link #two} / {@link #three} / {@link #four} 可选:</p>
* <ul>
* <li>对 HOUSE_RESOURCE 维度:直接按 {@code house_resource.two/three/four} 过滤;</li>
* <li>对 BUSINESS_ENTITY_INFO 维度:通过 mapping + house_resource 关联过滤,
* 即"关联到至少一个匹配网格的房源"的经营主体才会被计入。</li>
* </ul>
*/
@Data @Data
public class HouseResourceDataCollectionQuery { public class HouseResourceDataCollectionQuery {
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date changeStartTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date changeEndTime;
private String two; private String two;
private String three; private String three;
private String four; private String four;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") /**
private Date createTimeStart; * 当前登录人的网格权限范围,由 Service 层注入,前端无需传。
* 非空时会额外限定 house_resource.two IN (wgCodes)。
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") */
private Date createTimeEnd; private List<String> wgCodes;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTimeStart;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTimeEnd;
} }
package com.ruoyi.system.domain.house.vo;
import lombok.Data;
/**
* 汇总查询的 SQL 行结构,按 (type, operationType) 展开。
* 仅服务内部使用,不直接对外暴露。
*
* <p>对 BUSINESS_ENTITY_INFO 维度的查询,{@link #type} 恒为 0 / 无意义,
* 只使用 {@link #operationType} + {@link #count}。</p>
*/
@Data
public class HouseResourceDataCollectionRow {
private int type;
private int operationType;
private long count;
}
...@@ -2,10 +2,25 @@ package com.ruoyi.system.domain.house.vo; ...@@ -2,10 +2,25 @@ package com.ruoyi.system.domain.house.vo;
import lombok.Data; import lombok.Data;
import java.util.List;
/**
* 房源变更汇总中的一个 type 档位桶。
*
* <ul>
* <li>{@link #type}:house_resource.type(1 / 4 / 5 / 6 / 7)。</li>
* <li>{@link #count}:该档位去重后的房源数。</li>
* <li>{@link #ids}:该档位的 house_resource.id 列表,仅在 insert / update 桶中填充,
* 用于前端点击 drilldown 时直接传给 {@code /api/house/page} 的 {@code ids} 参数。
* delete 桶该字段固定为 {@code null}。</li>
* </ul>
*/
@Data @Data
public class HouseResourceDataCollectionSimpleObject { public class HouseResourceDataCollectionSimpleObject {
private int type; private int type;
private long count; private long count;
private List<String> ids;
} }
...@@ -44,6 +44,14 @@ public class HouseResourcePageQuery extends PageDomain { ...@@ -44,6 +44,14 @@ public class HouseResourcePageQuery extends PageDomain {
private List<String> wgCodes; private List<String> wgCodes;
/**
* 直接按 house_resource.id 精确筛选。
*
* <p>典型用途:dataCollection 返回的 insert / update id 列表直接回传,用于
* drilldown 查看变更明细列表。传空 / null 不生效。</p>
*/
private List<String> ids;
private String name; private String name;
private boolean zlFlag; private boolean zlFlag;
......
...@@ -8,7 +8,6 @@ import com.ruoyi.system.domain.house.HouseResource; ...@@ -8,7 +8,6 @@ import com.ruoyi.system.domain.house.HouseResource;
import com.ruoyi.system.domain.house.vo.*; import com.ruoyi.system.domain.house.vo.*;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List; import java.util.List;
public interface HouseResourceMapper { public interface HouseResourceMapper {
...@@ -43,9 +42,34 @@ public interface HouseResourceMapper { ...@@ -43,9 +42,34 @@ public interface HouseResourceMapper {
List<HouseResourcePage> selectByIdList(@Param("idList") List<String> idList); List<HouseResourcePage> selectByIdList(@Param("idList") List<String> idList);
List<HouseResourceDataCollectionSimpleObject> selectHouseResourceByCreateTime(@Param("query") HouseResourceDataCollectionQuery query); /**
* 时间窗内房源(subject_type = HOUSE_RESOURCE)的新增 / 修改**详情**行。
List<HouseResourceDataCollectionSimpleObject> selectHouseResourceByUpdateTime(@Param("query") HouseResourceDataCollectionQuery query); *
* <p>每行一个 (type, operation_type, subject_id) 元组,由 Service 层按 (type, operation_type)
List<HouseResourceDataCollectionSimpleObject> selectHouseResourceByUpdateTimeDeleteFlag(@Param("query") HouseResourceDataCollectionQuery query); * 分组聚合出 count + ids。仅包含 operation_type IN (1, 2)。强制 {@code hr.delete_flag = 0},
* 保证 id 列表可以安全回传给 {@code /api/house/page} 做 drilldown。</p>
*/
List<HouseResourceDataCollectionDetailRow> selectHouseResourceChangeDetails(@Param("query") HouseResourceDataCollectionQuery query);
/**
* 时间窗内房源(subject_type = HOUSE_RESOURCE)的**删除**计数,按 house_resource.type 分组。
*
* <p>只含 operation_type = 3,**不**过滤 {@code hr.delete_flag}(需要统计已逻辑删除的房源)。</p>
*/
List<HouseResourceDataCollectionRow> selectHouseResourceDeleteCount(@Param("query") HouseResourceDataCollectionQuery query);
/**
* 时间窗内经营主体(subject_type = BUSINESS_ENTITY_INFO)的新增 / 修改**详情**行。
*
* <p>每行一个 (operation_type, subject_id) 元组(type 恒为 0)。仅包含 operation_type IN (1, 2)。
* 强制 {@code bei.delete_flag = 0}。</p>
*/
List<HouseResourceDataCollectionDetailRow> selectBusinessEntityChangeDetails(@Param("query") HouseResourceDataCollectionQuery query);
/**
* 时间窗内经营主体(subject_type = BUSINESS_ENTITY_INFO)的**删除**计数。
*
* <p>只含 operation_type = 3,**不**过滤 {@code bei.delete_flag}。返回 0 或 1 行。</p>
*/
List<HouseResourceDataCollectionRow> selectBusinessEntityDeleteCount(@Param("query") HouseResourceDataCollectionQuery query);
} }
...@@ -13,6 +13,8 @@ import com.ruoyi.system.domain.house.HouseResource; ...@@ -13,6 +13,8 @@ import com.ruoyi.system.domain.house.HouseResource;
import com.ruoyi.system.domain.house.HouseResourceBusinessEntityInfoMapping; import com.ruoyi.system.domain.house.HouseResourceBusinessEntityInfoMapping;
import com.ruoyi.system.domain.house.enums.HouseEnums; import com.ruoyi.system.domain.house.enums.HouseEnums;
import com.ruoyi.system.domain.house.vo.*; import com.ruoyi.system.domain.house.vo.*;
import com.ruoyi.system.domain.changerecord.enums.OperationType;
import com.ruoyi.system.domain.changerecord.enums.SubjectType;
import com.ruoyi.system.mapper.grid.GridRegionMapper; import com.ruoyi.system.mapper.grid.GridRegionMapper;
import com.ruoyi.system.mapper.grid.GridRegionUserMapper; import com.ruoyi.system.mapper.grid.GridRegionUserMapper;
import com.ruoyi.system.mapper.house.BusinessEntityInfoMapper; import com.ruoyi.system.mapper.house.BusinessEntityInfoMapper;
...@@ -229,14 +231,9 @@ public class HouseResourceServiceImpl implements HouseResourceService { ...@@ -229,14 +231,9 @@ public class HouseResourceServiceImpl implements HouseResourceService {
@Override @Override
public IPage<HouseResourcePage> pageHouseResources(HouseResourcePageQuery query) { public IPage<HouseResourcePage> pageHouseResources(HouseResourcePageQuery query) {
String userId = SecurityUtils.getLoginUser().getUser().getUserId(); List<String> wgCodes = resolveCurrentUserWgCodes(true);
GridRegionUserExample example = new GridRegionUserExample(); if (wgCodes != null) {
GridRegionUserExample.Criteria criteria = example.createCriteria(); query.setWgCodes(wgCodes);
criteria.andIsValidEqualTo("1");
criteria.andUserIdEqualTo(userId);
List<GridRegionUser> gridRegionUsers = gridRegionUserMapper.selectByExample(example);
if (!CollectionUtils.isEmpty(gridRegionUsers) && !SecurityUtils.getLoginUser().getUser().isAdmin() && !SecurityUtils.getLoginUser().getUser().getUserId().equals("794aa2c8b5c24933a30591dd7dc439ed") && !SecurityUtils.getLoginUser().getUser().getUserId().equals("ca1df7d1a3f347dc9e73e8283dd134a5") && !SecurityUtils.getLoginUser().getUser().getUserId().equals("3cf3b0fa6c0945919504be4aa237f89e")) {
query.setWgCodes(gridRegionUsers.stream().map(GridRegionUser::getWgId).collect(Collectors.toList()));
} }
IPage<HouseResourcePage> page = houseResourceMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), query); IPage<HouseResourcePage> page = houseResourceMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), query);
List<HouseResourcePage> records = handleHouseList(page.getRecords(), false); List<HouseResourcePage> records = handleHouseList(page.getRecords(), false);
...@@ -400,14 +397,9 @@ public class HouseResourceServiceImpl implements HouseResourceService { ...@@ -400,14 +397,9 @@ public class HouseResourceServiceImpl implements HouseResourceService {
@Override @Override
public List<HouseResourcePage> exportListHouseResources(HouseResourcePageQuery query) { public List<HouseResourcePage> exportListHouseResources(HouseResourcePageQuery query) {
String userId = SecurityUtils.getLoginUser().getUser().getUserId(); List<String> wgCodes = resolveCurrentUserWgCodes(false);
GridRegionUserExample example = new GridRegionUserExample(); if (wgCodes != null) {
GridRegionUserExample.Criteria criteria = example.createCriteria(); query.setWgCodes(wgCodes);
criteria.andIsValidEqualTo("1");
criteria.andUserIdEqualTo(userId);
List<GridRegionUser> gridRegionUsers = gridRegionUserMapper.selectByExample(example);
if (!CollectionUtils.isEmpty(gridRegionUsers) && !SecurityUtils.getLoginUser().getUser().isAdmin()) {
query.setWgCodes(gridRegionUsers.stream().map(GridRegionUser::getWgId).collect(Collectors.toList()));
} }
List<HouseResourcePage> list = houseResourceMapper.exportList(query); List<HouseResourcePage> list = houseResourceMapper.exportList(query);
list.forEach(x -> { list.forEach(x -> {
...@@ -551,15 +543,9 @@ public class HouseResourceServiceImpl implements HouseResourceService { ...@@ -551,15 +543,9 @@ public class HouseResourceServiceImpl implements HouseResourceService {
} }
} }
} else { } else {
String userId = SecurityUtils.getLoginUser().getUser().getUserId(); List<String> wgCodes = resolveCurrentUserWgCodes(true);
GridRegionUserExample example = new GridRegionUserExample(); if (wgCodes != null) {
GridRegionUserExample.Criteria criteria = example.createCriteria(); for (String two : wgCodes) {
criteria.andIsValidEqualTo("1");
criteria.andUserIdEqualTo(userId);
List<GridRegionUser> gridRegionUsers = gridRegionUserMapper.selectByExample(example);
if (!CollectionUtils.isEmpty(gridRegionUsers) && !SecurityUtils.getLoginUser().getUser().isAdmin() && !SecurityUtils.getLoginUser().getUser().getUserId().equals("794aa2c8b5c24933a30591dd7dc439ed") && !SecurityUtils.getLoginUser().getUser().getUserId().equals("ca1df7d1a3f347dc9e73e8283dd134a5") && !SecurityUtils.getLoginUser().getUser().getUserId().equals("3cf3b0fa6c0945919504be4aa237f89e")) {
List<String> twos = gridRegionUsers.stream().map(GridRegionUser::getWgId).collect(Collectors.toList());
for (String two : twos) {
for (HouseResourcePage houseResourcePage : list) { for (HouseResourcePage houseResourcePage : list) {
if (two.equals(houseResourcePage.getTwo())) { if (two.equals(houseResourcePage.getTwo())) {
result.add(houseResourcePage); result.add(houseResourcePage);
...@@ -640,154 +626,147 @@ public class HouseResourceServiceImpl implements HouseResourceService { ...@@ -640,154 +626,147 @@ public class HouseResourceServiceImpl implements HouseResourceService {
return result ; return result ;
} }
/** HOUSE_RESOURCE 按 type 展示时固定补齐的档位,缺失档位会补一个 count = 0 的桶。 */
private static final List<Integer> HOUSE_RESOURCE_TYPE_BUCKETS = Arrays.asList(1, 4, 5, 6, 7);
@Override @Override
public HouseResourceDataCollection houseResourceDataCollection(HouseResourceDataCollectionQuery query) { public HouseResourceDataCollection houseResourceDataCollection(HouseResourceDataCollectionQuery query) {
HouseResourceDataCollection houseResourceDataCollection = new HouseResourceDataCollection(); if (query.getChangeStartTime() == null || query.getChangeEndTime() == null) {
List<HouseResourceDataCollectionSimpleObject> newObjectList = houseResourceMapper.selectHouseResourceByCreateTime(query); throw new IllegalArgumentException("changeStartTime / changeEndTime 不能为空");
if (CollectionUtils.isEmpty(newObjectList)) { }
List<HouseResourceDataCollectionSimpleObject> emptyNewObjectList = new ArrayList<>(); if (query.getChangeStartTime().after(query.getChangeEndTime())) {
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject(); throw new IllegalArgumentException("changeStartTime 不能晚于 changeEndTime");
simpleObject1.setType(1); }
emptyNewObjectList.add(simpleObject1);
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject(); applyGridPermission(query);
simpleObject4.setType(4);
emptyNewObjectList.add(simpleObject4); HouseResourceDataCollection result = new HouseResourceDataCollection();
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject();
simpleObject5.setType(5); // HOUSE_RESOURCE: insert / update 出详情(带 ids),delete 只出 count。
emptyNewObjectList.add(simpleObject5); List<HouseResourceDataCollectionDetailRow> houseDetails =
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject(); houseResourceMapper.selectHouseResourceChangeDetails(query);
simpleObject6.setType(6); List<HouseResourceDataCollectionRow> houseDeleteRows =
emptyNewObjectList.add(simpleObject6); houseResourceMapper.selectHouseResourceDeleteCount(query);
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject();
simpleObject7.setType(7); result.setHouseResourceInsert(buildHouseTypeBuckets(houseDetails, OperationType.INSERT.getCode(), true));
emptyNewObjectList.add(simpleObject7); result.setHouseResourceUpdate(buildHouseTypeBuckets(houseDetails, OperationType.UPDATE.getCode(), true));
houseResourceDataCollection.setNewObject(emptyNewObjectList); result.setHouseResourceDelete(buildHouseTypeBucketsFromCount(houseDeleteRows));
} else {
Map<Integer, Long> collect1 = newObjectList.stream().collect(Collectors.toMap(HouseResourceDataCollectionSimpleObject::getType, HouseResourceDataCollectionSimpleObject::getCount)); // BUSINESS_ENTITY_INFO: insert / update 拿 ids + count;delete 只拿 count。
if (null == collect1.get(1)) { List<HouseResourceDataCollectionDetailRow> entityDetails =
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject(); houseResourceMapper.selectBusinessEntityChangeDetails(query);
simpleObject1.setType(1); Map<Integer, List<String>> entityIdsByOp = entityDetails.stream()
newObjectList.add(simpleObject1); .collect(Collectors.groupingBy(
} HouseResourceDataCollectionDetailRow::getOperationType,
if (null == collect1.get(4)) { Collectors.mapping(HouseResourceDataCollectionDetailRow::getSubjectId, Collectors.toList())));
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject(); List<String> entityInsertIds = entityIdsByOp.getOrDefault(OperationType.INSERT.getCode(), Collections.emptyList());
simpleObject4.setType(4); List<String> entityUpdateIds = entityIdsByOp.getOrDefault(OperationType.UPDATE.getCode(), Collections.emptyList());
newObjectList.add(simpleObject4); result.setBusinessEntityInsertIds(entityInsertIds);
} result.setBusinessEntityInsertCount(entityInsertIds.size());
if (null == collect1.get(5)) { result.setBusinessEntityUpdateIds(entityUpdateIds);
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject(); result.setBusinessEntityUpdateCount(entityUpdateIds.size());
simpleObject5.setType(5);
newObjectList.add(simpleObject5); long entityDeleteCount = houseResourceMapper.selectBusinessEntityDeleteCount(query).stream()
} .mapToLong(HouseResourceDataCollectionRow::getCount)
if (null == collect1.get(6)) { .sum();
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject(); result.setBusinessEntityDeleteCount(entityDeleteCount);
simpleObject6.setType(6);
newObjectList.add(simpleObject6); return result;
} }
if (null == collect1.get(7)) {
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject(); /**
simpleObject7.setType(7); * 无需网格限制的登录人白名单(即便不是 admin 也可以看全量)。
newObjectList.add(simpleObject7); * 目前只给 {@link #pageHouseResources}、{@link #businessEntityStatistics}、{@link #houseResourceDataCollection}
} * 这类"列表/汇总"场景启用;{@link #exportListHouseResources} 保持原有行为不放开。
houseResourceDataCollection.setNewObject(newObjectList); */
} private static final Set<String> WG_PERMISSION_BYPASS_USER_IDS = new HashSet<>(Arrays.asList(
List<HouseResourceDataCollectionSimpleObject> editObjectList = houseResourceMapper.selectHouseResourceByUpdateTime(query); "794aa2c8b5c24933a30591dd7dc439ed",
if (CollectionUtils.isEmpty(editObjectList)) { "ca1df7d1a3f347dc9e73e8283dd134a5",
List<HouseResourceDataCollectionSimpleObject> emptyEditObjectList = new ArrayList<>(); "3cf3b0fa6c0945919504be4aa237f89e"));
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject();
simpleObject1.setType(1); /**
emptyEditObjectList.add(simpleObject1); * 计算当前登录人应被限制到的 wgCodes 列表。返回 {@code null} 表示无需限制(全量可见),
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject(); * 返回非 {@code null} 表示需要把后续查询限定在这些网格内。
simpleObject4.setType(4); *
emptyEditObjectList.add(simpleObject4); * <p>短路条件(任意命中即不限制):</p>
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject(); * <ul>
simpleObject5.setType(5); * <li>登录人没有任何有效的 {@code grid_region_user} 绑定;</li>
emptyEditObjectList.add(simpleObject5); * <li>登录人是 admin;</li>
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject(); * <li>{@code applyBypassWhitelist = true} 且登录人在 {@link #WG_PERMISSION_BYPASS_USER_IDS} 里。</li>
simpleObject6.setType(6); * </ul>
emptyEditObjectList.add(simpleObject6); *
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject(); * @param applyBypassWhitelist 是否启用白名单短路。列表/汇总类场景传 {@code true};
simpleObject7.setType(7); * 导出等严格场景传 {@code false} 保持原行为。
emptyEditObjectList.add(simpleObject7); */
houseResourceDataCollection.setEditObject(emptyEditObjectList); private List<String> resolveCurrentUserWgCodes(boolean applyBypassWhitelist) {
} else { String userId = SecurityUtils.getLoginUser().getUser().getUserId();
Map<Integer, Long> collect2 = editObjectList.stream().collect(Collectors.toMap(HouseResourceDataCollectionSimpleObject::getType, HouseResourceDataCollectionSimpleObject::getCount)); GridRegionUserExample example = new GridRegionUserExample();
if (null == collect2.get(1)) { example.createCriteria().andIsValidEqualTo("1").andUserIdEqualTo(userId);
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject(); List<GridRegionUser> gridRegionUsers = gridRegionUserMapper.selectByExample(example);
simpleObject1.setType(1); if (CollectionUtils.isEmpty(gridRegionUsers) || SecurityUtils.getLoginUser().getUser().isAdmin()) {
editObjectList.add(simpleObject1); return null;
} }
if (null == collect2.get(4)) { if (applyBypassWhitelist && WG_PERMISSION_BYPASS_USER_IDS.contains(userId)) {
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject(); return null;
simpleObject4.setType(4); }
editObjectList.add(simpleObject4); return gridRegionUsers.stream().map(GridRegionUser::getWgId).collect(Collectors.toList());
} }
if (null == collect2.get(5)) {
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject(); /**
simpleObject5.setType(5); * 给 dataCollection 查询注入当前登录人的网格权限(与 pageHouseResources 保持一致)。
editObjectList.add(simpleObject5); */
} private void applyGridPermission(HouseResourceDataCollectionQuery query) {
if (null == collect2.get(6)) { List<String> wgCodes = resolveCurrentUserWgCodes(true);
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject(); if (wgCodes != null) {
simpleObject6.setType(6); query.setWgCodes(wgCodes);
editObjectList.add(simpleObject6);
}
if (null == collect2.get(7)) {
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject();
simpleObject7.setType(7);
editObjectList.add(simpleObject7);
}
houseResourceDataCollection.setEditObject(editObjectList);
}
List<HouseResourceDataCollectionSimpleObject> deleteObjectList = houseResourceMapper.selectHouseResourceByUpdateTimeDeleteFlag(query);
if (CollectionUtils.isEmpty(deleteObjectList)) {
List<HouseResourceDataCollectionSimpleObject> emptyDeleteObjectList = new ArrayList<>();
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject();
simpleObject1.setType(1);
emptyDeleteObjectList.add(simpleObject1);
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject();
simpleObject4.setType(4);
emptyDeleteObjectList.add(simpleObject4);
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject();
simpleObject5.setType(5);
emptyDeleteObjectList.add(simpleObject5);
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject();
simpleObject6.setType(6);
emptyDeleteObjectList.add(simpleObject6);
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject();
simpleObject7.setType(7);
emptyDeleteObjectList.add(simpleObject7);
houseResourceDataCollection.setDeleteObject(emptyDeleteObjectList);
} else {
Map<Integer, Long> collect3 = deleteObjectList.stream().collect(Collectors.toMap(HouseResourceDataCollectionSimpleObject::getType, HouseResourceDataCollectionSimpleObject::getCount));
if (null == collect3.get(1)) {
HouseResourceDataCollectionSimpleObject simpleObject1 = new HouseResourceDataCollectionSimpleObject();
simpleObject1.setType(1);
deleteObjectList.add(simpleObject1);
}
if (null == collect3.get(4)) {
HouseResourceDataCollectionSimpleObject simpleObject4 = new HouseResourceDataCollectionSimpleObject();
simpleObject4.setType(4);
deleteObjectList.add(simpleObject4);
}
if (null == collect3.get(5)) {
HouseResourceDataCollectionSimpleObject simpleObject5 = new HouseResourceDataCollectionSimpleObject();
simpleObject5.setType(5);
deleteObjectList.add(simpleObject5);
}
if (null == collect3.get(6)) {
HouseResourceDataCollectionSimpleObject simpleObject6 = new HouseResourceDataCollectionSimpleObject();
simpleObject6.setType(6);
deleteObjectList.add(simpleObject6);
}
if (null == collect3.get(7)) {
HouseResourceDataCollectionSimpleObject simpleObject7 = new HouseResourceDataCollectionSimpleObject();
simpleObject7.setType(7);
deleteObjectList.add(simpleObject7);
}
houseResourceDataCollection.setDeleteObject(deleteObjectList);
} }
return houseResourceDataCollection; }
/**
* 按 HOUSE_RESOURCE_TYPE_BUCKETS 把指定 operation 下的详情行聚合成 {type, count, ids} 桶。
*
* @param rows 详情行列表(可能包含多个 operation 的行)
* @param operationType 本次要筛出的 operation
* @param includeIds true 填充 ids,false 只填 count(当前调用点都传 true)
*/
private List<HouseResourceDataCollectionSimpleObject> buildHouseTypeBuckets(
List<HouseResourceDataCollectionDetailRow> rows, int operationType, boolean includeIds) {
Map<Integer, List<String>> idsByType = rows.stream()
.filter(r -> r.getOperationType() == operationType)
.collect(Collectors.groupingBy(
HouseResourceDataCollectionDetailRow::getType,
Collectors.mapping(HouseResourceDataCollectionDetailRow::getSubjectId, Collectors.toList())));
List<HouseResourceDataCollectionSimpleObject> list = new ArrayList<>(HOUSE_RESOURCE_TYPE_BUCKETS.size());
for (Integer type : HOUSE_RESOURCE_TYPE_BUCKETS) {
List<String> ids = idsByType.getOrDefault(type, Collections.emptyList());
HouseResourceDataCollectionSimpleObject item = new HouseResourceDataCollectionSimpleObject();
item.setType(type);
item.setCount(ids.size());
if (includeIds) {
item.setIds(ids);
}
list.add(item);
}
return list;
}
/**
* delete 桶从聚合 count 行构造,不填 ids。同样按 HOUSE_RESOURCE_TYPE_BUCKETS 补齐。
*/
private List<HouseResourceDataCollectionSimpleObject> buildHouseTypeBucketsFromCount(
List<HouseResourceDataCollectionRow> rows) {
Map<Integer, Long> byType = rows.stream()
.collect(Collectors.toMap(HouseResourceDataCollectionRow::getType,
HouseResourceDataCollectionRow::getCount,
Long::sum));
List<HouseResourceDataCollectionSimpleObject> list = new ArrayList<>(HOUSE_RESOURCE_TYPE_BUCKETS.size());
for (Integer type : HOUSE_RESOURCE_TYPE_BUCKETS) {
HouseResourceDataCollectionSimpleObject item = new HouseResourceDataCollectionSimpleObject();
item.setType(type);
item.setCount(byType.getOrDefault(type, 0L));
list.add(item);
}
return list;
} }
private List<HouseResourcePage> handleHouseList(List<HouseResourcePage> list, boolean houseNumberSortFlag) { private List<HouseResourcePage> handleHouseList(List<HouseResourcePage> list, boolean houseNumberSortFlag) {
......
...@@ -245,6 +245,12 @@ ...@@ -245,6 +245,12 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
<if test="query.ids != null and query.ids.size() > 0">
and t1.id in
<foreach item="hid" collection="query.ids" open="(" separator="," close=")">
#{hid}
</foreach>
</if>
<if test="query.createTimeStart != null"> <if test="query.createTimeStart != null">
AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart} AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart}
</if> </if>
...@@ -426,6 +432,12 @@ ...@@ -426,6 +432,12 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
<if test="query.ids != null and query.ids.size() > 0">
and t1.id in
<foreach item="hid" collection="query.ids" open="(" separator="," close=")">
#{hid}
</foreach>
</if>
<if test="query.createTimeStart != null"> <if test="query.createTimeStart != null">
AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart} AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart}
</if> </if>
...@@ -604,6 +616,12 @@ ...@@ -604,6 +616,12 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
<if test="query.ids != null and query.ids.size() > 0">
and t1.id in
<foreach item="hid" collection="query.ids" open="(" separator="," close=")">
#{hid}
</foreach>
</if>
<if test="query.createTimeStart != null"> <if test="query.createTimeStart != null">
AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart} AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart}
</if> </if>
...@@ -805,6 +823,12 @@ ...@@ -805,6 +823,12 @@
#{item} #{item}
</foreach> </foreach>
</if> </if>
<if test="query.ids != null and query.ids.size() > 0">
and t1.id in
<foreach item="hid" collection="query.ids" open="(" separator="," close=")">
#{hid}
</foreach>
</if>
<if test="query.createTimeStart != null"> <if test="query.createTimeStart != null">
AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart} AND t1.create_time <![CDATA[ >= ]]> #{query.createTimeStart}
</if> </if>
...@@ -891,82 +915,152 @@ ...@@ -891,82 +915,152 @@
#{item} #{item}
</foreach> </foreach>
</select> </select>
<select id="selectHouseResourceByCreateTime" <!--
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionSimpleObject"> 时间窗内房源(HOUSE_RESOURCE)的新增 / 修改**详情**行:每行一个 (type, operation_type, subject_id)。
SELECT 强制 hr.delete_flag = 0,保证回传的 id 一定能被 /api/house/page 的 query.ids 命中。
type, DISTINCT 去除 dcr 里同一房源在同一 op 下多条操作日志(连续多次修改)的重复。
COUNT(*) AS count -->
FROM <select id="selectHouseResourceChangeDetails"
house_resource resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionDetailRow">
WHERE delete_flag = 0 SELECT DISTINCT
<if test="query.two != null and query.two != ''"> hr.type AS type,
AND two = #{query.two} dcr.operation_type AS operationType,
</if> dcr.subject_id AS subjectId
<if test="query.three != null and query.three != ''"> FROM data_change_record dcr
AND three = #{query.three} INNER JOIN house_resource hr ON hr.id = dcr.subject_id AND hr.delete_flag = 0
</if> WHERE dcr.subject_type = 'HOUSE_RESOURCE'
<if test="query.four != null and query.four != ''"> AND dcr.operation_type IN (1, 2)
AND four = #{query.four} AND dcr.operation_time <![CDATA[ >= ]]> #{query.changeStartTime}
</if> AND dcr.operation_time <![CDATA[ <= ]]> #{query.changeEndTime}
<if test="query.createTimeStart != null"> <if test="query.two != null and query.two != ''">
AND create_time <![CDATA[ >= ]]> #{query.createTimeStart} AND hr.two = #{query.two}
</if> </if>
<if test="query.createTimeEnd != null"> <if test="query.three != null and query.three != ''">
AND create_time <![CDATA[ <= ]]> #{query.createTimeEnd} AND hr.three = #{query.three}
</if> </if>
GROUP BY <if test="query.four != null and query.four != ''">
type AND hr.four = #{query.four}
</if>
<if test="query.wgCodes != null and query.wgCodes.size() > 0">
AND hr.two IN
<foreach collection="query.wgCodes" item="wg" open="(" separator="," close=")">#{wg}</foreach>
</if>
</select> </select>
<select id="selectHouseResourceByUpdateTime"
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionSimpleObject"> <!--
时间窗内房源(HOUSE_RESOURCE)的**删除**计数,按 hr.type 分组。
不过滤 hr.delete_flag(已逻辑删除的房源也要统计);不返回 subject_id,因为
前端不需要对已删除数据做 drilldown。
-->
<select id="selectHouseResourceDeleteCount"
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionRow">
SELECT SELECT
type, hr.type AS type,
COUNT(*) AS count 3 AS operationType,
FROM COUNT(DISTINCT dcr.subject_id) AS count
house_resource FROM data_change_record dcr
WHERE delete_flag = 0 INNER JOIN house_resource hr ON hr.id = dcr.subject_id
<if test="query.two != null and query.two != ''"> WHERE dcr.subject_type = 'HOUSE_RESOURCE'
AND two = #{query.two} AND dcr.operation_type = 3
</if> AND dcr.operation_time <![CDATA[ >= ]]> #{query.changeStartTime}
<if test="query.three != null and query.three != ''"> AND dcr.operation_time <![CDATA[ <= ]]> #{query.changeEndTime}
AND three = #{query.three} <if test="query.two != null and query.two != ''">
</if> AND hr.two = #{query.two}
<if test="query.four != null and query.four != ''"> </if>
AND four = #{query.four} <if test="query.three != null and query.three != ''">
</if> AND hr.three = #{query.three}
<if test="query.updateTimeStart != null"> </if>
AND update_time <![CDATA[ >= ]]> #{query.updateTimeStart} <if test="query.four != null and query.four != ''">
</if> AND hr.four = #{query.four}
<if test="query.updateTimeEnd != null"> </if>
AND update_time <![CDATA[ <= ]]> #{query.updateTimeEnd} <if test="query.wgCodes != null and query.wgCodes.size() > 0">
</if> AND hr.two IN
GROUP BY <foreach collection="query.wgCodes" item="wg" open="(" separator="," close=")">#{wg}</foreach>
type </if>
GROUP BY hr.type
</select>
<!--
时间窗内经营主体(BUSINESS_ENTITY_INFO)的新增 / 修改**详情**行:每行一个 (operation_type, subject_id)。
强制 bei.delete_flag = 0 以保证 id 回传后能被 drilldown 查到。
网格过滤(two / three / four / wgCodes)通过 EXISTS 子查询用 mapping + house_resource 实现:
"关联到至少一个匹配网格的、仍有效的房源" 才计入。这里 mapping 和 hr2 也限制 delete_flag = 0,
确保 id 回传 drilldown 时 JOIN 链路能走通。
-->
<select id="selectBusinessEntityChangeDetails"
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionDetailRow">
SELECT DISTINCT
0 AS type,
dcr.operation_type AS operationType,
dcr.subject_id AS subjectId
FROM data_change_record dcr
INNER JOIN business_entity_info bei ON bei.id = dcr.subject_id AND bei.delete_flag = 0
WHERE dcr.subject_type = 'BUSINESS_ENTITY_INFO'
AND dcr.operation_type IN (1, 2)
AND dcr.operation_time <![CDATA[ >= ]]> #{query.changeStartTime}
AND dcr.operation_time <![CDATA[ <= ]]> #{query.changeEndTime}
<if test="(query.two != null and query.two != '') or (query.three != null and query.three != '') or (query.four != null and query.four != '') or (query.wgCodes != null and query.wgCodes.size() > 0)">
AND EXISTS (
SELECT 1
FROM house_resource_business_entity_info_mapping m
INNER JOIN house_resource hr2 ON hr2.id = m.house_resource_id AND hr2.delete_flag = 0
WHERE m.business_entity_info_id = bei.id
AND m.delete_flag = 0
<if test="query.two != null and query.two != ''">
AND hr2.two = #{query.two}
</if>
<if test="query.three != null and query.three != ''">
AND hr2.three = #{query.three}
</if>
<if test="query.four != null and query.four != ''">
AND hr2.four = #{query.four}
</if>
<if test="query.wgCodes != null and query.wgCodes.size() > 0">
AND hr2.two IN
<foreach collection="query.wgCodes" item="wg" open="(" separator="," close=")">#{wg}</foreach>
</if>
)
</if>
</select> </select>
<select id="selectHouseResourceByUpdateTimeDeleteFlag"
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionSimpleObject"> <!--
时间窗内经营主体(BUSINESS_ENTITY_INFO)的**删除**计数,只返回一行 count。
不过滤 bei.delete_flag。网格过滤使用不限制 delete_flag 的 EXISTS——"历史上曾关联到
匹配网格的房源"即计入,覆盖"经营主体删除前房源或 mapping 已经被解除"的场景。
-->
<select id="selectBusinessEntityDeleteCount"
resultType="com.ruoyi.system.domain.house.vo.HouseResourceDataCollectionRow">
SELECT SELECT
type, 0 AS type,
COUNT(*) AS count 3 AS operationType,
FROM COUNT(DISTINCT dcr.subject_id) AS count
house_resource FROM data_change_record dcr
WHERE delete_flag = 1 INNER JOIN business_entity_info bei ON bei.id = dcr.subject_id
<if test="query.two != null and query.two != ''"> WHERE dcr.subject_type = 'BUSINESS_ENTITY_INFO'
AND two = #{query.two} AND dcr.operation_type = 3
</if> AND dcr.operation_time <![CDATA[ >= ]]> #{query.changeStartTime}
<if test="query.three != null and query.three != ''"> AND dcr.operation_time <![CDATA[ <= ]]> #{query.changeEndTime}
AND three = #{query.three} <if test="(query.two != null and query.two != '') or (query.three != null and query.three != '') or (query.four != null and query.four != '') or (query.wgCodes != null and query.wgCodes.size() > 0)">
</if> AND EXISTS (
<if test="query.four != null and query.four != ''"> SELECT 1
AND four = #{query.four} FROM house_resource_business_entity_info_mapping m
</if> INNER JOIN house_resource hr2 ON hr2.id = m.house_resource_id
<if test="query.updateTimeStart != null"> WHERE m.business_entity_info_id = bei.id
AND update_time <![CDATA[ >= ]]> #{query.updateTimeStart} <if test="query.two != null and query.two != ''">
</if> AND hr2.two = #{query.two}
<if test="query.updateTimeEnd != null"> </if>
AND update_time <![CDATA[ <= ]]> #{query.updateTimeEnd} <if test="query.three != null and query.three != ''">
</if> AND hr2.three = #{query.three}
GROUP BY </if>
type <if test="query.four != null and query.four != ''">
AND hr2.four = #{query.four}
</if>
<if test="query.wgCodes != null and query.wgCodes.size() > 0">
AND hr2.two IN
<foreach collection="query.wgCodes" item="wg" open="(" separator="," close=")">#{wg}</foreach>
</if>
)
</if>
</select> </select>
</mapper> </mapper>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment