fix(public): unify family representative selection with detail endpoint
List/home endpoints picked the family representative row via goods[0], which drifted with the list sort parameter (cheapest under PRICE_ASC, lowest id under DEFAULT ties), causing goodName and other representative-derived fields to differ from the detail endpoint. Extract pickFamilyRepresentative (goodPriority desc -> createdAt desc -> id asc, same as getGoodByFamilyId) and use it in getGoods and getHomeGoods grouping. Group ordering and API contracts unchanged.
This commit is contained in:
@@ -254,7 +254,7 @@ export class PublicService {
|
||||
else grouped.set(key, [good]);
|
||||
}
|
||||
let items = [...grouped.values()].map((goods) => {
|
||||
const rep = goods[0];
|
||||
const rep = this.pickFamilyRepresentative(goods);
|
||||
const dto = this.toPublicGood(rep);
|
||||
// 列表价 = 族矩阵最低价("这个款之下有哪些价格"的起价);无矩阵回退链接价
|
||||
const familyMin = rep.familyId
|
||||
@@ -421,16 +421,20 @@ export class PublicService {
|
||||
take: query.limit,
|
||||
});
|
||||
const familyMinPrices = await this.loadFamilyMinPrices();
|
||||
// 首页同样按族去重(同族多条位置配置只保留排序最前一条),再截取 limit
|
||||
const seen = new Set<string>();
|
||||
const items: PublicGoodDto[] = [];
|
||||
// 首页同样按族去重(一族只出一条),代表行选取与详情/列表一致
|
||||
const grouped = new Map<string, PublicGoodListRow[]>();
|
||||
for (const good of rows) {
|
||||
const key = good.familyId ? `f:${good.familyId}` : `g:${good.id}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
const dto = this.toPublicGood(good);
|
||||
const familyMin = good.familyId
|
||||
? familyMinPrices.get(good.familyId.toString())
|
||||
const bucket = grouped.get(key);
|
||||
if (bucket) bucket.push(good);
|
||||
else grouped.set(key, [good]);
|
||||
}
|
||||
const items: PublicGoodDto[] = [];
|
||||
for (const goods of grouped.values()) {
|
||||
const rep = this.pickFamilyRepresentative(goods);
|
||||
const dto = this.toPublicGood(rep);
|
||||
const familyMin = rep.familyId
|
||||
? familyMinPrices.get(rep.familyId.toString())
|
||||
: undefined;
|
||||
if (familyMin !== undefined) dto.price = familyMin;
|
||||
items.push(dto);
|
||||
@@ -438,6 +442,22 @@ export class PublicService {
|
||||
return items.slice(0, query.limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 族代表行选取:与详情 getGoodByFamilyId 的 orderBy 保持一致
|
||||
* (goodPriority desc → createdAt desc → id asc),
|
||||
* 保证列表/首页与详情返回的 goodName、主图、分类等代表行字段一致。
|
||||
*/
|
||||
private pickFamilyRepresentative<T extends { goodPriority: number | null; createdAt: Date; id: bigint }>(
|
||||
goods: T[],
|
||||
): T {
|
||||
return [...goods].sort(
|
||||
(a, b) =>
|
||||
(b.goodPriority ?? 0) - (a.goodPriority ?? 0) ||
|
||||
b.createdAt.getTime() - a.createdAt.getTime() ||
|
||||
(a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
||||
)[0];
|
||||
}
|
||||
|
||||
/** 入参用精简行类型:列表(PublicGoodListRow)与详情(PublicGoodRow,字段超集)都能传 */
|
||||
private toPublicGood(good: PublicGoodListRow): PublicGoodDto {
|
||||
const formatGroup = (group: { id: bigint; groupName: string; sortOrder: number } | null) =>
|
||||
|
||||
Reference in New Issue
Block a user