用户身份和代理数量bug修复

This commit is contained in:
hy001 2026-05-12 20:12:16 +08:00
parent c9ea77f762
commit 7238e8eb46
17 changed files with 456 additions and 53 deletions

View File

@ -37,4 +37,7 @@ public interface UserHistoryIdentityClientApi {
@GetMapping("/saveManager")
ResultResponse<Void> saveManager(@RequestParam("userId") Long userId);
@GetMapping("/saveYumiManager")
ResultResponse<Void> saveYumiManager(@RequestParam("userId") Long userId);
}

View File

@ -40,8 +40,13 @@ public class UserHistoryIdentityDTO implements Serializable {
private Boolean host;
/**
* 是否做过团队经理.
* 是否做过团队经理.
*/
private Boolean manager;
/**
* 是否做过 Yumi 团队经理.
*/
private Boolean yumiManager;
}

View File

@ -15,8 +15,6 @@ import com.red.circle.other.inner.endpoint.team.target.UserHistoryIdentityClient
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.enums.sys.appmanager.RoomRolesEnum;
import com.red.circle.other.inner.model.cmd.sys.AddSysAdministratorCmd;
import com.red.circle.other.inner.model.cmd.sys.SysAdministratorChangeStatusCmd;
import com.red.circle.other.inner.model.cmd.team.manager.TeamManagerOpsPageQryCmd;
import com.red.circle.other.inner.model.dto.sys.AdministratorDTO;
import com.red.circle.other.inner.model.dto.agency.manager.TeamManagerDTO;
@ -117,8 +115,7 @@ public class TeamManagerServiceImpl implements TeamManagerService {
.setCreateUser(cmd.getCreateUser());
ResponseAssert.requiredSuccess(teamManagerClient.add(addCmd));
ResponseAssert.requiredSuccess(userHistoryIdentityClient.saveManager(cmd.getUserId()));
ensureAppManager(cmd.getUserId(), cmd.getCreateUser());
ResponseAssert.requiredSuccess(userHistoryIdentityClient.saveYumiManager(cmd.getUserId()));
}
@Override
@ -138,36 +135,6 @@ public class TeamManagerServiceImpl implements TeamManagerService {
removeAppManagerIfOnlyManagerRole(db.getUserId());
}
private void ensureAppManager(Long userId, Long operatorUserId) {
AdministratorDTO administrator = ResponseAssert.requiredSuccess(
administratorClient.getByUserId(userId));
if (Objects.isNull(administrator)) {
AddSysAdministratorCmd addCmd = new AddSysAdministratorCmd();
addCmd.setUserId(userId);
addCmd.setRoles(RoomRolesEnum.MANAGER.name());
addCmd.setReqUserId(operatorUserId);
ResponseAssert.requiredSuccess(administratorClient.addSysAdministrator(addCmd));
administrator = ResponseAssert.requiredSuccess(administratorClient.getByUserId(userId));
}
ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, administrator);
if (StringUtils.isBlank(administrator.getRoles())
|| !Boolean.TRUE.equals(administrator.getStatus())) {
ResponseAssert.requiredSuccess(
administratorClient.changeRoles(administrator.getId(), RoomRolesEnum.MANAGER.name()));
}
if (!Boolean.TRUE.equals(administrator.getStatus())) {
SysAdministratorChangeStatusCmd statusCmd = new SysAdministratorChangeStatusCmd()
.setId(administrator.getId())
.setStatus(1);
statusCmd.setReqUserId(operatorUserId);
ResponseAssert.requiredSuccess(administratorClient.changeStatusAdministrator(statusCmd));
}
}
private void removeAppManagerIfOnlyManagerRole(Long userId) {
AdministratorDTO administrator = ResponseAssert.requiredSuccess(
administratorClient.getByUserId(userId));

View File

@ -287,9 +287,10 @@ public class UserBaseInfoServiceImpl implements
.setAnchor(Objects.nonNull(teamMember))
.setBd(ResponseAssert.requiredSuccess(bdTeamInfoClient.check(userId)))
.setBdLeader(ResponseAssert.requiredSuccess(bdTeamLeaderClient.check(userId)))
.setManager(ResponseAssert.requiredSuccess(teamManagerClient.check(userId)))
.setManager(Boolean.FALSE)
.setYumiManager(ResponseAssert.requiredSuccess(teamManagerClient.check(userId)))
.setFreightAgent(
ResponseAssert.requiredSuccess(freightGoldClient.checkFreightAgent(userId)))
ResponseAssert.requiredSuccess(freightGoldClient.checkFreightAgent(userId)))
.setHistoryIdentity(
ResponseAssert.requiredSuccess(historyIdentityClient.getLabels(userId)));
}

View File

@ -37,13 +37,18 @@ public class UserIdentityCO implements Serializable {
private Boolean bdLeader;
/**
* true团队经理false:不是.
* true团队经理false:不是.
*/
private Boolean manager;
/**
* trueYumi 团队经理false:不是.
*/
private Boolean yumiManager;
/**
* 货运代理: true: false:不是.
*/
*/
private Boolean freightAgent;
/**

View File

@ -0,0 +1,314 @@
# Admin 数据大屏游戏数据表技术方案
## 目标
在新 admin 的 `统计管理 / 数据大屏` 中增加独立的“游戏数据”表,用于按日期、国家、游戏维度查看消耗和收益数据。
页面查询不能直接扫描 `likei_wallet.wallet_gold_asset_record_1..12`、第三方回调日志或游戏明细表。所有 admin 查询默认读取游戏维度预聚合表;只有后台刷新任务和手动回填任务允许按时间窗口扫描原始数据。
## 现状
当前国家数据大屏入口:
- 前端路由:`chatapp3-admin/apps/src/router/routes/modules/statistics.ts`
- 前端页面:`chatapp3-admin/apps/src/views/statistics/datav.vue`
- 前端接口:`chatapp3-admin/apps/src/api/legacy/datav.ts`
- 后端 Controller`DatavRestController`
- 后端 Service`CountryDashboardServiceImpl`
- 后端 Mapper`CountryDashboardDAO.xml`
- 现有查询文档:`chatapp3-java/rc-service/rc-service-console/docs/country-dashboard-data-query.md`
现有大屏已经聚合了国家维度的 `game_total_flow``game_payout``game_user`,但没有保留游戏维度。新增功能不改动现有总览口径,新增一套游戏维度查询和预聚合表。
## 页面设计
`datav.vue` 的表格区域增加第三个 tab
- `指标总览`
- `充值相关数据`
- `游戏数据`
“游戏数据”表复用页面已有筛选项:
- 系统:`sysOrigin`
- 统计时区:`statTimezone`
- 周期:`periodType`
- 日期范围:`startDate/endDate`
- 国家多选:`countryCodes`
新增游戏筛选项:
- `gameProvider`:游戏厂商,支持 `INTERNAL``BAISHUN``GAME_OPEN``LINGXIAN`,可为空。
- `gameKeyword`:游戏 ID 或游戏名称模糊搜索。
表格字段:
| 字段 | 说明 |
| --- | --- |
| 日期/周期 | `periodKey`DAY 显示日期WEEK/MONTH/ALL 显示周期 |
| 国家 | `countryCode / countryName` |
| 游戏厂商 | `gameProvider` |
| 游戏 ID | `gameId` |
| 游戏名称 | `gameName` |
| 消耗 | 用户玩游戏支出,现有钱包口径为 `type = 1` |
| 返奖 | 游戏返还给用户,现有钱包口径为 `type = 0` |
| 收益 | `consumeAmount - payoutAmount` |
| 返奖率 | `payoutAmount / consumeAmount` |
| 收益率 | `profitAmount / consumeAmount` |
| 游戏用户数 | 周期内去重用户数 |
| 流水笔数 | 周期内消耗和返奖记录数 |
| 更新时间 | 预聚合刷新时间 |
表格必须服务端分页,默认 `limit=50`,最大 `limit=200`
## 后端接口
新增接口:
```http
GET /console/datav/country-dashboard/game-metrics
```
请求参数:
| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `sysOrigin` | 是 | 系统来源 |
| `periodType` | 是 | `DAY` / `WEEK` / `MONTH` / `ALL` |
| `startDate` | DAY/WEEK/MONTH 必填 | 查询开始日期 |
| `endDate` | DAY/WEEK/MONTH 必填 | 查询结束日期 |
| `statTimezone` | 否 | 默认 `Asia/Riyadh` |
| `countryCodes` | 否 | 多国家码,逗号分隔 |
| `countryKeyword` | 否 | 国家码或国家名搜索 |
| `gameProvider` | 否 | 游戏厂商 |
| `gameKeyword` | 否 | 游戏 ID 或名称搜索 |
| `cursor` | 否 | 页码,默认 1 |
| `limit` | 否 | 页大小,默认 50最大 200 |
| `sortField` | 否 | 允许 `consumeAmount/payoutAmount/profitAmount/userCount/orderCount` |
| `sortOrder` | 否 | `ascend/descend` |
响应结构:
```json
{
"current": 1,
"size": 50,
"total": 120,
"records": [
{
"periodKey": "2026-05-12",
"periodName": "2026-05-12",
"statTimezone": "Asia/Riyadh",
"countryCode": "SA",
"countryName": "Saudi Arabia",
"gameProvider": "BAISHUN",
"gameId": "1067",
"gameName": "example game",
"consumeAmount": "1000.00",
"payoutAmount": "720.00",
"profitAmount": "280.00",
"payoutRate": "72.00%",
"profitRate": "28.00%",
"userCount": 86,
"orderCount": 320,
"refreshedAt": "2026-05-12 10:20:00"
}
]
}
```
后端新增对象建议:
- `CountryDashboardGameMetricQryCmd`
- `CountryDashboardGameMetricCO`
- `CountryDashboardGameMetricDTO`
- `CountryDashboardGameMetricService`
- `CountryDashboardGameMetricDAO`
- `CountryDashboardGameMetricDAO.xml`
## 预聚合表
### `country_dashboard_game_period_metric`
页面直接查询这张表。
```sql
CREATE TABLE IF NOT EXISTS `country_dashboard_game_period_metric` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`period_type` varchar(16) NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
`period_key` varchar(32) NOT NULL COMMENT '周期键',
`stat_timezone` varchar(64) NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
`period_name` varchar(64) NOT NULL DEFAULT '' COMMENT '周期名称',
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
`sys_origin` varchar(32) NOT NULL DEFAULT '' COMMENT '系统来源',
`country_code` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
`country_name` varchar(128) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
`game_provider` varchar(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
`game_id` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
`game_name` varchar(128) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏名称',
`consume_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户消耗',
`payout_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户返奖',
`profit_amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '平台收益',
`user_count` bigint NOT NULL DEFAULT 0 COMMENT '去重游戏用户数',
`order_count` bigint NOT NULL DEFAULT 0 COMMENT '流水笔数',
`refreshed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近刷新时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_game_period` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `game_provider`, `game_id`),
KEY `idx_game_period_query` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `country_code`),
KEY `idx_game_lookup` (`sys_origin`, `stat_timezone`, `game_provider`, `game_id`, `period_type`, `period_key`),
KEY `idx_game_period_sort` (`sys_origin`, `stat_timezone`, `period_type`, `period_key`, `consume_amount`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期预聚合';
```
### `country_dashboard_game_period_user_metric`
用于 `WEEK/MONTH/ALL` 去重用户数,避免直接把日用户数相加导致重复。
```sql
CREATE TABLE IF NOT EXISTS `country_dashboard_game_period_user_metric` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`period_type` varchar(16) NOT NULL COMMENT '周期类型:DAY/WEEK/MONTH/ALL',
`period_key` varchar(32) NOT NULL COMMENT '周期键',
`stat_timezone` varchar(64) NOT NULL DEFAULT 'Asia/Riyadh' COMMENT '统计时区',
`period_name` varchar(64) NOT NULL DEFAULT '' COMMENT '周期名称',
`period_start_date` date DEFAULT NULL COMMENT '周期开始日期',
`period_end_date` date DEFAULT NULL COMMENT '周期结束日期,开区间',
`sys_origin` varchar(32) NOT NULL DEFAULT '' COMMENT '系统来源',
`country_code` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
`country_name` varchar(128) NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
`game_provider` varchar(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏厂商',
`game_id` varchar(64) NOT NULL DEFAULT 'UNKNOWN' COMMENT '游戏ID',
`user_id` bigint NOT NULL COMMENT '用户ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_game_period_user` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `country_code`, `game_provider`, `game_id`, `user_id`),
KEY `idx_game_user_count` (`period_type`, `period_key`, `stat_timezone`, `sys_origin`, `game_provider`, `game_id`, `country_code`),
KEY `idx_game_user_lookup` (`sys_origin`, `stat_timezone`, `user_id`, `game_provider`, `game_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期用户去重明细';
```
## 数据来源和游戏识别
以钱包流水作为财务主口径:
- `wallet_gold_asset_record_1..12.type = 1`:消耗
- `wallet_gold_asset_record_1..12.type = 0`:返奖
- 金额沿用现有大屏口径:`penny_amount / 100.00`
- `LUCKY_GIFT``LUCKY_GIFT_GOLD_REWARD` 继续归属幸运礼物模块,不并入游戏数据表。
游戏维度来源:
| 来源 | 识别方式 | 名称补全 |
| --- | --- | --- |
| 百顺 | 钱包 `event_id = baishun_order_idempotency.wallet_event_id`,取 `vendor_game_id`;兜底解析 `event_type = BAISHUN_GAME_{gameId}` | `baishun_game_catalog.name` |
| 统一三方游戏 | 钱包 `event_id = CONCAT('GAME_OPEN:', game_open_callback_log.order_id)`,取 `game_open_callback_log.game_id`;兜底解析 `event_type = GAME_OPEN_{gameId}` | `lingxian_game_catalog.name``sys_game_list_config.name` |
| Lucky Box | `game_lucky_box_count` 固定为 `gameProvider=INTERNAL, gameId=LUCKY_BOX` | 固定名称 `Lucky Box` |
| 其他站内游戏 | 按 `event_type` 映射稳定 `gameId`,例如 `FRUIT_GAME/GAME_FRUIT_* -> FRUIT_GAME``LUDO_* -> LUDO_GAME` | 代码内映射或后续配置表 |
如果某条流水无法识别具体游戏,落到:
- `gameProvider = UNKNOWN`
- `gameId = event_type`
- `gameName = event_type`
这样可以保证金额不丢失,同时便于后续补映射。
## 刷新和回填
新增刷新服务建议为 `CountryDashboardGameMetricService`
默认任务:
- 每 10 分钟刷新最近 1 天。
- 每天 00:30 刷新最近 8 天。
- 支持手动回填,单次最大 366 天,和现有国家大屏保持一致。
刷新逻辑:
1. 按 `sysOrigin``statTimezone` 计算周期窗口,转成存储时区时间窗口。
2. 扫描窗口内原始游戏流水,按 `period + country + gameProvider + gameId` 聚合金额和流水笔数。
3. 删除对应周期的 `country_dashboard_game_period_user_metric`,写入去重用户明细。
4. 从用户明细聚合 `user_count`
5. 删除并写入对应周期的 `country_dashboard_game_period_metric`
6. `ALL` 周期的金额从 `DAY` 周期表汇总,用户数从日级用户明细去重汇总。
刷新任务失败只影响游戏数据表,不影响现有国家大屏总览。
## 查询性能要求
页面查询只能访问 `country_dashboard_game_period_metric`,禁止在请求链路访问以下原始表:
- `likei_wallet.wallet_gold_asset_record_1..12`
- `game_open_callback_log`
- `baishun_order_idempotency`
- `game_lucky_box_count`
- 第三方游戏 catalog 表
分页查询必须满足:
- `sysOrigin` 必传。
- `periodType``statTimezone` 必须落到联合索引前缀。
- `DAY/WEEK/MONTH` 查询必须带日期范围,并转成 `period_key``period_start_date` 范围。
- `ALL` 只查 `period_type='ALL' AND period_key='ALL'`,不回退全历史实时扫描。
- `countryKeyword``gameKeyword` 只允许在已经命中周期范围之后过滤;大范围模糊搜索需要限制日期范围。
- 只允许白名单排序字段,避免动态 SQL 注入和无法使用索引的任意排序。
- `limit` 上限 200。
刷新任务扫描原始钱包分表时必须带:
- `sys_origin`
- `create_time` 毫秒闭开区间
- 游戏 `event_type` 白名单或前缀条件
现有 wallet 分表有 `idx_sorigin_etype_ctime(sys_origin,event_type,create_time)``idx_event(event_id)`,刷新 SQL 应优先利用这两个索引。不能对 `create_time` 列包函数做过滤,只能在 SELECT 或 GROUP BY 阶段转换时间。
## 实施步骤
1. 新增 SQL 迁移:创建 `country_dashboard_game_period_metric``country_dashboard_game_period_user_metric`
2. 新增后端 DTO/Cmd/CO/DAO/Service并在 `DatavRestController` 暴露 `/country-dashboard/game-metrics`
3. 新增刷新任务和手动回填入口,复用现有 `red-circle.country-dashboard.refresh-sys-origins``refresh-timezones` 配置。
4. 新 admin 新增接口方法 `countryDashboardGameMetrics`
5. `datav.vue` 增加“游戏数据”tab、游戏筛选项和分页表格。
6. 首次上线后先回填最近 8 天,再回填历史需要展示的范围。
## 验证方案
SQL 验证:
```sql
EXPLAIN
SELECT *
FROM country_dashboard_game_period_metric
WHERE sys_origin = 'LIKEI'
AND stat_timezone = 'Asia/Riyadh'
AND period_type = 'DAY'
AND period_key BETWEEN '2026-05-01' AND '2026-05-12'
ORDER BY consume_amount DESC
LIMIT 50;
```
预期使用 `idx_game_period_query``idx_game_period_sort`,不能全表扫描。
数据口径验证:
```sql
SELECT SUM(consume_amount), SUM(payout_amount)
FROM country_dashboard_game_period_metric
WHERE sys_origin = 'LIKEI'
AND stat_timezone = 'Asia/Riyadh'
AND period_type = 'DAY'
AND period_key = '2026-05-12';
```
对比现有 `country_dashboard_period_metric` 同日同国家汇总的 `game_total_flow/game_payout`,差异应只来自“无法识别但仍落 UNKNOWN”的映射归类不应出现金额丢失。
功能验证:
- 查询 DAY 日期范围,表格按日期、国家、游戏拆分。
- 查询 WEEK/MONTH用户数为周期内去重不是日用户数累加。
- 查询 ALL不触发实时原始流水扫描。
- 国家筛选、游戏厂商筛选、游戏关键字筛选、分页、排序都能返回稳定结果。
- 刷新任务失败时,现有数据大屏总览接口不受影响。

View File

@ -164,6 +164,7 @@ public class TeamBdMemberBillListQryExe {
TeamBdMemberCO result = new TeamBdMemberCO();
result.setMemberBillList(memberBillList);
result.setMemberCount(memberBillList.size());
result.setAgencyNumber(memberBillList.size());
result.setTotalSalary(totalSalary);
result.setTotalRecharge(totalRecharge);
result.setBillTitle(TeamBillCycleUtils.parseBillBelongToDateRangeStr(currentBillBelong));
@ -222,6 +223,8 @@ public class TeamBdMemberBillListQryExe {
TeamBdMemberCO result = new TeamBdMemberCO();
result.setMemberBillList(memberBillList);
result.setMemberCount(memberBillList.size());
result.setBdNumber(memberBillList.size());
result.setAgencyNumber(totalAgentCount);
result.setTotalSalary(totalSalary);
result.setTotalRecharge(totalRecharge);
result.setBillTitle(TeamBillCycleUtils.parseBillBelongToDateRangeStr(currentBillBelong));
@ -321,6 +324,8 @@ public class TeamBdMemberBillListQryExe {
TeamBdMemberCO result = new TeamBdMemberCO();
result.setMemberBillList(Collections.emptyList());
result.setMemberCount(0);
result.setAgencyNumber(0);
result.setBdNumber(0);
result.setTotalSalary(BigDecimal.ZERO);
result.setTotalRecharge(BigDecimal.ZERO);
result.setBillTitle(TeamBillCycleUtils.parseBillBelongToDateRangeStr(currentBillBelong));
@ -335,24 +340,45 @@ public class TeamBdMemberBillListQryExe {
@NotNull
private Map<Long, BigDecimal> getRechargeMap(Integer currentBillBelong, Set<Long> teamIds) {
if (CollectionUtils.isEmpty(teamIds)) {
return Collections.emptyMap();
}
Map<Long, List<TeamMember>> mapByTeamIds = teamMemberService.mapByTeamIds(teamIds);
if (Objects.isNull(mapByTeamIds) || mapByTeamIds.isEmpty()) {
return Collections.emptyMap();
}
List<Long> memberIds = mapByTeamIds.values().stream()
.flatMap(List::stream)
.map(TeamMember::getMemberId)
.toList();
if (CollectionUtils.isEmpty(memberIds)) {
return Collections.emptyMap();
}
LocalDateTime startTime = TeamBillCycleUtils.getBillBelongStartTime(currentBillBelong);
LocalDateTime endTime = TeamBillCycleUtils.getBillBelongEndTime(currentBillBelong);
ResultResponse<List<BatchUserRechargeStatisticsDTO>> response =
inAppPurchaseDetailsClient.batchUserRechargeStatistics(
memberIds,
SysOriginPlatformEnum.LIKEI.name(),
startTime,
endTime
);
List<BatchUserRechargeStatisticsDTO> body = response.getBody();
Map<Long, BigDecimal> rechargeMap = body.stream().collect(Collectors.toMap(BatchUserRechargeStatisticsDTO::getUserId, BatchUserRechargeStatisticsDTO::getTotalAmount, BigDecimal::add));
return rechargeMap;
try {
ResultResponse<List<BatchUserRechargeStatisticsDTO>> response =
inAppPurchaseDetailsClient.batchUserRechargeStatistics(
memberIds,
SysOriginPlatformEnum.LIKEI.name(),
startTime,
endTime
);
List<BatchUserRechargeStatisticsDTO> body = Optional.ofNullable(response)
.map(ResultResponse::getBody)
.orElse(Collections.emptyList());
if (CollectionUtils.isEmpty(body)) {
return Collections.emptyMap();
}
return body.stream().collect(Collectors.toMap(
BatchUserRechargeStatisticsDTO::getUserId,
BatchUserRechargeStatisticsDTO::getTotalAmount,
BigDecimal::add));
} catch (Exception ex) {
log.warn("Failed to query BD team recharge statistics, teamIds={}", teamIds, ex);
return Collections.emptyMap();
}
}
private Collection<? extends BusinessDevelopmentTeam> buildBdTeamVO(List<TeamMember> teamMembers) {

View File

@ -62,10 +62,11 @@ public class UserIdentityServiceImpl implements UserIdentityService {
.setSuperAdmin(isSuperAdmin)
.setAdmin(isValidAdmin)
.setBdLeader(ResponseAssert.requiredSuccess(bdTeamLeaderClient.check(userId)))
.setManager(isTeamManager)
.setManager(Boolean.FALSE)
.setYumiManager(isTeamManager)
.setAppManager(isManager)
;
}
}
@Override
public Boolean hasPermission(PropsStoreTypeQryCmd cmd) {

View File

@ -17,6 +17,10 @@ public class TeamBdMemberCO {
private Integer memberCount;
private Integer agencyNumber;
private Integer bdNumber;
private List<TeamBdMemberBillCO> memberBillList;
private BdHistoryCO bdHistoryCO;

View File

@ -55,10 +55,16 @@ public class UserIdentityVO implements Serializable {
private Boolean superAdmin;
/**
* 是否是经理
* 旧经理字段Yumi 经理身份不再使用.
*/
private Boolean manager;
/**
* 是否是 Yumi 经理.
*/
@JsonProperty("yumi_manager")
private Boolean yumiManager;
/**
* 是否是App管理员角色里的经理.
*/

View File

@ -51,9 +51,15 @@ public class UserHistoryIdentity extends TimestampBaseEntity {
private Boolean host;
/**
* 是否做过团队经理.
* 是否做过团队经理.
*/
@TableField("is_manager")
private Boolean manager;
/**
* 是否做过 Yumi 团队经理.
*/
@TableField("is_yumi_manager")
private Boolean yumiManager;
}

View File

@ -22,6 +22,11 @@ public interface UserHistoryIdentityService extends BaseService<UserHistoryIdent
* 保存身份=manager.
*/
void saveManager(Long userId);
/**
* 保存身份=yumi manager.
*/
void saveYumiManager(Long userId);
/**
* 保存身份=host.

View File

@ -43,6 +43,7 @@ public class UserHistoryIdentityServiceImpl extends
.setAgent(Boolean.FALSE)
.setHost(Boolean.FALSE)
.setManager(Boolean.FALSE)
.setYumiManager(Boolean.FALSE)
);
} catch (DuplicateKeyException ex) {
// ignore
@ -66,6 +67,31 @@ public class UserHistoryIdentityServiceImpl extends
.setAgent(Boolean.FALSE)
.setHost(Boolean.FALSE)
.setManager(Boolean.TRUE)
.setYumiManager(Boolean.FALSE)
);
} catch (DuplicateKeyException ex) {
// ignore
}
}
@Override
public void saveYumiManager(Long userId) {
if (exists(userId)) {
update()
.set(UserHistoryIdentity::getYumiManager, Boolean.TRUE)
.eq(UserHistoryIdentity::getId, userId)
.execute();
return;
}
try {
save(new UserHistoryIdentity()
.setId(userId)
.setBd(Boolean.FALSE)
.setAgent(Boolean.FALSE)
.setHost(Boolean.FALSE)
.setManager(Boolean.FALSE)
.setYumiManager(Boolean.TRUE)
);
} catch (DuplicateKeyException ex) {
// ignore
@ -88,6 +114,7 @@ public class UserHistoryIdentityServiceImpl extends
.setAgent(Boolean.FALSE)
.setHost(Boolean.TRUE)
.setManager(Boolean.FALSE)
.setYumiManager(Boolean.FALSE)
);
} catch (DuplicateKeyException ex) {
// ignore
@ -112,6 +139,7 @@ public class UserHistoryIdentityServiceImpl extends
.setAgent(Boolean.TRUE)
.setHost(Boolean.TRUE)
.setManager(Boolean.FALSE)
.setYumiManager(Boolean.FALSE)
);
} catch (DuplicateKeyException ex) {
// ignore
@ -174,6 +202,9 @@ public class UserHistoryIdentityServiceImpl extends
if (Objects.equals(identity.getManager(), Boolean.TRUE)) {
res.add("Manager");
}
if (Objects.equals(identity.getYumiManager(), Boolean.TRUE)) {
res.add("YumiManager");
}
return res;
}

View File

@ -62,4 +62,10 @@ public class UserHistoryIdentityEndpoint implements UserHistoryIdentityClientApi
historyIdentityClientService.saveManager(userId);
return ResultResponse.success();
}
@Override
public ResultResponse<Void> saveYumiManager(Long userId) {
historyIdentityClientService.saveYumiManager(userId);
return ResultResponse.success();
}
}

View File

@ -23,4 +23,6 @@ public interface UserHistoryIdentityClientService {
void saveBd(Long userId);
void saveManager(Long userId);
void saveYumiManager(Long userId);
}

View File

@ -55,4 +55,9 @@ public class UserHistoryIdentityClientServiceImpl implements UserHistoryIdentity
public void saveManager(Long userId) {
historyIdentityService.saveManager(userId);
}
@Override
public void saveYumiManager(Long userId) {
historyIdentityService.saveYumiManager(userId);
}
}

View File

@ -0,0 +1,16 @@
SET @table_schema = DATABASE();
SET @ddl = IF(
EXISTS(
SELECT 1
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = @table_schema
AND TABLE_NAME = 'user_history_identity'
AND COLUMN_NAME = 'is_yumi_manager'
),
'SELECT 1',
"ALTER TABLE `user_history_identity` ADD COLUMN `is_yumi_manager` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否做过Yumi团队经理' AFTER `is_manager`"
);
PREPARE stmt FROM @ddl;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;