feat(撞款+用户): ①扫描基线与店铺表全量对齐——无结果文件的店以空态(0记录+分组标签)注入扫描,撞款控制台超管可见全部店铺(实测 shop_count 83=全量) ②用户列表支持 role 筛选参数 ③修复 toItem creatorMap.getOrDefault(null) 在不可变 Map 的 NPE
This commit is contained in:
+3
-2
@@ -38,11 +38,12 @@ public class AdminUserController {
|
||||
@RequestParam(required = false) String username,
|
||||
@RequestParam(name = "search", required = false) String search,
|
||||
@RequestParam(name = "created_by_id", required = false) Long createdById,
|
||||
@RequestParam(name = "admin_id", required = false) Long adminId) {
|
||||
@RequestParam(name = "admin_id", required = false) Long adminId,
|
||||
@RequestParam(name = "role", required = false) String role) {
|
||||
AdminUserEntity currentUser = adminAuthSupport.requireAdminOrInternal(request);
|
||||
String kw = (username == null || username.isBlank()) ? search : username;
|
||||
Long filterCreatedBy = createdById != null ? createdById : adminId;
|
||||
AdminUserListVo vo = adminUserService.listUsers(currentUser, page, pageSize, kw, filterCreatedBy);
|
||||
AdminUserListVo vo = adminUserService.listUsers(currentUser, page, pageSize, kw, filterCreatedBy, role);
|
||||
return ApiResponse.success(vo);
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -42,7 +42,7 @@ public class AdminUserService {
|
||||
private final PermissionMenuService permissionMenuService;
|
||||
|
||||
public AdminUserListVo listUsers(AdminUserEntity currentUser, Integer page, Integer pageSize,
|
||||
String username, Long createdById) {
|
||||
String username, Long createdById, String roleFilter) {
|
||||
String role = adminAuthSupport.currentRole(currentUser);
|
||||
if (role == null) {
|
||||
throw new BusinessException(403, "需要管理员权限");
|
||||
@@ -60,6 +60,10 @@ public class AdminUserService {
|
||||
Long filterCreatedBy = "super_admin".equals(role) ? createdById : null;
|
||||
|
||||
LambdaQueryWrapper<AdminUserEntity> query = new LambdaQueryWrapper<>();
|
||||
String roleFilterValue = roleFilter == null ? "" : roleFilter.trim();
|
||||
if (!roleFilterValue.isEmpty()) {
|
||||
query.eq(AdminUserEntity::getRole, roleFilterValue);
|
||||
}
|
||||
if ("super_admin".equals(role)) {
|
||||
if (!kw.isEmpty()) {
|
||||
query.like(AdminUserEntity::getUsername, kw);
|
||||
@@ -318,7 +322,9 @@ public class AdminUserService {
|
||||
vo.setAdmin(entity.getIsAdmin() != null && entity.getIsAdmin() == 1);
|
||||
vo.setRole(entity.getRole() == null || entity.getRole().isEmpty() ? "normal" : entity.getRole());
|
||||
vo.setCreatedById(entity.getCreatedById());
|
||||
vo.setCreatorUsername(creatorMap.getOrDefault(entity.getCreatedById(), ""));
|
||||
// 不可变 Map(Map.of) 的 getOrDefault 不允许 null key:createdById 为 null 时直接空串。
|
||||
Long creatorId = entity.getCreatedById();
|
||||
vo.setCreatorUsername(creatorId == null ? "" : creatorMap.getOrDefault(creatorId, ""));
|
||||
LocalDateTime createdAt = entity.getCreatedAt();
|
||||
vo.setCreatedAt(createdAt == null ? "" : createdAt.format(CREATED_AT_FORMATTER));
|
||||
vo.setPinyinAbbr(PinyinAbbrUtil.abbr(entity.getUsername()));
|
||||
|
||||
+15
@@ -68,6 +68,21 @@ public interface ShopDuplicateCheckSourceMapper {
|
||||
""")
|
||||
List<ShopGroupLabelDto> selectGroupLabels(@Param("shopNames") List<String> shopNames);
|
||||
|
||||
/**
|
||||
* 全量店铺名+分组标签(biz_shop_manage 全集):扫描基线对齐店铺表——
|
||||
* 无结果文件的店以空态(0 记录)参与扫描,保证撞款控制台超管可见全部分组店铺。
|
||||
*/
|
||||
@Select("""
|
||||
SELECT TRIM(sm.shop_name) AS shopName,
|
||||
GROUP_CONCAT(DISTINCT COALESCE(NULLIF(g.group_name, ''), NULLIF(sm.group_name, ''))
|
||||
ORDER BY sm.id SEPARATOR '、') AS groupName
|
||||
FROM biz_shop_manage sm
|
||||
LEFT JOIN biz_shop_manage_group g ON g.id = sm.group_id
|
||||
WHERE TRIM(COALESCE(sm.shop_name, '')) <> ''
|
||||
GROUP BY TRIM(sm.shop_name)
|
||||
""")
|
||||
List<ShopGroupLabelDto> selectAllShopLabels();
|
||||
|
||||
/**
|
||||
* 主管(admin 角色)可管店铺:自己创建或指派给自己的分组(created_by_id=me OR user_id=me)
|
||||
* 下的店铺名,trim 后去重。无分组时返回空集合(什么都看不到),与 Python 语义一致。
|
||||
|
||||
+20
-1
@@ -26,6 +26,7 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -214,7 +215,8 @@ public class ShopDataDuplicateCheckScanService {
|
||||
return message == null || message.isBlank() ? ex.getClass().getSimpleName() : message;
|
||||
}
|
||||
|
||||
/** 逐店并发拉取/解析结果文件;单店失败仅告警跳过,不中断整体。 */
|
||||
/** 逐店并发拉取/解析结果文件;单店失败仅告警跳过,不中断整体。
|
||||
* 扫描基线与店铺表全量对齐:无结果文件的店以空态(0 记录)注入,保证撞款控制台可见全部店铺。 */
|
||||
private List<ShopParsed> collectShopParsed(DistributedJobLockService.LockHandle lock) {
|
||||
List<ShopSourceRowDto> rows = sourceMapper.selectLatestResultRows();
|
||||
List<ShopParsed> parsedShops = new ArrayList<>();
|
||||
@@ -222,6 +224,7 @@ public class ShopDataDuplicateCheckScanService {
|
||||
return parsedShops;
|
||||
}
|
||||
Map<String, String> groupLabels = loadGroupLabels(rows);
|
||||
Set<String> parsedNames = new HashSet<>();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Math.min(PARSE_THREADS, rows.size()));
|
||||
try {
|
||||
List<Future<ShopParsed>> futures = new ArrayList<>(rows.size());
|
||||
@@ -236,6 +239,7 @@ public class ShopDataDuplicateCheckScanService {
|
||||
ShopParsed item = future.get();
|
||||
if (item != null) {
|
||||
parsedShops.add(item);
|
||||
parsedNames.add(normalizeShopName(item.shopName()));
|
||||
}
|
||||
} catch (ExecutionException ex) {
|
||||
log.warn("[shop-duplicate-check] 并发解析店铺结果文件异常: {}", ex.getCause() == null
|
||||
@@ -257,6 +261,21 @@ public class ShopDataDuplicateCheckScanService {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
// 基线扩展:店铺表全量中尚未解析到结果文件行的店 → 空态注入(0 记录、分组标签来自店铺表)。
|
||||
List<ShopGroupLabelDto> allShops = sourceMapper.selectAllShopLabels();
|
||||
int injectedEmptyShops = 0;
|
||||
for (ShopGroupLabelDto all : allShops) {
|
||||
String name = normalizeShopName(all.getShopName());
|
||||
if (name.isEmpty() || parsedNames.contains(name)) {
|
||||
continue;
|
||||
}
|
||||
String groupName = all.getGroupName() == null ? "" : all.getGroupName();
|
||||
parsedShops.add(new ShopParsed(name, groupName, List.of(), List.of()));
|
||||
injectedEmptyShops++;
|
||||
}
|
||||
if (injectedEmptyShops > 0) {
|
||||
log.info("[shop-duplicate-check] 扫描基线含空态店铺 {} 家(无结果文件,以 0 记录参与撞款视图)", injectedEmptyShops);
|
||||
}
|
||||
return parsedShops;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user