Compare commits

...

14 Commits

Author SHA1 Message Date
hy001
3f59e5b653 CP 2026-05-21 15:43:38 +08:00
hy001
c02050274b fix manual salary received total display 2026-05-21 15:06:03 +08:00
hy001
8460125d79 expose manual salary role breakdown 2026-05-21 14:05:38 +08:00
hy001
d0d48d71ac include agent salary in manual settlement 2026-05-21 13:59:36 +08:00
hy001
3cef4fc322 fix manual salary settlement net payable 2026-05-21 13:46:33 +08:00
hy001
a48fddb613 Merge branch 'release/redis-memory-fix-20260521' of gitea.haiyihy.com:hy/chatapp3-java 2026-05-21 11:19:21 +08:00
hy001
7bc8b44450 Merge branch 'main' of gitea.haiyihy.com:hy/chatapp3-java 2026-05-21 11:19:05 +08:00
hy001
a8424ad5e3 分国家发放工资 2026-05-21 11:19:04 +08:00
hy001
1343fa4631 CP系统 2026-05-21 11:18:31 +08:00
hy001
4627d2c254 fix: disable country dashboard queries by config 2026-05-21 10:57:41 +08:00
hy001
2ec48cab6b fix: use stable rtc active bitmap offset 2026-05-21 10:50:18 +08:00
ZuoZuo
baaf171151 feat(manager): support badge gifts 2026-05-20 19:59:34 +08:00
hy001
a0cc63d784 fix: tolerate duplicate region configs 2026-05-20 14:11:54 +08:00
hy001
663affb734 Reopen salary exchange and user coin seller transfer 2026-05-20 12:32:45 +08:00
97 changed files with 4891 additions and 1135 deletions

View File

@ -64,6 +64,8 @@ BADGE_ACTIVATION.body=You have received a new achievement badge, go check it out
# cp\u5173\u7CFB\u89E3\u9664 # cp\u5173\u7CFB\u89E3\u9664
CP_DISMISS.title=CP Dismiss CP_DISMISS.title=CP Dismiss
CP_DISMISS.body=${nickname} Dissolved the CP relationship with you CP_DISMISS.body=${nickname} Dissolved the CP relationship with you
CP_BLESS.title=Cp bless
CP_BLESS.body=${nickname} has sent you CP blessing gifts
# \u63A5\u6536\u8D35\u65CFvip # \u63A5\u6536\u8D35\u65CFvip
ACCEPT_NOBLE_VIP.title=Receiving nobles ACCEPT_NOBLE_VIP.title=Receiving nobles
ACCEPT_NOBLE_VIP.body=${nickname} gives you ${days} days of noble status~ ACCEPT_NOBLE_VIP.body=${nickname} gives you ${days} days of noble status~

View File

@ -81,6 +81,8 @@ AGREE_BD_LEADER_INVITE_AGENT.body=${userProfile} menerima undangan BD Anda
BD_LEADER_SEND_INVITE_BD.title=Undangan BD BD_LEADER_SEND_INVITE_BD.title=Undangan BD
BD_LEADER_SEND_INVITE_BD.body=${userProfile}mengundang Anda untuk menjadi BD BD_LEADER_SEND_INVITE_BD.body=${userProfile}mengundang Anda untuk menjadi BD
# CP祝福通知 # CP祝福通知
CP_DISMISS.title=CP Dismiss
CP_DISMISS.body=${nickname} Dissolved the CP relationship with you
CP_BLESS.title=Cp memberkati CP_BLESS.title=Cp memberkati
CP_BLESS.body=${nickname} Saya memberi Anda hadiah berkah ruang CP CP_BLESS.body=${nickname} Saya memberi Anda hadiah berkah ruang CP
# 告白通知 # 告白通知

View File

@ -60,6 +60,8 @@ BADGE_ACTIVATION.body=Yeni bir ba\u015Far\u0131 rozeti ald\u0131n\u0131z, gidip
# cp\u5173\u7CFB\u89E3\u9664 # cp\u5173\u7CFB\u89E3\u9664
CP_DISMISS.title=CP s\u00FCr\u00FCm\u00FC CP_DISMISS.title=CP s\u00FCr\u00FCm\u00FC
CP_DISMISS.body= ${nickname} sizinle CP ili\u015Fkisini bozdu CP_DISMISS.body= ${nickname} sizinle CP ili\u015Fkisini bozdu
CP_BLESS.title=Cp bless
CP_BLESS.body=${nickname} has sent you CP blessing gifts
# \u63A5\u6536\u8D35\u65CFvip # \u63A5\u6536\u8D35\u65CFvip
ACCEPT_NOBLE_VIP.title=Soylular al\u0131r ACCEPT_NOBLE_VIP.title=Soylular al\u0131r
ACCEPT_NOBLE_VIP.body =${nickname} size ${days} g\u00FCnl\u00FCk asil stat\u00FCs\u00FC verir ~ ACCEPT_NOBLE_VIP.body =${nickname} size ${days} g\u00FCnl\u00FCk asil stat\u00FCs\u00FC verir ~

View File

@ -63,6 +63,8 @@ BADGE_ACTIVATION.body=\u60A8\u5DF2\u7372\u5F97\u65B0\u7684\u6210\u5C31\u5FBD\u7A
# cp\u5173\u7CFB\u89E3\u9664 # cp\u5173\u7CFB\u89E3\u9664
CP_DISMISS.title=CP \u89E3\u9664 CP_DISMISS.title=CP \u89E3\u9664
CP_DISMISS.body= ${nickname} \u8207\u60A8\u89E3\u9664\u4E86 CP \u95DC\u4FC2 CP_DISMISS.body= ${nickname} \u8207\u60A8\u89E3\u9664\u4E86 CP \u95DC\u4FC2
CP_BLESS.title=Cp \u795D\u798F
CP_BLESS.body=${nickname} \u7D66\u4F60\u5011\u9001\u4E86cp\u7A7A\u9593\u795D\u798F\u79AE\u7269
# \u63A5\u6536\u8D35\u65CFvip # \u63A5\u6536\u8D35\u65CFvip
ACCEPT_NOBLE_VIP.title=\u8CB4\u65CF\u63A5\u6536 ACCEPT_NOBLE_VIP.title=\u8CB4\u65CF\u63A5\u6536
ACCEPT_NOBLE_VIP.body =${nickname} \u8D08\u9001\u60A8 ${days} \u5929\u7684\u8CB4\u65CF\u8EAB\u4EFD~ ACCEPT_NOBLE_VIP.body =${nickname} \u8D08\u9001\u60A8 ${days} \u5929\u7684\u8CB4\u65CF\u8EAB\u4EFD~

View File

@ -51,4 +51,9 @@ public class GiveGiftConfig implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
} }

View File

@ -11,11 +11,15 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd; import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd; import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd; import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
@ -150,6 +154,20 @@ public interface TeamProfileClientApi {
ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition( ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
@RequestBody TeamSalaryBackQryCmd cmd); @RequestBody TeamSalaryBackQryCmd cmd);
/**
* 后台手动发放主播工资预览.
*/
@PostMapping("/manualSalary/page")
ResultResponse<PageResult<TeamManualSalaryPaymentDTO>> manualSalaryPage(
@RequestBody TeamManualSalaryPaymentQryCmd cmd);
/**
* 后台手动发放主播工资.
*/
@PostMapping("/manualSalary/pay")
ResultResponse<List<TeamManualSalaryPaymentResultDTO>> manualSalaryPay(
@RequestBody TeamManualSalaryPaymentCmd cmd);
/** /**
* 获得区域下的所有团队ID. * 获得区域下的所有团队ID.
*/ */

View File

@ -72,6 +72,11 @@ public enum UserRelationErrorCode implements IResponseErrorCode {
*/ */
CP_RELATIONSHIP_NOT_EXISTS(4313, "CP relationship does not exist"), CP_RELATIONSHIP_NOT_EXISTS(4313, "CP relationship does not exist"),
/**
* 关系类型与用户性别不匹配.
*/
RELATION_TYPE_GENDER_NOT_MATCH(4314, "Relationship type does not match user gender"),
; ;

View File

@ -0,0 +1,40 @@
package com.red.circle.other.inner.model.cmd.team;
import com.red.circle.framework.dto.Command;
import java.io.Serial;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentCmd extends Command {
@Serial
private static final long serialVersionUID = 1L;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 指定发放用户为空时按国家全量发放.
*/
private List<Long> userIds;
/**
* 后台操作人.
*/
private Long operationUserId;
}

View File

@ -0,0 +1,44 @@
package com.red.circle.other.inner.model.cmd.team;
import com.red.circle.framework.core.dto.PageCommand;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentQryCmd extends PageCommand {
@Serial
private static final long serialVersionUID = 1L;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 用户 id.
*/
private Long userId;
/**
* 排序字段.
*/
private String sortField;
/**
* 排序方向.
*/
private String sortOrder;
}

View File

@ -0,0 +1,153 @@
package com.red.circle.other.inner.model.dto.agency.agency;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 成员目标记录 id.
*/
private String id;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 区域 id.
*/
private String region;
/**
* 团队 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
/**
* 团队拥有者用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
/**
* 主播用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 当前账期.
*/
private Integer billBelong;
/**
* 上次结算的积分.
*/
private Long lastSettlementTarget;
/**
* 本次积分.
*/
private Long currentTarget;
/**
* 本次积分档位.
*/
private Integer policyLevel;
/**
* 是否满足任意档位.
*/
private Boolean satisfyLevel;
/**
* 当前账期按积分档位计算出的工资.
*/
private BigDecimal expectedSalary;
/**
* 当前账期主播应发工资.
*/
private BigDecimal expectedMemberSalary;
/**
* 当前账期代理应发工资.
*/
private BigDecimal expectedAgentSalary;
/**
* 待结算工资 = 当前账期应发工资 - 当前账期实收工资.
*/
private BigDecimal payableSalary;
/**
* 主播待结算工资 = 主播应发工资 - 主播实收工资.
*/
private BigDecimal payableMemberSalary;
/**
* 代理待结算工资 = 代理应发工资 - 代理实收工资.
*/
private BigDecimal payableAgentSalary;
/**
* 当前账期总发放工资.
*/
private BigDecimal totalIssuedSalary;
/**
* 当前账期主播实收工资.
*/
private BigDecimal memberIssuedSalary;
/**
* 当前账期代理实收工资.
*/
private BigDecimal agentIssuedSalary;
/**
* 当前账期多发工资.
*/
private BigDecimal overIssuedSalary;
/**
* 当前账期主播多发工资.
*/
private BigDecimal memberOverIssuedSalary;
/**
* 当前账期代理多发工资.
*/
private BigDecimal agentOverIssuedSalary;
/**
* 当前工资余额.
*/
private BigDecimal currentSalaryBalance;
/**
* 当前档位是否已发放.
*/
private Boolean paid;
}

View File

@ -0,0 +1,88 @@
package com.red.circle.other.inner.model.dto.agency.agency;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资结果.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryPaymentResultDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 工资发放明细 id.
*/
private String id;
/**
* 归属系统.
*/
private String sysOrigin;
/**
* 国家 code.
*/
private String countryCode;
/**
* 区域 id.
*/
private String region;
/**
* 团队 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
/**
* 团队拥有者用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
/**
* 主播用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
/**
* 当前账期.
*/
private Integer billBelong;
/**
* 发放前工资余额.
*/
private BigDecimal beforeSalaryBalance;
/**
* 发放后工资余额.
*/
private BigDecimal afterSalaryBalance;
/**
* 本次发放工资.
*/
private BigDecimal issuedSalary;
/**
* 本次积分.
*/
private Long currentTarget;
/**
* 发放档位.
*/
private Integer policyLevel;
}

View File

@ -96,6 +96,11 @@ public class GiftConfigDTO implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -41,6 +41,11 @@ public class CpSimpleUserProfileCO implements Serializable {
*/ */
private String meUserNickname; private String meUserNickname;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
private String relationType;
/** /**
* CP用户ID * CP用户ID
*/ */

View File

@ -1,12 +1,19 @@
package com.red.circle.console.adapter.app.team; package com.red.circle.console.adapter.app.team;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO; import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.service.app.team.TeamSalaryBackService; import com.red.circle.console.app.service.app.team.TeamSalaryBackService;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController; import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -30,4 +37,22 @@ public class TeamSalaryRestController extends BaseController {
return teamSalaryBackService.listByCondition(query); return teamSalaryBackService.listByCondition(query);
} }
/**
* 手动工资发放预览.
*/
@GetMapping("/manual/page")
public PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query) {
return teamSalaryBackService.manualSalaryPage(query);
}
/**
* 确认手动发放工资.
*/
@PostMapping("/manual/pay")
public List<TeamManualSalaryResultCO> manualSalaryPay(
@RequestBody TeamManualSalaryPaymentCmd cmd) {
cmd.setOperationUserId(getReqUserId());
return teamSalaryBackService.manualSalaryPay(cmd);
}
} }

View File

@ -16,10 +16,12 @@ import com.red.circle.other.inner.model.cmd.user.OnlineCountQryCmd;
import com.red.circle.other.inner.model.dto.user.ActiveUserCountryCodeDTO; import com.red.circle.other.inner.model.dto.user.ActiveUserCountryCodeDTO;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -41,6 +43,9 @@ public class DatavRestController extends BaseController {
private final CountryDashboardDailyMetricService countryDashboardDailyMetricService; private final CountryDashboardDailyMetricService countryDashboardDailyMetricService;
private final CountryDashboardGameMetricService countryDashboardGameMetricService; private final CountryDashboardGameMetricService countryDashboardGameMetricService;
@Value("${red-circle.country-dashboard.query-enabled:true}")
private boolean countryDashboardQueryEnabled;
@GetMapping("/active/user-country-code") @GetMapping("/active/user-country-code")
public List<ActiveUserCountryCodeDTO> latestActiveUserCountryCode() { public List<ActiveUserCountryCodeDTO> latestActiveUserCountryCode() {
return userExpandService.latestActiveUserCountryCode(LocalDateTime.now().minusHours(2)); return userExpandService.latestActiveUserCountryCode(LocalDateTime.now().minusHours(2));
@ -58,30 +63,45 @@ public class DatavRestController extends BaseController {
@GetMapping("/country-dashboard") @GetMapping("/country-dashboard")
public CountryDashboardCO countryDashboard(CountryDashboardQryCmd cmd) { public CountryDashboardCO countryDashboard(CountryDashboardQryCmd cmd) {
if (!countryDashboardQueryEnabled) {
return emptyCountryDashboard(cmd);
}
return countryDashboardService.query(cmd); return countryDashboardService.query(cmd);
} }
@GetMapping("/country-dashboard/recharge-details") @GetMapping("/country-dashboard/recharge-details")
public PageResult<CountryDashboardRechargeDetailCO> countryDashboardRechargeDetails( public PageResult<CountryDashboardRechargeDetailCO> countryDashboardRechargeDetails(
CountryDashboardRechargeDetailQryCmd cmd) { CountryDashboardRechargeDetailQryCmd cmd) {
if (!countryDashboardQueryEnabled) {
return PageResult.newPageResult(0);
}
return countryDashboardService.pageRechargeDetails(cmd); return countryDashboardService.pageRechargeDetails(cmd);
} }
@GetMapping("/country-dashboard/game-metrics") @GetMapping("/country-dashboard/game-metrics")
public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetrics( public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetrics(
CountryDashboardGameMetricQryCmd cmd) { CountryDashboardGameMetricQryCmd cmd) {
if (!countryDashboardQueryEnabled) {
return PageResult.newPageResult(0);
}
return countryDashboardGameMetricService.pageGameMetrics(cmd); return countryDashboardGameMetricService.pageGameMetrics(cmd);
} }
@GetMapping("/country-dashboard/game-metrics/games") @GetMapping("/country-dashboard/game-metrics/games")
public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetricGames( public PageResult<CountryDashboardGameMetricCO> countryDashboardGameMetricGames(
CountryDashboardGameMetricQryCmd cmd) { CountryDashboardGameMetricQryCmd cmd) {
if (!countryDashboardQueryEnabled) {
return PageResult.newPageResult(0);
}
return countryDashboardGameMetricService.pageGameSummaries(cmd); return countryDashboardGameMetricService.pageGameSummaries(cmd);
} }
@GetMapping("/country-dashboard/game-metrics/country-rank") @GetMapping("/country-dashboard/game-metrics/country-rank")
public List<CountryDashboardGameMetricCO> countryDashboardGameMetricCountryRanks( public List<CountryDashboardGameMetricCO> countryDashboardGameMetricCountryRanks(
CountryDashboardGameMetricQryCmd cmd) { CountryDashboardGameMetricQryCmd cmd) {
if (!countryDashboardQueryEnabled) {
return Collections.emptyList();
}
return countryDashboardGameMetricService.listGameCountryRanks(cmd); return countryDashboardGameMetricService.listGameCountryRanks(cmd);
} }
@ -91,6 +111,9 @@ public class DatavRestController extends BaseController {
@RequestParam String endDate, @RequestParam String endDate,
@RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin, @RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin,
@RequestParam(value = "statTimezones", required = false) String statTimezones) { @RequestParam(value = "statTimezones", required = false) String statTimezones) {
if (!countryDashboardQueryEnabled) {
return disabledCountryDashboardBackfill(startDate, endDate, sysOrigin);
}
LocalDate parsedStartDate = LocalDate.parse(startDate); LocalDate parsedStartDate = LocalDate.parse(startDate);
LocalDate parsedEndDate = LocalDate.parse(endDate); LocalDate parsedEndDate = LocalDate.parse(endDate);
List<String> refreshedTimezones = countryDashboardDailyMetricService.refreshRange( List<String> refreshedTimezones = countryDashboardDailyMetricService.refreshRange(
@ -109,6 +132,9 @@ public class DatavRestController extends BaseController {
@RequestParam String endDate, @RequestParam String endDate,
@RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin, @RequestParam(value = "sysOrigin", defaultValue = "LIKEI") String sysOrigin,
@RequestParam(value = "statTimezones", required = false) String statTimezones) { @RequestParam(value = "statTimezones", required = false) String statTimezones) {
if (!countryDashboardQueryEnabled) {
return disabledCountryDashboardBackfill(startDate, endDate, sysOrigin);
}
LocalDate parsedStartDate = LocalDate.parse(startDate); LocalDate parsedStartDate = LocalDate.parse(startDate);
LocalDate parsedEndDate = LocalDate.parse(endDate); LocalDate parsedEndDate = LocalDate.parse(endDate);
List<String> refreshedTimezones = countryDashboardGameMetricService.refreshRange( List<String> refreshedTimezones = countryDashboardGameMetricService.refreshRange(
@ -121,4 +147,23 @@ public class DatavRestController extends BaseController {
"statTimezones", refreshedTimezones); "statTimezones", refreshedTimezones);
} }
private CountryDashboardCO emptyCountryDashboard(CountryDashboardQryCmd cmd) {
return new CountryDashboardCO()
.setPeriodType(cmd.getPeriodType())
.setStartDate(cmd.getStartDate())
.setEndDate(cmd.getEndDate())
.setStatTimezone(cmd.getStatTimezone())
.setTotal(new com.red.circle.console.app.dto.clientobject.datav.CountryDashboardMetricCO())
.setRecords(Collections.emptyList());
}
private Map<String, Object> disabledCountryDashboardBackfill(String startDate, String endDate,
String sysOrigin) {
return Map.of(
"status", "disabled",
"startDate", startDate,
"endDate", endDate,
"sysOrigin", sysOrigin);
}
} }

View File

@ -2,13 +2,19 @@ package com.red.circle.console.app.service.app.team;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.console.app.convertor.team.TeamAppConvertor; import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO; import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryDetailsCO; import com.red.circle.console.app.dto.clienobject.team.TeamSalaryDetailsCO;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient; import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDetailsDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDetailsDTO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
@ -16,7 +22,9 @@ import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient; 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.endpoint.user.user.UserProfileClient;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -113,6 +121,39 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
return page; return page;
} }
@Override
public PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query) {
PageResult<TeamManualSalaryPaymentDTO> pageResult = ResponseAssert.requiredSuccess(
teamProfileClient.manualSalaryPage(query));
PageResult<TeamManualSalaryCO> page = pageResult.convert(this::toManualSalaryCO);
if (page.checkRecordsEmpty()) {
return page;
}
Map<Long, UserProfileDTO> userProfileMap = getUserProfileMap(page.getRecords().stream()
.map(TeamManualSalaryCO::getUserId)
.collect(Collectors.toSet()));
page.getRecords().forEach(record -> record.setUserProfile(userProfileMap.get(record.getUserId())));
return page;
}
@Override
public List<TeamManualSalaryResultCO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd) {
List<TeamManualSalaryPaymentResultDTO> results = ResponseAssert.requiredSuccess(
teamProfileClient.manualSalaryPay(cmd));
if (CollectionUtils.isEmpty(results)) {
return List.of();
}
Map<Long, UserProfileDTO> userProfileMap = getUserProfileMap(results.stream()
.map(TeamManualSalaryPaymentResultDTO::getUserId)
.collect(Collectors.toSet()));
return results.stream()
.map(result -> toManualSalaryResultCO(result)
.setUserProfile(userProfileMap.get(result.getUserId())))
.collect(Collectors.toList());
}
private Map<String, String> getMapRegionName(TeamSalaryBackQryCmd query) { private Map<String, String> getMapRegionName(TeamSalaryBackQryCmd query) {
return ResponseAssert.requiredSuccess( return ResponseAssert.requiredSuccess(
regionConfigClient.mapRegionNameBySysOrigin(query.getSysOrigin())); regionConfigClient.mapRegionNameBySysOrigin(query.getSysOrigin()));
@ -147,4 +188,65 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds)); return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
} }
private Map<Long, UserProfileDTO> getUserProfileMap(Collection<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Maps.newHashMap();
}
Set<Long> userIdSet = userIds.stream()
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(userIdSet)) {
return Maps.newHashMap();
}
return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIdSet));
}
private TeamManualSalaryCO toManualSalaryCO(TeamManualSalaryPaymentDTO dto) {
return new TeamManualSalaryCO()
.setId(dto.getId())
.setSysOrigin(dto.getSysOrigin())
.setCountryCode(dto.getCountryCode())
.setRegion(dto.getRegion())
.setTeamId(dto.getTeamId())
.setTeamOwnId(dto.getTeamOwnId())
.setUserId(dto.getUserId())
.setBillBelong(dto.getBillBelong())
.setLastSettlementTarget(dto.getLastSettlementTarget())
.setCurrentTarget(dto.getCurrentTarget())
.setPolicyLevel(dto.getPolicyLevel())
.setSatisfyLevel(dto.getSatisfyLevel())
.setExpectedSalary(dto.getExpectedSalary())
.setExpectedMemberSalary(dto.getExpectedMemberSalary())
.setExpectedAgentSalary(dto.getExpectedAgentSalary())
.setPayableSalary(dto.getPayableSalary())
.setPayableMemberSalary(dto.getPayableMemberSalary())
.setPayableAgentSalary(dto.getPayableAgentSalary())
.setTotalIssuedSalary(dto.getTotalIssuedSalary())
.setMemberIssuedSalary(dto.getMemberIssuedSalary())
.setAgentIssuedSalary(dto.getAgentIssuedSalary())
.setOverIssuedSalary(dto.getOverIssuedSalary())
.setMemberOverIssuedSalary(dto.getMemberOverIssuedSalary())
.setAgentOverIssuedSalary(dto.getAgentOverIssuedSalary())
.setCurrentSalaryBalance(dto.getCurrentSalaryBalance())
.setPaid(dto.getPaid());
}
private TeamManualSalaryResultCO toManualSalaryResultCO(
TeamManualSalaryPaymentResultDTO dto) {
return new TeamManualSalaryResultCO()
.setId(dto.getId())
.setSysOrigin(dto.getSysOrigin())
.setCountryCode(dto.getCountryCode())
.setRegion(dto.getRegion())
.setTeamId(dto.getTeamId())
.setTeamOwnId(dto.getTeamOwnId())
.setUserId(dto.getUserId())
.setBillBelong(dto.getBillBelong())
.setBeforeSalaryBalance(dto.getBeforeSalaryBalance())
.setAfterSalaryBalance(dto.getAfterSalaryBalance())
.setIssuedSalary(dto.getIssuedSalary())
.setCurrentTarget(dto.getCurrentTarget())
.setPolicyLevel(dto.getPolicyLevel());
}
} }

View File

@ -91,6 +91,11 @@ public class SysGiftConfigCO implements Serializable {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -0,0 +1,56 @@
package com.red.circle.console.app.dto.clienobject.team;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资预览.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryCO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String id;
private String sysOrigin;
private String countryCode;
private String region;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private UserProfileDTO userProfile;
private Integer billBelong;
private Long lastSettlementTarget;
private Long currentTarget;
private Integer policyLevel;
private Boolean satisfyLevel;
private BigDecimal expectedSalary;
private BigDecimal expectedMemberSalary;
private BigDecimal expectedAgentSalary;
private BigDecimal payableSalary;
private BigDecimal payableMemberSalary;
private BigDecimal payableAgentSalary;
private BigDecimal totalIssuedSalary;
private BigDecimal memberIssuedSalary;
private BigDecimal agentIssuedSalary;
private BigDecimal overIssuedSalary;
private BigDecimal memberOverIssuedSalary;
private BigDecimal agentOverIssuedSalary;
private BigDecimal currentSalaryBalance;
private Boolean paid;
}

View File

@ -0,0 +1,43 @@
package com.red.circle.console.app.dto.clienobject.team;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 后台手动发放主播工资结果.
*/
@Data
@Accessors(chain = true)
public class TeamManualSalaryResultCO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String id;
private String sysOrigin;
private String countryCode;
private String region;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamId;
@JsonSerialize(using = ToStringSerializer.class)
private Long teamOwnId;
@JsonSerialize(using = ToStringSerializer.class)
private Long userId;
private UserProfileDTO userProfile;
private Integer billBelong;
private BigDecimal beforeSalaryBalance;
private BigDecimal afterSalaryBalance;
private BigDecimal issuedSalary;
private Long currentTarget;
private Integer policyLevel;
}

View File

@ -2,8 +2,13 @@ package com.red.circle.console.app.service.app.team;
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO; import com.red.circle.console.app.dto.clienobject.team.TeamSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryCO;
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
import com.red.circle.framework.dto.PageResult; import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import java.util.List;
/** /**
* 团队自动发送工资凭证. * 团队自动发送工资凭证.
@ -17,4 +22,14 @@ public interface TeamSalaryBackService {
*/ */
PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query); PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query);
/**
* 手动工资发放预览.
*/
PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query);
/**
* 确认手动发放工资.
*/
List<TeamManualSalaryResultCO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd);
} }

View File

@ -1,14 +1,15 @@
package com.red.circle.other.adapter.app.user.relation; package com.red.circle.other.adapter.app.user.relation;
import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.common.business.dto.cmd.UserIdCmd;
import com.red.circle.framework.web.controller.BaseController; import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.command.user.LoveLetterQueryCmdExe; import com.red.circle.other.app.command.user.LoveLetterQueryCmdExe;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO;
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
import com.red.circle.other.app.service.user.relation.UserCpRelationService; import com.red.circle.other.app.service.user.relation.UserCpRelationService;
@ -51,7 +52,7 @@ public class CpRelationshipRestController extends BaseController {
* @eo.request-type formdata * @eo.request-type formdata
*/ */
@GetMapping("/pair") @GetMapping("/pair")
public List<CpPairUserProfileCO> getCpPairUserProfileCO(AppExtCommand cmd) { public List<CpPairUserProfileCO> getCpPairUserProfileCO(CpRelationshipQueryCmd cmd) {
return userCpRelationService.getCpPairUserProfileCO(cmd); return userCpRelationService.getCpPairUserProfileCO(cmd);
} }
@ -103,7 +104,7 @@ public class CpRelationshipRestController extends BaseController {
* @eo.request-type formdata * @eo.request-type formdata
*/ */
@GetMapping("/apply-id") @GetMapping("/apply-id")
public String getCpApplyId(@Validated UserIdCmd cmd) { public String getCpApplyId(@Validated CpApplyIdQueryCmd cmd) {
return userCpRelationService.getCpApplyId(cmd); return userCpRelationService.getCpApplyId(cmd);
} }

View File

@ -1,13 +1,13 @@
package com.red.circle.other.adapter.app.user.user; package com.red.circle.other.adapter.app.user.user;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.framework.web.controller.BaseController; import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO; import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO;
import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd; import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import com.red.circle.other.app.service.user.user.UserCpCabinService; import com.red.circle.other.app.service.user.user.UserCpCabinService;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -34,7 +34,7 @@ public class UserCpCabinRestController extends BaseController {
* cp小屋相关信息 * cp小屋相关信息
*/ */
@GetMapping @GetMapping
public UserCpCabinCO getCpCabinInfo(UserIdCommonCmd cmd) { public UserCpCabinCO getCpCabinInfo(UserCpCabinQueryCmd cmd) {
return userCpCabinService.getCpCabinInfo(cmd); return userCpCabinService.getCpCabinInfo(cmd);
} }
@ -42,7 +42,7 @@ public class UserCpCabinRestController extends BaseController {
* cp value. * cp value.
*/ */
@GetMapping("/value") @GetMapping("/value")
public UserCpCabinValueCO getCpCabinValue(UserIdCommonCmd cmd) { public UserCpCabinValueCO getCpCabinValue(UserCpCabinQueryCmd cmd) {
return userCpCabinService.getCpCabinValue(cmd); return userCpCabinService.getCpCabinValue(cmd);
} }
@ -50,7 +50,7 @@ public class UserCpCabinRestController extends BaseController {
* 获得最近两百条祝福记录,并标记为已读. * 获得最近两百条祝福记录,并标记为已读.
*/ */
@GetMapping("/list") @GetMapping("/list")
public UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserIdCommonCmd cmd) { public UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserCpCabinQueryCmd cmd) {
return userCpCabinService.getCpBlessRecordAndUpRead(cmd); return userCpCabinService.getCpBlessRecordAndUpRead(cmd);
} }

View File

@ -0,0 +1,30 @@
package com.red.circle.other.adapter.app.user.user;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpRightsConfigCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRightsConfigQueryCmd;
import com.red.circle.other.app.service.user.user.UserCpCabinService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* CP rights.
*/
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/user/cp/rights", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserCpRightsRestController extends BaseController {
private final UserCpCabinService userCpCabinService;
/**
* CP rights config.
*/
@GetMapping("/config")
public CpRightsConfigCO getCpRightsConfig(CpRightsConfigQueryCmd cmd) {
return userCpCabinService.getCpRightsConfig(cmd);
}
}

View File

@ -1,10 +1,15 @@
package com.red.circle.other.app.command.user; package com.red.circle.other.app.command.user;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.red.circle.external.inner.endpoint.message.ImMessageClient; import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.app.service.user.user.UserProfileService; import com.red.circle.other.app.service.user.user.UserProfileService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.user.UserGiftBackpackCommon; import com.red.circle.other.infra.common.user.UserGiftBackpackCommon;
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService; import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService; import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService;
@ -15,6 +20,9 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordSe
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveService; import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.collection.MapBuilder;
import com.red.circle.tool.core.date.TimestampUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -33,7 +41,7 @@ public class DismissCpApplyCmdExe {
private final CpValueService cpValueService; private final CpValueService cpValueService;
private final CpApplyService cpApplyService; private final CpApplyService cpApplyService;
private final ImMessageClient imMessageClient; private final OfficialNoticeClient officialNoticeClient;
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
private final UserGiftBackpackCommon userGiftBackpackCommon; private final UserGiftBackpackCommon userGiftBackpackCommon;
private final WeekCpValueCountService weekCpValueCountService; private final WeekCpValueCountService weekCpValueCountService;
@ -44,20 +52,24 @@ public class DismissCpApplyCmdExe {
private final CpRelationshipCacheService cpRelationshipCacheService; private final CpRelationshipCacheService cpRelationshipCacheService;
public void execute(CpApplyDismissCmd cmd) { public void execute(CpApplyDismissCmd cmd) {
String relationType = parseRelationType(cmd.getRelationType());
var cpRelationship = cpRelationshipService.getByUserId(cmd.requiredReqUserId()); var cpRelationship = cpRelationshipService.getByUserId(
cmd.requiredReqUserId(), cmd.getCpUserId(), relationType);
if (Objects.isNull(cpRelationship)) { if (Objects.isNull(cpRelationship)) {
return; return;
} }
// 更新申请状态为解散 // 更新申请状态为解散
cpApplyService.dismiss(cmd.requiredReqUserId()); cpApplyService.dismiss(cmd.requiredReqUserId(), relationType);
// 更新CP关系为分手中不删除数据 // 更新CP关系为分手中不删除数据
cpRelationshipService.updateToDismissing(cmd.requiredReqUserId(), cmd.getCpUserId()); cpRelationshipService.updateToDismissing(cmd.requiredReqUserId(), cmd.getCpUserId(), relationType);
cpRelationshipCacheService.remove(Arrays.asList(cmd.requiredReqUserId(), cmd.getCpUserId())); cpRelationshipCacheService.remove(Arrays.asList(cmd.requiredReqUserId(), cmd.getCpUserId()));
sendDismissNotice(cmd, relationType);
// 以下数据不删除7天后由定时任务清理 // 以下数据不删除7天后由定时任务清理
// weekCpValueCountService.removeThisWeek(...) // weekCpValueCountService.removeThisWeek(...)
// cpValueService.deleteById(...) // cpValueService.deleteById(...)
@ -72,4 +84,36 @@ public class DismissCpApplyCmdExe {
return cmd.requiredReqUserId().toString(); return cmd.requiredReqUserId().toString();
} }
private void sendDismissNotice(CpApplyDismissCmd cmd, String relationType) {
UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId());
if (Objects.isNull(userProfile)) {
return;
}
long dismissEndTime = TimestampUtils.now().getTime() + (7 * 24 * 60 * 60 * 1000L);
officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder()
.toAccount(cmd.getCpUserId())
.noticeType(OfficialNoticeTypeEnum.CP_DISMISS)
.templateParam(MapBuilder.builder()
.put("nickname", userProfile.getUserNickname())
.put("userNickname", userProfile.getUserNickname())
.put("account", userProfile.getAccount())
.put("userAvatar", userProfile.getUserAvatar())
.put("countryCode", userProfile.getCountryCode())
.put("countryName", userProfile.getCountryName())
.put("relationType", relationType)
.put("dismissEndTime", dismissEndTime)
.build())
.expand(cmd.requiredReqUserId())
.build());
}
private String parseRelationType(String relationType) {
try {
return CpRelationshipType.normalize(relationType);
} catch (IllegalArgumentException ex) {
ResponseAssert.failure(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE, "relationType is not supported.");
return CpRelationshipType.CP.name();
}
}
} }

View File

@ -16,6 +16,7 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus; import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; import com.red.circle.other.inner.asserts.user.UserRelationErrorCode;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
@ -38,6 +39,9 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ProcessCpApplyCmd { public class ProcessCpApplyCmd {
private static final int CP_MAX_COUNT = 1;
private static final int FRIEND_MAX_COUNT = 3;
private final CpApplyService cpApplyService; private final CpApplyService cpApplyService;
private final CpValueService cpValueService; private final CpValueService cpValueService;
private final ImMessageClient imMessageClient; private final ImMessageClient imMessageClient;
@ -55,6 +59,7 @@ public class ProcessCpApplyCmd {
CpApply cpApply = cpApplyService.getById(cmd.getApplyId()); CpApply cpApply = cpApplyService.getById(cmd.getApplyId());
// 没有找到映射信息 // 没有找到映射信息
ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, cpApply); ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, cpApply);
String relationType = CpRelationshipType.normalize(cpApply.getRelationType());
// 状态错误 // 状态错误
ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR, ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
@ -62,19 +67,20 @@ public class ProcessCpApplyCmd {
// 检查是否是复合申请 // 检查是否是复合申请
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp( CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()); cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), relationType);
boolean isReconcile = Objects.nonNull(dismissingCp); boolean isReconcile = Objects.nonNull(dismissingCp);
if (!isReconcile) { if (!isReconcile) {
// 非复合申请CP已存在 // 非复合申请CP已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED, ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId())); cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
relationType));
// 检查CP数量限制最多4个 int maxCount = relationMaxCount(relationType);
int senderCpCount = cpRelationshipService.countCp(cmd.requiredReqUserId()); ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
int receiverCpCount = cpRelationshipService.countCp(cpApply.getAcceptApplyUserId()); cpRelationshipService.countCp(cpApply.getSendApplyUserId(), relationType) >= maxCount);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4); ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4); cpRelationshipService.countCp(cpApply.getAcceptApplyUserId(), relationType) >= maxCount);
} }
// 操作失败 // 操作失败
@ -86,23 +92,29 @@ public class ProcessCpApplyCmd {
return; return;
} }
refuseStatusWait(cmd, cpApply);
if (isReconcile) { if (isReconcile) {
// 复合更新状态为NORMAL // 复合更新状态为NORMAL
cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()); cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(),
relationType);
} else { } else {
// 新建关系 // 新建关系
cpRelationshipService cpRelationshipService
.addCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), createCpValue(), .addCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), createCpValue(),
cmd.requireReqSysOrigin()); cmd.requireReqSysOrigin(), relationType);
} }
refuseStatusWaitIfLimitReached(cmd, cpApply, relationType);
cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId())); cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
} }
private void refuseStatusWait(ProcessApplyCmd cmd, CpApply cpApply) { private void refuseStatusWaitIfLimitReached(ProcessApplyCmd cmd, CpApply cpApply,
cpApplyService.refuseStatusWait(cmd.requiredReqUserId()); String relationType) {
if (cpRelationshipService.countCp(cpApply.getAcceptApplyUserId(), relationType)
< relationMaxCount(relationType)) {
return;
}
cpApplyService.refuseStatusWait(cmd.requiredReqUserId(), relationType);
userMqMessageService.cpApplyDelayed( userMqMessageService.cpApplyDelayed(
new CpApplyEvent() new CpApplyEvent()
.setId(cpApply.getId()) .setId(cpApply.getId())
@ -110,7 +122,18 @@ public class ProcessCpApplyCmd {
TimestampUtils.nowPlusMinutes(1)); TimestampUtils.nowPlusMinutes(1));
} }
private int relationMaxCount(String relationType) {
return Objects.equals(CpRelationshipType.CP, CpRelationshipType.of(relationType))
? CP_MAX_COUNT
: FRIEND_MAX_COUNT;
}
private void returnGoldCoins(ProcessApplyCmd cmd, CpApply cpApply) { private void returnGoldCoins(ProcessApplyCmd cmd, CpApply cpApply) {
if (Objects.isNull(cpApply.getApplyConsumeGold())
|| cpApply.getApplyConsumeGold().compareTo(BigDecimal.ZERO) <= 0) {
cpApplyService.changeStatusById(cmd.getApplyId(), CpApplyStatus.REIMBURSE);
return;
}
walletGoldClient.changeBalance(GoldReceiptCmd.builder() walletGoldClient.changeBalance(GoldReceiptCmd.builder()
.appIncome() .appIncome()
.userId(cpApply.getSendApplyUserId()) .userId(cpApply.getSendApplyUserId())

View File

@ -5,6 +5,7 @@ import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.mq.business.model.event.user.CpApplyEvent; import com.red.circle.mq.business.model.event.user.CpApplyEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService; import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
@ -18,6 +19,7 @@ import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus; import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
@ -27,10 +29,12 @@ import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO; import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin; import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@ -40,8 +44,12 @@ import org.springframework.stereotype.Component;
*/ */
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j
public class SendCpApplyCmdExe { public class SendCpApplyCmdExe {
private static final int CP_MAX_COUNT = 1;
private static final int FRIEND_MAX_COUNT = 3;
private final CpApplyService cpApplyService; private final CpApplyService cpApplyService;
private final OfficialNoticeClient officialNoticeClient; private final OfficialNoticeClient officialNoticeClient;
private final WalletGoldClient walletGoldClient; private final WalletGoldClient walletGoldClient;
@ -53,73 +61,103 @@ public class SendCpApplyCmdExe {
private final UserRegionGateway userRegionGateway; private final UserRegionGateway userRegionGateway;
public void execute(CpApplyCmd cmd) { public void execute(CpApplyCmd cmd) {
process(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId(), cmd.requireReqSysOrigin(),
cmd.getRelationType(), true);
}
public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin) {
return autoCreateFromCpGift(sendUserId, acceptUserId, sysOrigin, CpRelationshipType.CP.name());
}
public boolean autoCreateFromCpGift(Long sendUserId, Long acceptUserId, String sysOrigin,
String relationTypeValue) {
try {
process(sendUserId, acceptUserId, sysOrigin, relationTypeValue, false);
return true;
} catch (Exception ex) {
log.info(
"[CP] skip auto create cp apply from gift, sendUserId={}, acceptUserId={}, relationType={}, reason={}",
sendUserId, acceptUserId, relationTypeValue, ex.getMessage());
return false;
}
}
private void process(Long sendUserId, Long acceptUserId, String sysOrigin, String relationTypeValue,
boolean chargeGold) {
CpRelationshipType relationType = parseRelationType(relationTypeValue);
// 不可用操作自己 // 不可用操作自己
ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_OPS_YOURSELF, ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_OPS_YOURSELF,
Objects.equals(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId())); Objects.equals(sendUserId, acceptUserId));
boolean eqRegion = userRegionGateway.checkEqRegion(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId()); boolean eqRegion = userRegionGateway.checkEqRegion(sendUserId, acceptUserId);
ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_NOT_REGION, eqRegion); ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_NOT_REGION, eqRegion);
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(sendUserId));
UserProfileDTO acceptUserProfile = userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(acceptUserId));
validateRelationTypeGender(relationType, userProfile, acceptUserProfile);
// 检查是否是复合申请 // 检查是否是复合申请
CpRelationship dismissingCp = cpRelationshipService.getDismissingCp( CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
cmd.requiredReqUserId(), cmd.getAcceptApplyUserId()); sendUserId, acceptUserId, relationType.name());
boolean isReconcile = Objects.nonNull(dismissingCp); boolean isReconcile = Objects.nonNull(dismissingCp);
if (!isReconcile) { if (!isReconcile) {
// 非复合申请检查CP是否已存在 // 非复合申请检查CP是否已存在
ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED, ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
cpRelationshipService cpRelationshipService
.existsCp(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId())); .existsCp(sendUserId, acceptUserId, relationType.name()));
// 检查CP数量限制最多4个 validateRelationLimit(sendUserId, acceptUserId, relationType);
int senderCpCount = cpRelationshipService.countCp(cmd.requiredReqUserId());
int receiverCpCount = cpRelationshipService.countCp(cmd.getAcceptApplyUserId());
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4);
} }
// 您已发送申请 // 您已发送申请
ResponseAssert.isFalse(UserRelationErrorCode.YOU_HAVE_SENT_AN_APPLICATION, ResponseAssert.isFalse(UserRelationErrorCode.YOU_HAVE_SENT_AN_APPLICATION,
cpApplyService.existsNotProcessSendApply(cmd.requiredReqUserId())); cpApplyService.exists(acceptUserId, sendUserId, relationType.name()));
// 对方已向你发送申请 // 对方已向你发送申请
ResponseAssert.isFalse(UserRelationErrorCode.OTHER_SIDE_ALREADY_APPLIED, ResponseAssert.isFalse(UserRelationErrorCode.OTHER_SIDE_ALREADY_APPLIED,
cpApplyService.exists(cmd.requiredReqUserId(), cmd.getAcceptApplyUserId())); cpApplyService.exists(sendUserId, acceptUserId, relationType.name()));
UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(userProfileGateway.getByUserId(cmd.requiredReqUserId())); BigDecimal applyConsumeGold = BigDecimal.ZERO;
UserProfileDTO acceptUserProfile = userProfileAppConvertor.toUserProfileDTO(userProfileGateway.getByUserId(cmd.getAcceptApplyUserId())); if (chargeGold) {
ResponseAssert.isFalse(UserRelationErrorCode.UNAVAILABLE_SAME_GENDER, Objects.equals(userProfile.getUserSex(), acceptUserProfile.getUserSex())); WalletReceiptResDTO receipt = ResponseAssert.requiredSuccess(
walletGoldClient.changeBalance(GoldReceiptCmd.builder()
WalletReceiptResDTO receipt = ResponseAssert.requiredSuccess( .appExpenditure()
walletGoldClient.changeBalance(GoldReceiptCmd.builder() .userId(sendUserId)
.appExpenditure() .eventId(acceptUserId.toString())
.userId(cmd.requiredReqUserId()) .sysOrigin(sysOrigin)
.eventId(cmd.getAcceptApplyUserIdString()) .origin(GoldOrigin.CP_BUILD)
.sysOrigin(cmd.requireReqSysOrigin()) .amount(enumConfigCacheService.getValueBigDecimal(EnumConfigKey.CP_PRICE,
.origin(GoldOrigin.CP_BUILD) sysOrigin))
.amount(enumConfigCacheService.getValueBigDecimal(EnumConfigKey.CP_PRICE, .build())
cmd.requireReqSysOrigin())) );
.build()) applyConsumeGold = receipt.getOpAmount().getDollarAmount();
); }
String applyText = isReconcile String applyText = isReconcile
? "User " + userProfile.getUserNickname() + " wants to reconcile with you; if you accept, you will be a couple again." ? "User " + userProfile.getUserNickname() + " wants to reconcile with you; if you accept, you will be "
: "User " + userProfile.getUserNickname() + " confessed the feeling to you; if you accept, you will become a couple."; + relationType.applyTargetText() + " again."
: "User " + userProfile.getUserNickname() + " invited you; if you accept, you will become "
+ relationType.applyTargetText() + ".";
CpApply cpApply = new CpApply() CpApply cpApply = new CpApply()
.setSendApplyUserId(cmd.requiredReqUserId()) .setSendApplyUserId(sendUserId)
.setAcceptApplyUserId(cmd.getAcceptApplyUserId()) .setAcceptApplyUserId(acceptUserId)
.setRelationType(relationType.name())
.setApplyText(applyText) .setApplyText(applyText)
.setStatus(CpApplyStatus.WAIT.name()) .setStatus(CpApplyStatus.WAIT.name())
.setApplyConsumeGold(receipt.getOpAmount().getDollarAmount()); .setApplyConsumeGold(applyConsumeGold);
cpApplyService.save(cpApply); cpApplyService.save(cpApply);
Map<Object, Object> map = OfficialNoticeUtils.buildUserProfile(userProfile); Map<Object, Object> map = OfficialNoticeUtils.buildUserProfile(userProfile);
map.put("applyType", isReconcile ? "RECONCILE" : "APPLY"); map.put("applyType", isReconcile ? "RECONCILE" : "APPLY");
map.put("relationType", relationType.name());
officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder() officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder()
.toAccount(cmd.getAcceptApplyUserId()) .toAccount(acceptUserId)
.noticeType(OfficialNoticeTypeEnum.CP_BUILD) .noticeType(OfficialNoticeTypeEnum.CP_BUILD)
.templateParam(map) .templateParam(map)
.expand(cpApply.getId()) .expand(cpApply.getId())
@ -129,8 +167,39 @@ public class SendCpApplyCmdExe {
userMqMessageService.cpApplyDelayed( userMqMessageService.cpApplyDelayed(
new CpApplyEvent() new CpApplyEvent()
.setId(cpApply.getId()) .setId(cpApply.getId())
.setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin())), .setSysOrigin(SysOriginPlatformEnum.valueOf(sysOrigin)),
TimestampUtils.nowPlusDays(1)); TimestampUtils.nowPlusDays(1));
} }
private void validateRelationLimit(Long sendUserId, Long acceptUserId,
CpRelationshipType relationType) {
int maxCount = relationMaxCount(relationType);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
cpRelationshipService.countCp(sendUserId, relationType.name()) >= maxCount);
ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED,
cpRelationshipService.countCp(acceptUserId, relationType.name()) >= maxCount);
}
private int relationMaxCount(CpRelationshipType relationType) {
return Objects.equals(relationType, CpRelationshipType.CP) ? CP_MAX_COUNT : FRIEND_MAX_COUNT;
}
private CpRelationshipType parseRelationType(String relationTypeValue) {
try {
return CpRelationshipType.of(relationTypeValue);
} catch (IllegalArgumentException ex) {
ResponseAssert.failure(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE, "relationType is not supported.");
return CpRelationshipType.CP;
}
}
private void validateRelationTypeGender(CpRelationshipType relationType, UserProfileDTO userProfile,
UserProfileDTO acceptUserProfile) {
if (!Objects.equals(relationType, CpRelationshipType.CP)) {
return;
}
ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_SAME_GENDER,
relationType.genderMatched(userProfile.getUserSex(), acceptUserProfile.getUserSex()));
}
} }

View File

@ -1,7 +1,8 @@
package com.red.circle.other.app.command.user.query; package com.red.circle.other.app.command.user.query;
import com.red.circle.common.business.dto.cmd.UserIdCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import java.util.Optional; import java.util.Optional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -17,8 +18,9 @@ public class CpApplyIdQryExe {
private final CpApplyService cpApplyService; private final CpApplyService cpApplyService;
public String execute(UserIdCmd cmd) { public String execute(CpApplyIdQueryCmd cmd) {
return Optional.ofNullable(cpApplyService.getId(cmd.requiredReqUserId(), cmd.getUserId())) return Optional.ofNullable(cpApplyService.getId(cmd.requiredReqUserId(), cmd.getUserId(),
CpRelationshipType.normalize(cmd.getRelationType())))
.map(String::valueOf) .map(String::valueOf)
.orElse(null); .orElse(null);
} }

View File

@ -12,6 +12,7 @@ import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship; import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService; import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -37,7 +38,8 @@ public class UserCpBlessRecordQueryExe {
public List<CpBlessRecordCO> execute(UserCpBlessRecordQueryCmd cmd) { public List<CpBlessRecordCO> execute(UserCpBlessRecordQueryCmd cmd) {
CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(cmd.getUserId()); CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(
cmd.getUserId(), CpRelationshipType.normalize(cmd.getRelationType()));
if (Objects.isNull(relationship)) { if (Objects.isNull(relationship)) {
return Lists.newArrayList(); return Lists.newArrayList();
} }

View File

@ -2,9 +2,6 @@ package com.red.circle.other.app.command.user.query;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.red.circle.common.business.core.enums.CommonErrorEnum;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.app.convertor.user.UserCpCabinConvertor; import com.red.circle.other.app.convertor.user.UserCpCabinConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinConfigCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinConfigCO;
@ -12,6 +9,7 @@ import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProf
import com.red.circle.other.app.dto.clientobject.user.relation.cp.UserCpCabinSpecialMemberCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.UserCpCabinSpecialMemberCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinSpecialCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinSpecialCO;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord; import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord;
@ -23,10 +21,12 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveS
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinConfigService; import com.red.circle.other.infra.database.rds.service.user.user.CpCabinConfigService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.NumUtils; import com.red.circle.tool.core.num.NumUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -55,30 +55,26 @@ public class UserCpCabinQueryExe {
private final CpCabinAchieveService cpCabinAchieveService; private final CpCabinAchieveService cpCabinAchieveService;
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
public UserCpCabinCO execute(UserIdCommonCmd cmd) { public UserCpCabinCO execute(UserCpCabinQueryCmd cmd) {
String relationType = CpRelationshipType.normalize(cmd.getRelationType());
List<CpCabinConfig> cpCabinConfig = cpCabinConfigService.getCpCabinConfig( CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(
cmd.getReqSysOrigin().getOrigin()); cmd.getUserId(), relationType);
ResponseAssert.notEmpty(CommonErrorEnum.NOT_FOUND_MAPPING_INFO, cpCabinConfig);
List<CpCabinConfigCO> cpCabinConfigList = userCpCabinConvertor.toCpCabinConfigCO(cpCabinConfig);
CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(cmd.getUserId());
Boolean selfCabin = Objects.equals(cmd.getUserId(), cmd.getReqUserId()); Boolean selfCabin = Objects.equals(cmd.getUserId(), cmd.getReqUserId());
if (Objects.isNull(relationship)) { if (Objects.isNull(relationship)) {
UserCpCabinCO userCpCabinCO = new UserCpCabinCO(); return emptyCabin(selfCabin);
CpPairUserProfileCO cpPairUserProfileCO = new CpPairUserProfileCO();
cpPairUserProfileCO.setMeUserProfile(userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(cmd.getReqUserId())));
userCpCabinCO.setCpPairUserProfileCO(cpPairUserProfileCO);
userCpCabinCO.setCpCabinConfigCO(cpCabinConfigList);
userCpCabinCO.setCheckSelfCabin(selfCabin);
userCpCabinCO.setCpValLastMonth(0L);
return userCpCabinCO;
} }
List<CpCabinConfig> cpCabinConfig = cpCabinConfigService.getCpCabinConfig(
cmd.getReqSysOrigin().getOrigin());
List<CpCabinConfigCO> cpCabinConfigList = CollectionUtils.isEmpty(cpCabinConfig)
? Collections.emptyList()
: userCpCabinConvertor.toCpCabinConfigCO(cpCabinConfig);
CpValue cpValue = cpValueService.getById(relationship.getCpValId()); CpValue cpValue = cpValueService.getById(relationship.getCpValId());
CpPairUserProfileCO cpPairUserProfileCO = new CpPairUserProfileCO(); CpPairUserProfileCO cpPairUserProfileCO = new CpPairUserProfileCO();
cpPairUserProfileCO.setRelationType(CpRelationshipType.normalize(relationship.getRelationType()));
cpPairUserProfileCO.setCpUserProfile(userProfileAppConvertor.toUserProfileDTO( cpPairUserProfileCO.setCpUserProfile(userProfileAppConvertor.toUserProfileDTO(
userProfileGateway.getByUserId(relationship.getCpUserId()))); userProfileGateway.getByUserId(relationship.getCpUserId())));
cpPairUserProfileCO.setMeUserProfile(userProfileAppConvertor.toUserProfileDTO( cpPairUserProfileCO.setMeUserProfile(userProfileAppConvertor.toUserProfileDTO(
@ -104,6 +100,18 @@ public class UserCpCabinQueryExe {
.build(); .build();
} }
private static UserCpCabinCO emptyCabin(Boolean selfCabin) {
return UserCpCabinCO.builder()
.cpVal("0")
.blessVal(0L)
.cpPairUserProfileCO(null)
.cpCabinConfigCO(Collections.emptyList())
.currentCpCabinConfigCO(null)
.userCpCabinSpecial(null)
.checkSelfCabin(Boolean.TRUE.equals(selfCabin))
.build();
}
private UserCpCabinSpecialCO getUserCpCabinSpecialCO( private UserCpCabinSpecialCO getUserCpCabinSpecialCO(
CpRelationship relationship, CpPairUserProfileCO cpPairUserProfileCO, CpRelationship relationship, CpPairUserProfileCO cpPairUserProfileCO,
CpCabinConfigCO currentCpCabin) { CpCabinConfigCO currentCpCabin) {

View File

@ -1,12 +1,13 @@
package com.red.circle.other.app.command.user.query; package com.red.circle.other.app.command.user.query;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship; import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.entity.user.user.CpValue; import com.red.circle.other.infra.database.rds.entity.user.user.CpValue;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.num.NumUtils; import com.red.circle.tool.core.num.NumUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Objects; import java.util.Objects;
@ -27,9 +28,10 @@ public class UserCpCabinValueQueryExe {
private final CpValueService cpValueService; private final CpValueService cpValueService;
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
public UserCpCabinValueCO execute(UserIdCommonCmd cmd) { public UserCpCabinValueCO execute(UserCpCabinQueryCmd cmd) {
CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(cmd.getUserId()); CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(
cmd.getUserId(), CpRelationshipType.normalize(cmd.getRelationType()));
if (Objects.isNull(relationship)) { if (Objects.isNull(relationship)) {
return UserCpCabinValueCO.builder() return UserCpCabinValueCO.builder()
.blessVal(0L) .blessVal(0L)

View File

@ -2,14 +2,14 @@ package com.red.circle.other.app.command.user.query;
import com.red.circle.common.business.core.enums.CommonErrorEnum; import com.red.circle.common.business.core.enums.CommonErrorEnum;
import com.red.circle.common.business.dto.cmd.UserIdCmd;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.UserCpLightCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.UserCpLightCmd;
import com.red.circle.other.app.service.user.relation.UserCpLightCacheService; import com.red.circle.other.app.service.user.relation.UserCpLightCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import java.util.Objects; import java.util.Objects;
import com.red.circle.other.app.service.user.relation.UserCpRelationService;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -26,40 +26,41 @@ import org.springframework.stereotype.Component;
public class UserCpLightCmdExe { public class UserCpLightCmdExe {
private final UserCpLightCacheService userCpLightCacheService; private final UserCpLightCacheService userCpLightCacheService;
private final UserCpRelationService userCpRelationService; private final CpRelationshipService cpRelationshipService;
public void execute(UserCpLightCmd cmd) { public void execute(UserCpLightCmd cmd) {
if (StringUtils.isBlank(cmd.getNearUserId()) || Objects.equals(cmd.getNearUserId(), "null")) { if (StringUtils.isBlank(cmd.getNearUserId()) || Objects.equals(cmd.getNearUserId(), "null")) {
ResponseAssert.failure(CommonErrorEnum.DATA_PARSING_ERROR); ResponseAssert.failure(CommonErrorEnum.DATA_PARSING_ERROR);
} }
UserIdCmd userIdCmd = new UserIdCmd(); Long nearUserId = Long.parseLong(cmd.getNearUserId());
userIdCmd.setUserId(cmd.getReqUserId()); CpRelationship cpRelationship = cpRelationshipService.getByUserId(
CpUserProfileCO cpUserProfileCO = userCpRelationService.getCpUser(userIdCmd); cmd.getReqUserId(), nearUserId, CpRelationshipType.normalize(cmd.getRelationType()));
// 如果cp用户是空不点亮 // 如果cp用户是空不点亮
if (Objects.isNull(cpUserProfileCO)) { if (Objects.isNull(cpRelationship)) {
return; return;
} }
// 如果cp不是旁边用户不点亮 // 如果cp不是旁边用户不点亮
if (Objects.nonNull(cpUserProfileCO.getUserProfile().getId()) && !Objects.equals(cpUserProfileCO.getUserProfile().getId().toString(), cmd.getNearUserId())) { if (Objects.nonNull(cpRelationship.getCpUserId()) && !Objects.equals(
cpRelationship.getCpUserId(), nearUserId)) {
return; return;
} }
if (Objects.nonNull(cmd.getType()) && Objects.equals(cmd.getType(), "APPOINT")) { if (Objects.nonNull(cmd.getType()) && Objects.equals(cmd.getType(), "APPOINT")) {
userCpLightCacheService.lightCpUserAppoint(cmd.getReqUserId(), userCpLightCacheService.lightCpUserAppoint(cmd.getReqUserId(),
Long.parseLong(cmd.getNearUserId()), nearUserId,
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
userCpLightCacheService.lightCpUserAppoint( userCpLightCacheService.lightCpUserAppoint(
Long.parseLong(cmd.getNearUserId()), nearUserId,
cmd.getReqUserId(), cmd.getReqUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
} else { } else {
userCpLightCacheService.lightCpUser(cmd.getReqUserId(), userCpLightCacheService.lightCpUser(cmd.getReqUserId(),
Long.parseLong(cmd.getNearUserId()), nearUserId,
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
userCpLightCacheService.lightCpUser( userCpLightCacheService.lightCpUser(
Long.parseLong(cmd.getNearUserId()), nearUserId,
cmd.getReqUserId(), cmd.getReqUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
} }
@ -69,12 +70,12 @@ public class UserCpLightCmdExe {
if (StringUtils.isBlank(cmd.getNearUserId()) || Objects.equals(cmd.getNearUserId(), "null")) { if (StringUtils.isBlank(cmd.getNearUserId()) || Objects.equals(cmd.getNearUserId(), "null")) {
ResponseAssert.failure(CommonErrorEnum.DATA_PARSING_ERROR); ResponseAssert.failure(CommonErrorEnum.DATA_PARSING_ERROR);
} }
UserIdCmd userIdCmd = new UserIdCmd(); Long nearUserId = Long.parseLong(cmd.getNearUserId());
userIdCmd.setUserId(cmd.getReqUserId()); CpRelationship cpRelationship = cpRelationshipService.getByUserId(
CpUserProfileCO cpUserProfileCO = userCpRelationService.getCpUser(userIdCmd); cmd.getReqUserId(), nearUserId, CpRelationshipType.normalize(cmd.getRelationType()));
// 如果cp用户是空不点亮 // 如果cp用户是空不点亮
if (Objects.isNull(cpUserProfileCO)) { if (Objects.isNull(cpRelationship)) {
return; return;
} }
@ -85,18 +86,18 @@ public class UserCpLightCmdExe {
if (Objects.nonNull(cmd.getType()) && Objects.equals(cmd.getType(), "APPOINT")) { if (Objects.nonNull(cmd.getType()) && Objects.equals(cmd.getType(), "APPOINT")) {
userCpLightCacheService.cancelCpUserAppoint(cmd.getReqUserId(), userCpLightCacheService.cancelCpUserAppoint(cmd.getReqUserId(),
cpUserProfileCO.getUserProfile().getId(), cpRelationship.getCpUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
userCpLightCacheService.cancelCpUserAppoint( userCpLightCacheService.cancelCpUserAppoint(
cpUserProfileCO.getUserProfile().getId(), cpRelationship.getCpUserId(),
cmd.getReqUserId(), cmd.getReqUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
} else { } else {
userCpLightCacheService.cancelCpUser(cmd.getReqUserId(), userCpLightCacheService.cancelCpUser(cmd.getReqUserId(),
cpUserProfileCO.getUserProfile().getId(), cpRelationship.getCpUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
userCpLightCacheService.cancelCpUser( userCpLightCacheService.cancelCpUser(
cpUserProfileCO.getUserProfile().getId(), cpRelationship.getCpUserId(),
cmd.getReqUserId(), cmd.getReqUserId(),
cmd.getReqSysOrigin().getOrigin()); cmd.getReqSysOrigin().getOrigin());
} }

View File

@ -1,6 +1,5 @@
package com.red.circle.other.app.command.user.query; package com.red.circle.other.app.command.user.query;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert; import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent; import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
@ -8,16 +7,19 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.tool.core.date.DateUtils; import com.red.circle.tool.core.date.DateUtils;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship; import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -34,19 +36,24 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class UserCpPairUserProfileQryExe { public class UserCpPairUserProfileQryExe {
private static final long DISMISS_DURATION_MS = 7 * 24 * 60 * 60 * 1000L;
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
private final UserProfileGateway userProfileGateway; private final UserProfileGateway userProfileGateway;
private final UserProfileAppConvertor userProfileAppConvertor; private final UserProfileAppConvertor userProfileAppConvertor;
private final CpValueService cpValueService; private final CpValueService cpValueService;
private final TaskMqMessage taskMqMessage; private final TaskMqMessage taskMqMessage;
public List<CpPairUserProfileCO> execute(AppExtCommand cmd) { public List<CpPairUserProfileCO> execute(CpRelationshipQueryCmd cmd) {
String relationType = CpRelationshipType.normalize(cmd.getRelationType());
UserProfile meUser = userProfileGateway.getByUserId(cmd.requiredReqUserId()); UserProfile meUser = userProfileGateway.getByUserId(cmd.requiredReqUserId());
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, meUser); ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, meUser);
List<CpRelationship> cpRelationshipList = cpRelationshipService.getByUserId(cmd.requiredReqUserId()); List<CpRelationship> cpRelationshipList = cpRelationshipService.getByUserId(
List<CpRelationship> dismissing = cpRelationshipService.getDismissingByUserId(cmd.requiredReqUserId()); cmd.requiredReqUserId(), relationType);
List<CpRelationship> dismissing = cpRelationshipService.getDismissingByUserId(
cmd.requiredReqUserId(), relationType);
cpRelationshipList.addAll(dismissing); cpRelationshipList.addAll(dismissing);
if (cpRelationshipList.isEmpty()) { if (cpRelationshipList.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
@ -56,10 +63,13 @@ public class UserCpPairUserProfileQryExe {
.map(cpRelationship -> new CpPairUserProfileCO() .map(cpRelationship -> new CpPairUserProfileCO()
.setMeUserProfile(userProfileAppConvertor.toUserProfileDTO((meUser))) .setMeUserProfile(userProfileAppConvertor.toUserProfileDTO((meUser)))
.setCpUserProfile(getUserProfile(cpRelationship.getCpUserId())) .setCpUserProfile(getUserProfile(cpRelationship.getCpUserId()))
.setRelationType(relationType(cpRelationship))
.setStatus(cpRelationship.getStatus()) .setStatus(cpRelationship.getStatus())
.setCpValue(cpValueService.getCpVal(cpRelationship.getCpValId())) .setCpValue(cpValueService.getCpVal(cpRelationship.getCpValId()))
.setDays(DateUtils.toDurationDays(cpRelationship.getCreateTime(), DateUtils.now())) .setDays(relationshipDays(cpRelationship.getCreateTime()))
.setFirstDay(cpRelationship.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString())) .setFirstDay(cpRelationship.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString())
.setDismissEndTime(dismissEndTime(cpRelationship))
.setDismissRemainSeconds(dismissRemainSeconds(cpRelationship)))
.sorted(Comparator.comparing(CpPairUserProfileCO::getCpValue).reversed()) .sorted(Comparator.comparing(CpPairUserProfileCO::getCpValue).reversed())
.toList(); .toList();
} }
@ -68,4 +78,31 @@ public class UserCpPairUserProfileQryExe {
return userProfileAppConvertor.toUserProfileDTO(userProfileGateway return userProfileAppConvertor.toUserProfileDTO(userProfileGateway
.getByUserId(cpUserId)); .getByUserId(cpUserId));
} }
private static long relationshipDays(Date createTime) {
Date now = DateUtils.now();
if (!createTime.before(now)) {
return 0L;
}
return DateUtils.toDurationDays(createTime, now);
}
private static String relationType(CpRelationship cpRelationship) {
return CpRelationshipType.normalize(cpRelationship.getRelationType());
}
private static Long dismissEndTime(CpRelationship cpRelationship) {
if (Objects.isNull(cpRelationship.getDismissTime())) {
return null;
}
return cpRelationship.getDismissTime() + DISMISS_DURATION_MS;
}
private static Long dismissRemainSeconds(CpRelationship cpRelationship) {
Long dismissEndTime = dismissEndTime(cpRelationship);
if (Objects.isNull(dismissEndTime)) {
return null;
}
return Math.max(0L, (dismissEndTime - System.currentTimeMillis()) / 1000);
}
} }

View File

@ -8,10 +8,12 @@ import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileC
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService; import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.enums.user.UserCpLevelEnum; import com.red.circle.other.inner.enums.user.UserCpLevelEnum;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Date;
import java.util.Optional; import java.util.Optional;
import com.red.circle.tool.core.date.LocalDateTimeUtils; import com.red.circle.tool.core.date.LocalDateTimeUtils;
@ -37,7 +39,8 @@ public class UserCpRelationQryExe {
return Optional.ofNullable(cpRelationshipService.getByUserId(cmd.getReqUserId(), cmd.getUserId())) return Optional.ofNullable(cpRelationshipService.getByUserId(cmd.getReqUserId(), cmd.getUserId()))
.map(cpRelationship -> new CpUserProfileCO() .map(cpRelationship -> new CpUserProfileCO()
.setUserProfile(getUserProfile(cpRelationship.getCpUserId())) .setUserProfile(getUserProfile(cpRelationship.getCpUserId()))
.setDays(DateUtils.toDurationDays(cpRelationship.getCreateTime(), DateUtils.now())) .setRelationType(CpRelationshipType.normalize(cpRelationship.getRelationType()))
.setDays(relationshipDays(cpRelationship.getCreateTime()))
.setFirstDay(cpRelationship.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString()) .setFirstDay(cpRelationship.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString())
) )
.orElse(null); .orElse(null);
@ -48,6 +51,14 @@ public class UserCpRelationQryExe {
.getByUserId(cpUserId)); .getByUserId(cpUserId));
} }
private static long relationshipDays(Date createTime) {
Date now = DateUtils.now();
if (!createTime.before(now)) {
return 0L;
}
return DateUtils.toDurationDays(createTime, now);
}
private UserCpLevelEnum getLevel(Long userId1, Long userId2) { private UserCpLevelEnum getLevel(Long userId1, Long userId2) {
Long val = userCpValueCacheService.getValue(userId1, userId2); Long val = userCpValueCacheService.getValue(userId1, userId2);

View File

@ -21,6 +21,7 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveS
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinConfigService; import com.red.circle.other.infra.database.rds.service.user.user.CpCabinConfigService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.user.UserCpLevelEnum; import com.red.circle.other.inner.enums.user.UserCpLevelEnum;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
@ -78,6 +79,7 @@ public class UserCpRelationQueryV4Exe {
return buildUserCpCabinSpecialCO(CpCabinUserProfileCO.builder() return buildUserCpCabinSpecialCO(CpCabinUserProfileCO.builder()
.userProfile(getUserProfile(cpRelationship.getCpUserId())) .userProfile(getUserProfile(cpRelationship.getCpUserId()))
.relationType(CpRelationshipType.normalize(cpRelationship.getRelationType()))
.days(TimestampUtils.durationNowDays(cpRelationship.getCreateTime())) .days(TimestampUtils.durationNowDays(cpRelationship.getCreateTime()))
.level(getLevel(cpRelationship.getUserId(), cpRelationship.getCpUserId())) .level(getLevel(cpRelationship.getUserId(), cpRelationship.getCpUserId()))
.currentCpCabinConfigCO(currentCpCabinConfigCO) .currentCpCabinConfigCO(currentCpCabinConfigCO)

View File

@ -0,0 +1,141 @@
package com.red.circle.other.app.command.user.query;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpRightsConfigCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRightsConfigQueryCmd;
import com.red.circle.other.infra.database.rds.entity.user.user.CpCabinConfig;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.entity.user.user.CpValue;
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinConfigService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
* CP rights config query.
*/
@Component
@RequiredArgsConstructor
public class UserCpRightsConfigQueryExe {
private static final String RELATIONSHIP_STATUS_NONE = "NONE";
private final CpCabinConfigService cpCabinConfigService;
private final CpRelationshipService cpRelationshipService;
private final CpValueService cpValueService;
public CpRightsConfigCO execute(CpRightsConfigQueryCmd cmd) {
String relationType = CpRelationshipType.normalize(cmd.getRelationType());
CpRelationship relationship = findRelationship(cmd.getReqUserId(), cmd.getCpUserId(),
relationType);
Long intimacyValue = getIntimacyValue(relationship);
List<CpCabinConfig> configs = cpCabinConfigService.getCpCabinConfig(
cmd.getReqSysOrigin().getOrigin());
return CpRightsConfigCO.builder()
.relationType(relationType)
.cpUserId(resolveCpUserId(cmd.getReqUserId(), cmd.getCpUserId(), relationship))
.relationshipStatus(Objects.isNull(relationship) ? RELATIONSHIP_STATUS_NONE
: relationship.getStatus())
.intimacyValue(intimacyValue)
.levels(toLevels(relationType, configs, Objects.nonNull(relationship), intimacyValue))
.build();
}
private CpRelationship findRelationship(Long reqUserId, Long cpUserId, String relationType) {
if (Objects.isNull(reqUserId)) {
return null;
}
if (Objects.nonNull(cpUserId)) {
CpRelationship normal = cpRelationshipService.getByUserId(reqUserId, cpUserId,
relationType);
if (Objects.nonNull(normal)) {
return normal;
}
return cpRelationshipService.getDismissingCp(reqUserId, cpUserId, relationType);
}
return cpRelationshipService.getByUserIdOrCpUserId(reqUserId, relationType);
}
private Long getIntimacyValue(CpRelationship relationship) {
if (Objects.isNull(relationship)) {
return 0L;
}
CpValue cpValue = cpValueService.getById(relationship.getCpValId());
return Optional.ofNullable(cpValue)
.map(CpValue::getCpVal)
.map(BigDecimal::longValue)
.orElse(0L);
}
private Long resolveCpUserId(Long reqUserId, Long requestCpUserId, CpRelationship relationship) {
if (Objects.nonNull(requestCpUserId) || Objects.isNull(relationship)) {
return requestCpUserId;
}
if (Objects.equals(relationship.getUserId(), reqUserId)) {
return relationship.getCpUserId();
}
if (Objects.equals(relationship.getCpUserId(), reqUserId)) {
return relationship.getUserId();
}
return relationship.getCpUserId();
}
private List<CpRightsConfigCO.LevelCO> toLevels(String relationType, List<CpCabinConfig> configs,
boolean hasRelationship, Long intimacyValue) {
if (Objects.isNull(configs) || configs.isEmpty()) {
return Collections.emptyList();
}
return IntStream.range(0, configs.size())
.mapToObj(index -> toLevel(relationType, configs.get(index), index + 1, hasRelationship,
intimacyValue))
.toList();
}
private CpRightsConfigCO.LevelCO toLevel(String relationType, CpCabinConfig config, int level,
boolean hasRelationship, Long intimacyValue) {
Long requiredIntimacyValue = Optional.ofNullable(config.getCabinUnlockValue()).orElse(0L);
boolean unlocked = hasRelationship && Optional.ofNullable(intimacyValue).orElse(0L)
>= requiredIntimacyValue;
String levelName = StringUtils.defaultIfBlank(config.getName(), relationType + " Level "
+ level);
return CpRightsConfigCO.LevelCO.builder()
.level(level)
.levelName(levelName)
.requiredIntimacyValue(requiredIntimacyValue)
.unlocked(unlocked)
.rights(Collections.singletonList(toRight(relationType, config, level, levelName,
requiredIntimacyValue, unlocked)))
.build();
}
private CpRightsConfigCO.RightCO toRight(String relationType, CpCabinConfig config, int level,
String title, Long requiredIntimacyValue, boolean unlocked) {
return CpRightsConfigCO.RightCO.builder()
.rightKey(relationType + "_CABIN_LEVEL_" + level)
.icon(firstNotBlank(config.getCover(), config.getPersonCover(), config.getCardCover(),
config.getBackgroundCover(), config.getSourceMiniUrl(), config.getSourceUrl()))
.title(title)
.desc("Unlock when intimacy reaches " + requiredIntimacyValue)
.unlocked(unlocked)
.build();
}
private String firstNotBlank(String... values) {
for (String value : values) {
if (StringUtils.isNotBlank(value)) {
return value;
}
}
return null;
}
}

View File

@ -84,7 +84,8 @@ public interface GiftAppConvertor {
.setGiftIntegral(giftConfig.getGiftIntegral()) .setGiftIntegral(giftConfig.getGiftIntegral())
.setSpecial(giftConfig.getSpecial()) .setSpecial(giftConfig.getSpecial())
.setType(giftConfig.getType()) .setType(giftConfig.getType())
.setGiftTab(giftConfig.getGiftTab()); .setGiftTab(giftConfig.getGiftTab())
.setCpRelationType(giftConfig.getCpRelationType());
} }
default GiveAwayGiftBatchEvent toBackpackGift(GiveAwayGiftBackpackCmd cmd, default GiveAwayGiftBatchEvent toBackpackGift(GiveAwayGiftBackpackCmd cmd,

View File

@ -810,6 +810,7 @@ public class GiveGiftsListener implements MessageListener {
return new GiftValue() return new GiftValue()
.setCurrencyType(giftConfig.getType()) .setCurrencyType(giftConfig.getType())
.setGiftType(getGiftType(event, giftConfig)) .setGiftType(getGiftType(event, giftConfig))
.setCpRelationType(giftConfig.getCpRelationType())
.setUnitPrice(giftConfig.getGiftCandy()) .setUnitPrice(giftConfig.getGiftCandy())
.setQuantity(event.getQuantity()) .setQuantity(event.getQuantity())
.setUserSize(event.acceptUserSize()) .setUserSize(event.acceptUserSize())

View File

@ -7,11 +7,14 @@ import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.DynamicMessageEnum; import com.red.circle.common.business.enums.DynamicMessageEnum;
import com.red.circle.component.redis.service.RedisService; import com.red.circle.component.redis.service.RedisService;
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.mq.business.model.event.gift.OfflineProcessGiftEvent; import com.red.circle.mq.business.model.event.gift.OfflineProcessGiftEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage; import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService; import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.common.game.GameIndoorTeamPkCommon; import com.red.circle.other.app.common.game.GameIndoorTeamPkCommon;
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon; import com.red.circle.other.app.common.gift.GameLuckyGiftCommon;
import com.red.circle.other.app.command.user.SendCpApplyCmdExe;
import com.red.circle.other.app.dto.clientobject.dynamic.MessageItemCO; import com.red.circle.other.app.dto.clientobject.dynamic.MessageItemCO;
import com.red.circle.other.app.dto.clientobject.game.GameRoomPkUserCO; import com.red.circle.other.app.dto.clientobject.game.GameRoomPkUserCO;
import com.red.circle.other.app.dto.cmd.SpinsTaskProgressUpdateCmd; import com.red.circle.other.app.dto.cmd.SpinsTaskProgressUpdateCmd;
@ -69,6 +72,7 @@ import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordSe
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.database.rds.service.user.user.FriendshipCardService; import com.red.circle.other.infra.database.rds.service.user.user.FriendshipCardService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.game.GamePkTypeEnum; import com.red.circle.other.inner.enums.game.GamePkTypeEnum;
import com.red.circle.other.inner.enums.material.GiftCurrencyType; import com.red.circle.other.inner.enums.material.GiftCurrencyType;
@ -77,6 +81,7 @@ import com.red.circle.other.inner.enums.material.GiftTabEnum;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode; import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.collection.MapBuilder;
import com.red.circle.tool.core.date.TimestampUtils; import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils; import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.json.JacksonUtils; import com.red.circle.tool.core.json.JacksonUtils;
@ -147,6 +152,7 @@ public class GiftCountStrategy implements GiftStrategy {
private static final List<String> inRegionCodes = List.of("IN", "PK", "BD"); private static final List<String> inRegionCodes = List.of("IN", "PK", "BD");
private final UserRegionCacheService userRegionCacheService; private final UserRegionCacheService userRegionCacheService;
private final UserProfileGateway userProfileGateway; private final UserProfileGateway userProfileGateway;
private final SendCpApplyCmdExe sendCpApplyCmdExe;
@Override @Override
@ -187,6 +193,9 @@ public class GiftCountStrategy implements GiftStrategy {
// 动态礼物处理 // 动态礼物处理
processDynamicGift(runningWater); processDynamicGift(runningWater);
autoCreateCpApplyFromCpGift(runningWater);
saveBlessingsRecord(runningWater);
boolean isHighPriceGift = checkHighPriceGift(runningWater); boolean isHighPriceGift = checkHighPriceGift(runningWater);
BigDecimal giftValueCount = BigDecimal.valueOf(getActualAmount(giftValue, giftValue.getGiftValue())); BigDecimal giftValueCount = BigDecimal.valueOf(getActualAmount(giftValue, giftValue.getGiftValue()));
@ -357,18 +366,70 @@ public class GiftCountStrategy implements GiftStrategy {
cpValueService.incrBlessValue(cpValue.getId(), runningWater.getGiftValue().getGiftValue()); cpValueService.incrBlessValue(cpValue.getId(), runningWater.getGiftValue().getGiftValue());
UserProfile userProfile = userProfileRepository.getByUserId(runningWater.getUserId()); sendCpBlessNotice(runningWater, cpRelationship, cache);
// 发送祝福通知
/* officialService.send(NoticeExtTemplateTypeCmd.builder()
.toAccount(runningWater.getAcceptUsers().get(0).getAcceptUserId())
.noticeType(OfficialNoticeTypeEnum.CP_BLESS)
.templateParam(MapBuilder.builder()
.put("nickname", userProfile.getUserNickname())
.build())
.build());*/
} }
private void autoCreateCpApplyFromCpGift(GiftGiveRunningWater runningWater) {
try {
if (Objects.isNull(runningWater) || Objects.isNull(runningWater.getGiftValue())
|| !isCpGift(runningWater.getGiftValue())) {
return;
}
if (CollectionUtils.isEmpty(runningWater.getAcceptUsers())) {
return;
}
String relationType = CpRelationshipType.normalize(
runningWater.getGiftValue().getCpRelationType());
runningWater.getAcceptUsers().forEach(accept -> {
if (Objects.equals(runningWater.getUserId(), accept.getAcceptUserId())) {
return;
}
sendCpApplyCmdExe.autoCreateFromCpGift(
runningWater.getUserId(), accept.getAcceptUserId(), runningWater.getSysOrigin(),
relationType);
});
} catch (Exception ex) {
log.warn("[CP] skip auto create cp apply from gift, runningWaterId={}, giftId={}, sendUserId={}, reason={}",
Objects.nonNull(runningWater) ? runningWater.getId() : null,
Objects.nonNull(runningWater) ? runningWater.getGiftId() : null,
Objects.nonNull(runningWater) ? runningWater.getUserId() : null,
ex.getMessage());
}
}
private void sendCpBlessNotice(GiftGiveRunningWater runningWater, CpRelationship cpRelationship,
GiftConfigDTO giftConfig) {
UserProfile userProfile = userProfileRepository.getByUserId(runningWater.getUserId());
if (Objects.isNull(userProfile)) {
return;
}
List<Long> toAccounts = Lists.newArrayList(cpRelationship.getUserId(), cpRelationship.getCpUserId())
.stream()
.filter(Objects::nonNull)
.distinct()
.toList();
if (CollectionUtils.isEmpty(toAccounts)) {
return;
}
officialService.send(NoticeExtTemplateTypeCmd.builder()
.toAccounts(toAccounts)
.noticeType(OfficialNoticeTypeEnum.CP_BLESS)
.templateParam(MapBuilder.builder()
.put("nickname", userProfile.getUserNickname())
.put("userNickname", userProfile.getUserNickname())
.put("account", userProfile.getAccount())
.put("userAvatar", userProfile.getUserAvatar())
.put("countryCode", userProfile.getCountryCode())
.put("countryName", userProfile.getCountryName())
.put("giftCover", giftConfig.getGiftPhoto())
.put("giftCount", runningWater.getGiftValue().getQuantity())
.put("relationType", cpRelationship.getRelationType())
.build())
.expand(cpRelationship.getCpValId())
.build());
}
/** /**
* 房间粉丝人气票活动. * 房间粉丝人气票活动.

View File

@ -75,6 +75,11 @@ public class CpApplyListener implements MessageListener {
} }
private void reimburse(CpApplyEvent param, CpApply cpApply) { private void reimburse(CpApplyEvent param, CpApply cpApply) {
if (Objects.isNull(cpApply.getApplyConsumeGold())
|| cpApply.getApplyConsumeGold().signum() <= 0) {
cpApplyService.changeStatusById(cpApply.getId(), CpApplyStatus.REIMBURSE);
return;
}
walletGoldClient.changeBalance(GoldReceiptCmd.builder() walletGoldClient.changeBalance(GoldReceiptCmd.builder()
.appIncome() .appIncome()
.userId(cpApply.getSendApplyUserId()) .userId(cpApply.getSendApplyUserId())

View File

@ -111,15 +111,53 @@ public class OnlineStatusUploadListener implements MessageListener {
} }
private void countActiveUser(UserProfile userProfile) { private void countActiveUser(UserProfile userProfile) {
long account = Objects.hash(userProfile.getAccount()); Long bitmapOffset = resolveActiveUserBitmapOffset(userProfile);
if (Objects.isNull(bitmapOffset) || bitmapOffset <= 0) {
log.warn("[在线状态]跳过活跃bitmap统计, userId={}, account={}, actualAccount={}",
userProfile.getId(),
userProfile.getAccount(),
userProfile.getActualAccount());
return;
}
if (userProfileGateway.checkTeamMember(userProfile.getId())) { if (userProfileGateway.checkTeamMember(userProfile.getId())) {
countUserActiveIndexService.countAnchorActiveUser(account); countUserActiveIndexService.countAnchorActiveUser(bitmapOffset);
countUserActiveIndexService.countAnchorActiveUser(userProfile.getOriginSys(), account); countUserActiveIndexService.countAnchorActiveUser(userProfile.getOriginSys(), bitmapOffset);
return; return;
} }
countUserActiveIndexService.countOrdinaryActiveUser(account); countUserActiveIndexService.countOrdinaryActiveUser(bitmapOffset);
countUserActiveIndexService.countOrdinaryActiveUser(userProfile.getOriginSys(), account); countUserActiveIndexService.countOrdinaryActiveUser(userProfile.getOriginSys(), bitmapOffset);
}
static Long resolveActiveUserBitmapOffset(UserProfile userProfile) {
if (Objects.isNull(userProfile)) {
return null;
}
Long offset = parsePositiveLong(userProfile.getAccount());
if (Objects.nonNull(offset)) {
return offset;
}
offset = parsePositiveLong(userProfile.getActualAccount());
if (Objects.nonNull(offset)) {
return offset;
}
return Optional.ofNullable(userProfile.getId())
.filter(id -> id > 0)
.orElse(null);
}
private static Long parsePositiveLong(String value) {
if (StringUtils.isBlank(value)) {
return null;
}
try {
long parsed = Long.parseLong(value.trim());
return parsed > 0 ? parsed : null;
} catch (NumberFormatException ignore) {
return null;
}
} }
private void addOnlineUser(UserOnlineStatusEvent event, UserProfile userProfile) { private void addOnlineUser(UserOnlineStatusEvent event, UserProfile userProfile) {

View File

@ -79,7 +79,7 @@ public class ActivityCorrectionDataManager {
log.warn("correctionActivityData:CHARM_RANK_MONTH-完成"); log.warn("correctionActivityData:CHARM_RANK_MONTH-完成");
// cp // cp
CpRelationship cpRelationship = cpRelationshipService.getByUserId(userId, null); CpRelationship cpRelationship = cpRelationshipService.getByUserId(userId, (Long) null);
if (Objects.nonNull(cpRelationship)) { if (Objects.nonNull(cpRelationship)) {
BigDecimal cpVal = cpValueService.getCpVal(cpRelationship.getCpValId()); BigDecimal cpVal = cpValueService.getCpVal(cpRelationship.getCpValId());

View File

@ -33,21 +33,27 @@ import com.red.circle.other.infra.database.mongo.entity.user.profile.UserSpecial
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService; import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService; import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.mongo.service.user.profile.UserSpecialIdService; import com.red.circle.other.infra.database.mongo.service.user.profile.UserSpecialIdService;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig;
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord; import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
import com.red.circle.other.infra.database.rds.entity.sys.Administrator; import com.red.circle.other.infra.database.rds.entity.sys.Administrator;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo; import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam; import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentTeam;
import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService;
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService; import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorService; import com.red.circle.other.infra.database.rds.service.sys.AdministratorService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService; import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService; import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.material.props.BadgeBackpackClient;
import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient; import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient;
import com.red.circle.other.inner.endpoint.material.props.PropsNobleVipClient; import com.red.circle.other.inner.endpoint.material.props.PropsNobleVipClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum; import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType; import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.enums.team.TeamMemberRole; import com.red.circle.other.inner.enums.team.TeamMemberRole;
import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd; import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
import com.red.circle.other.inner.model.cmd.team.TeamMemberBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamMemberBackQryCmd;
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO; import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
@ -75,6 +81,8 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
public class ManagerAuthServiceImpl implements ManagerAuthService { public class ManagerAuthServiceImpl implements ManagerAuthService {
private static final int MANAGER_BADGE_GIFT_DAYS = 7;
private final RoomManagerAuthsQueryExe roomManagerAuthsQueryExe; private final RoomManagerAuthsQueryExe roomManagerAuthsQueryExe;
private final ManagerApprovalNotPassCmdExe managerApprovalNotPassCmdExe; private final ManagerApprovalNotPassCmdExe managerApprovalNotPassCmdExe;
private final PropsSourceRecordService propsSourceRecordService; private final PropsSourceRecordService propsSourceRecordService;
@ -84,6 +92,7 @@ public class ManagerAuthServiceImpl implements ManagerAuthService {
private final UserRegionGateway userRegionGateway; private final UserRegionGateway userRegionGateway;
private final RedisService redisService; private final RedisService redisService;
private final PropsBackpackClient propsBackpackClient; private final PropsBackpackClient propsBackpackClient;
private final BadgeBackpackClient badgeBackpackClient;
private final PropsStoreGateway propsStoreGateway; private final PropsStoreGateway propsStoreGateway;
private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService; private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService;
private final TeamMemberService teamMemberService; private final TeamMemberService teamMemberService;
@ -94,6 +103,7 @@ public class ManagerAuthServiceImpl implements ManagerAuthService {
private final OfficialNoticeClient officialNoticeClient; private final OfficialNoticeClient officialNoticeClient;
private final AdministratorAuthService administratorAuthService; private final AdministratorAuthService administratorAuthService;
private final PropsNobleVipClient propsNobleVipClient; private final PropsNobleVipClient propsNobleVipClient;
private final BadgeConfigService badgeConfigService;
@Override @Override
public RoomManagerAuthsCO managerAuths(RoomManageCommonCmd roomManageCommonCmd) { public RoomManagerAuthsCO managerAuths(RoomManageCommonCmd roomManageCommonCmd) {
@ -300,6 +310,16 @@ public class ManagerAuthServiceImpl implements ManagerAuthService {
// UserProfile admin = userProfileGateway.getByUserId(cmd.requiredReqUserId()); // UserProfile admin = userProfileGateway.getByUserId(cmd.requiredReqUserId());
if (PropsCommodityType.BADGE.name().equals(propsSourceRecord.getType())) {
if (!Boolean.TRUE.equals(propsSourceRecord.getAdminFree())) {
ResponseAssert.failure(CommonErrorCode.SEND_FAILURE,
"badge is not admin free.");
}
sendBadgeRecord(cmd, resolveBadgeType(propsSourceRecord.getId()),
MANAGER_BADGE_GIFT_DAYS);
return;
}
int days = PropsCommodityType.NOBLE_VIP.name().equals(propsSourceRecord.getType()) ? 3 : 7; int days = PropsCommodityType.NOBLE_VIP.name().equals(propsSourceRecord.getType()) ? 3 : 7;
sendPropsRecord(cmd, propsSourceRecord.getType(), days); sendPropsRecord(cmd, propsSourceRecord.getType(), days);
//vip单独处理 //vip单独处理
@ -401,6 +421,34 @@ public class ManagerAuthServiceImpl implements ManagerAuthService {
} catch (Exception e) { } catch (Exception e) {
log.error("赠送道具 发送机器人失败", e); log.error("赠送道具 发送机器人失败", e);
} }
sendGiftNotice(cmd, sourceType, days);
}
private void sendBadgeRecord(PropsGiveAwayCmd cmd, SysBadgeConfigTypeEnum badgeType, Integer days) {
log.info("admin free badge start.{}", cmd);
badgeBackpackClient.giveBadge(new GiveBadgeCmd()
.setAcceptUserId(cmd.getAcceptUserId())
.setBadgeId(cmd.getPropsId())
.setExpireType(BadgeBackpackExpireTypeEnum.TEMPORARY)
.setDays(days)
.setBadgeType(badgeType));
sendGiftNotice(cmd, badgeType.getName(), days);
}
private SysBadgeConfigTypeEnum resolveBadgeType(Long badgeId) {
BadgeConfig badgeConfig = badgeConfigService.getById(badgeId);
if (Objects.isNull(badgeConfig) || StringUtils.isBlank(badgeConfig.getType())) {
return SysBadgeConfigTypeEnum.ACTIVITY;
}
try {
return SysBadgeConfigTypeEnum.valueOf(badgeConfig.getType());
} catch (IllegalArgumentException e) {
log.warn("unknown badge type: {}, badgeId: {}", badgeConfig.getType(), badgeId);
return SysBadgeConfigTypeEnum.ACTIVITY;
}
}
private void sendGiftNotice(PropsGiveAwayCmd cmd, String sourceType, Integer days) {
RegionConfig region = userRegionGateway.getRegionConfigByUserId(cmd.getAcceptUserId()); RegionConfig region = userRegionGateway.getRegionConfigByUserId(cmd.getAcceptUserId());
if (region == null) { if (region == null) {
return; return;

View File

@ -1,6 +1,5 @@
package com.red.circle.other.app.service.user.relation; package com.red.circle.other.app.service.user.relation;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.common.business.dto.cmd.UserIdCmd; import com.red.circle.common.business.dto.cmd.UserIdCmd;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd; import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.command.user.DismissCpApplyCmdExe; import com.red.circle.other.app.command.user.DismissCpApplyCmdExe;
@ -15,10 +14,12 @@ import com.red.circle.other.app.command.user.query.UserCpRelationQueryV4Exe;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO;
import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
import com.red.circle.other.app.util.DistributedLockUtil; import com.red.circle.other.app.util.DistributedLockUtil;
@ -26,6 +27,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* 用户cp关系实现. * 用户cp关系实现.
@ -53,13 +55,14 @@ public class UserCpRelationServiceImpl implements UserCpRelationService {
} }
@Override @Override
public List<CpPairUserProfileCO> getCpPairUserProfileCO(AppExtCommand cmd) { public List<CpPairUserProfileCO> getCpPairUserProfileCO(CpRelationshipQueryCmd cmd) {
return userCpPairUserProfileQryExe.execute(cmd); return userCpPairUserProfileQryExe.execute(cmd);
} }
@Override @Override
public void sendApply(CpApplyCmd cmd) { public void sendApply(CpApplyCmd cmd) {
String key = "SEND_CP_APPLY:" + cmd.getReqUserId() + "_" + cmd.getAcceptApplyUserId(); String key = "SEND_CP_APPLY:" + Objects.toString(cmd.getRelationType(), "CP")
+ "_" + cmd.getReqUserId() + "_" + cmd.getAcceptApplyUserId();
distributedLockUtil.executeWithLock(key, () -> { distributedLockUtil.executeWithLock(key, () -> {
sendCpApplyCmdExe.execute(cmd); sendCpApplyCmdExe.execute(cmd);
}); });
@ -79,7 +82,7 @@ public class UserCpRelationServiceImpl implements UserCpRelationService {
} }
@Override @Override
public String getCpApplyId(UserIdCmd cmd) { public String getCpApplyId(CpApplyIdQueryCmd cmd) {
return cpApplyIdQryExe.execute(cmd); return cpApplyIdQryExe.execute(cmd);
} }

View File

@ -1,17 +1,18 @@
package com.red.circle.other.app.service.user.user; package com.red.circle.other.app.service.user.user;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.convertor.user.CpBlessRecordConvert; import com.red.circle.other.app.convertor.user.CpBlessRecordConvert;
import com.red.circle.other.app.convertor.user.UserCpCabinConvertor; import com.red.circle.other.app.convertor.user.UserCpCabinConvertor;
import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO; import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord; import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship; import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService; import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,7 +37,7 @@ public class CpBlessRecordAndUpReadExe {
private final CpRelationshipService cpRelationshipService; private final CpRelationshipService cpRelationshipService;
private final CpBlessRecordConvert cpBlessRecordConvert; private final CpBlessRecordConvert cpBlessRecordConvert;
public UserCpCabinUnreadCO execute(UserIdCommonCmd cmd) { public UserCpCabinUnreadCO execute(UserCpCabinQueryCmd cmd) {
if (!Objects.equals(cmd.getReqUserId(), cmd.getUserId())) { if (!Objects.equals(cmd.getReqUserId(), cmd.getUserId())) {
return UserCpCabinUnreadCO.builder() return UserCpCabinUnreadCO.builder()
@ -45,7 +46,8 @@ public class CpBlessRecordAndUpReadExe {
.build(); .build();
} }
CpRelationship relationship = cpRelationshipService.getByUserId(cmd.getReqUserId(), cmd.getUserId()); CpRelationship relationship = cpRelationshipService.getByUserIdOrCpUserId(
cmd.getUserId(), CpRelationshipType.normalize(cmd.getRelationType()));
if (Objects.isNull(relationship)) { if (Objects.isNull(relationship)) {
return UserCpCabinUnreadCO.builder() return UserCpCabinUnreadCO.builder()
.topClassRecords(Lists.newArrayList()) .topClassRecords(Lists.newArrayList())

View File

@ -1,15 +1,18 @@
package com.red.circle.other.app.service.user.user; package com.red.circle.other.app.service.user.user;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.command.user.query.UserCpBlessRecordQueryExe; import com.red.circle.other.app.command.user.query.UserCpBlessRecordQueryExe;
import com.red.circle.other.app.command.user.query.UserCpCabinQueryExe; import com.red.circle.other.app.command.user.query.UserCpCabinQueryExe;
import com.red.circle.other.app.command.user.query.UserCpCabinValueQueryExe; import com.red.circle.other.app.command.user.query.UserCpCabinValueQueryExe;
import com.red.circle.other.app.command.user.query.UserCpRightsConfigQueryExe;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpRightsConfigCO;
import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO; import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRightsConfigQueryCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd; import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -26,19 +29,20 @@ public class UserCpCabinServiceImpl implements UserCpCabinService {
private final UserCpCabinValueQueryExe userCpCabinValueQueryExe; private final UserCpCabinValueQueryExe userCpCabinValueQueryExe;
private final CpBlessRecordAndUpReadExe cpBlessRecordAndUpReadExe; private final CpBlessRecordAndUpReadExe cpBlessRecordAndUpReadExe;
private final UserCpBlessRecordQueryExe userCpBlessRecordQueryExe; private final UserCpBlessRecordQueryExe userCpBlessRecordQueryExe;
private final UserCpRightsConfigQueryExe userCpRightsConfigQueryExe;
@Override @Override
public UserCpCabinCO getCpCabinInfo(UserIdCommonCmd cmd) { public UserCpCabinCO getCpCabinInfo(UserCpCabinQueryCmd cmd) {
return userCpCabinQueryExe.execute(cmd); return userCpCabinQueryExe.execute(cmd);
} }
@Override @Override
public UserCpCabinValueCO getCpCabinValue(UserIdCommonCmd cmd) { public UserCpCabinValueCO getCpCabinValue(UserCpCabinQueryCmd cmd) {
return userCpCabinValueQueryExe.execute(cmd); return userCpCabinValueQueryExe.execute(cmd);
} }
@Override @Override
public UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserIdCommonCmd cmd) { public UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserCpCabinQueryCmd cmd) {
return cpBlessRecordAndUpReadExe.execute(cmd); return cpBlessRecordAndUpReadExe.execute(cmd);
} }
@ -46,4 +50,9 @@ public class UserCpCabinServiceImpl implements UserCpCabinService {
public List<CpBlessRecordCO> pageCpBlessRecord(UserCpBlessRecordQueryCmd cmd) { public List<CpBlessRecordCO> pageCpBlessRecord(UserCpBlessRecordQueryCmd cmd) {
return userCpBlessRecordQueryExe.execute(cmd); return userCpBlessRecordQueryExe.execute(cmd);
} }
@Override
public CpRightsConfigCO getCpRightsConfig(CpRightsConfigQueryCmd cmd) {
return userCpRightsConfigQueryExe.execute(cmd);
}
} }

View File

@ -0,0 +1,110 @@
package com.red.circle.other.app.command.user;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
class SendCpApplyCmdExeTest {
@Test
void autoCreateFromCpGiftShouldNotRestrictBrotherAndSistersGender() {
Fixture fixture = new Fixture(1, 0);
assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "BROTHER"));
assertEquals("BROTHER", fixture.savedApply().getRelationType());
fixture = new Fixture(0, 1);
assertTrue(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "SISTERS"));
assertEquals("SISTERS", fixture.savedApply().getRelationType());
}
@Test
void autoCreateFromCpGiftShouldSkipSameGenderCp() {
Fixture fixture = new Fixture(1, 1);
assertFalse(fixture.exe.autoCreateFromCpGift(1001L, 2001L, "LIKEI", "CP"));
verify(fixture.cpApplyService, never()).save(any(CpApply.class));
}
private static class Fixture {
private final CpApplyService cpApplyService = mock(CpApplyService.class);
private final SendCpApplyCmdExe exe;
private Fixture(Integer sendSex, Integer acceptSex) {
OfficialNoticeClient officialNoticeClient = mock(OfficialNoticeClient.class);
WalletGoldClient walletGoldClient = mock(WalletGoldClient.class);
UserMqMessageService userMqMessageService = mock(UserMqMessageService.class);
CpRelationshipService cpRelationshipService = mock(CpRelationshipService.class);
EnumConfigCacheService enumConfigCacheService = mock(EnumConfigCacheService.class);
UserProfileGateway userProfileGateway = mock(UserProfileGateway.class);
UserProfileAppConvertor userProfileAppConvertor = mock(UserProfileAppConvertor.class);
UserRegionGateway userRegionGateway = mock(UserRegionGateway.class);
UserProfile sendProfile = new UserProfile();
UserProfile acceptProfile = new UserProfile();
UserProfileDTO sendProfileDTO = profile(1001L, sendSex);
UserProfileDTO acceptProfileDTO = profile(2001L, acceptSex);
when(userRegionGateway.checkEqRegion(1001L, 2001L)).thenReturn(true);
when(userProfileGateway.getByUserId(1001L)).thenReturn(sendProfile);
when(userProfileGateway.getByUserId(2001L)).thenReturn(acceptProfile);
when(userProfileAppConvertor.toUserProfileDTO(sendProfile)).thenReturn(sendProfileDTO);
when(userProfileAppConvertor.toUserProfileDTO(acceptProfile)).thenReturn(acceptProfileDTO);
when(officialNoticeClient.send(any(NoticeExtTemplateTypeCmd.class)))
.thenReturn(ResultResponse.success());
exe = new SendCpApplyCmdExe(
cpApplyService,
officialNoticeClient,
walletGoldClient,
userMqMessageService,
cpRelationshipService,
enumConfigCacheService,
userProfileGateway,
userProfileAppConvertor,
userRegionGateway
);
}
private CpApply savedApply() {
ArgumentCaptor<CpApply> captor = ArgumentCaptor.forClass(CpApply.class);
verify(cpApplyService).save(captor.capture());
return captor.getValue();
}
private static UserProfileDTO profile(Long userId, Integer sex) {
UserProfileDTO userProfile = new UserProfileDTO();
userProfile.setId(userId);
userProfile.setUserSex(sex);
userProfile.setAccount(userId.toString());
userProfile.setUserNickname("user-" + userId);
userProfile.setCountryCode("SA");
userProfile.setCountryName("Saudi Arabia");
return userProfile;
}
}
}

View File

@ -0,0 +1,52 @@
package com.red.circle.other.app.listener.user;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import com.red.circle.other.domain.model.user.OwnSpecialId;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.tool.core.date.TimestampUtils;
import org.junit.jupiter.api.Test;
class OnlineStatusUploadListenerTest {
@Test
void resolveActiveUserBitmapOffset_shouldUseNumericAccount() {
UserProfile profile = new UserProfile();
profile.setId(9988L);
profile.setAccount("123456");
assertEquals(123456L, OnlineStatusUploadListener.resolveActiveUserBitmapOffset(profile));
}
@Test
void resolveActiveUserBitmapOffset_shouldFallbackToNumericSpecialAccount() {
UserProfile profile = new UserProfile();
profile.setId(9988L);
profile.setAccount("abc");
OwnSpecialId ownSpecialId = new OwnSpecialId();
ownSpecialId.setAccount("654321");
ownSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(1));
profile.setOwnSpecialId(ownSpecialId);
assertEquals(654321L, OnlineStatusUploadListener.resolveActiveUserBitmapOffset(profile));
}
@Test
void resolveActiveUserBitmapOffset_shouldFallbackToUserIdWhenAccountIsNotNumeric() {
UserProfile profile = new UserProfile();
profile.setId(9988L);
profile.setAccount("abc");
assertEquals(9988L, OnlineStatusUploadListener.resolveActiveUserBitmapOffset(profile));
}
@Test
void resolveActiveUserBitmapOffset_shouldReturnNullWhenNoPositiveCandidateExists() {
UserProfile profile = new UserProfile();
profile.setId(0L);
profile.setAccount("abc");
assertNull(OnlineStatusUploadListener.resolveActiveUserBitmapOffset(profile));
}
}

View File

@ -68,6 +68,11 @@ public class GiftConfigCO extends ClientObject {
*/ */
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 规格ID. * 规格ID.
*/ */

View File

@ -31,6 +31,11 @@ public class CpCabinUserProfileCO extends ClientObject {
*/ */
private UserProfileDTO userProfile; private UserProfileDTO userProfile;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
private String relationType;
/** /**
* 组建天数. * 组建天数.
*/ */

View File

@ -33,6 +33,11 @@ public class CpPairUserProfileCO extends ClientObject {
private UserProfileDTO cpUserProfile; private UserProfileDTO cpUserProfile;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
private String relationType;
/** /**
* CP关系状态NORMAL-正常, DISMISSING-分手中 * CP关系状态NORMAL-正常, DISMISSING-分手中
*/ */
@ -54,5 +59,16 @@ public class CpPairUserProfileCO extends ClientObject {
*/ */
private String firstDay; private String firstDay;
/**
* 解除冷静期结束时间戳.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long dismissEndTime;
/**
* 解除冷静期剩余秒数.
*/
private Long dismissRemainSeconds;
} }

View File

@ -0,0 +1,116 @@
package com.red.circle.other.app.dto.clientobject.user.relation.cp;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import java.io.Serial;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* CP rights config.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CpRightsConfigCO extends ClientObject {
@Serial
private static final long serialVersionUID = 1L;
/**
* Relationship type: CP/BROTHER/SISTERS.
*/
private String relationType;
/**
* Relationship user id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long cpUserId;
/**
* NORMAL/DISMISSING/NONE.
*/
private String relationshipStatus;
/**
* Current intimacy value.
*/
private Long intimacyValue;
/**
* Level rights config.
*/
private List<LevelCO> levels;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class LevelCO {
/**
* Level number.
*/
private Integer level;
/**
* Level display name.
*/
private String levelName;
/**
* Required intimacy value.
*/
private Long requiredIntimacyValue;
/**
* Whether current relationship has unlocked this level.
*/
private Boolean unlocked;
/**
* Rights under this level.
*/
private List<RightCO> rights;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class RightCO {
/**
* Stable right key.
*/
private String rightKey;
/**
* Right icon.
*/
private String icon;
/**
* Right title.
*/
private String title;
/**
* Right description.
*/
private String desc;
/**
* Whether current relationship has unlocked this right.
*/
private Boolean unlocked;
}
}

View File

@ -26,6 +26,11 @@ public class CpUserProfileCO extends ClientObject {
*/ */
private UserProfileDTO userProfile; private UserProfileDTO userProfile;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
private String relationType;
/** /**
* 组建天数. * 组建天数.
*/ */

View File

@ -26,6 +26,10 @@ public class CpApplyCmd extends AppExtCommand {
@NotNull(message = "acceptApplyUserId required.") @NotNull(message = "acceptApplyUserId required.")
private Long acceptApplyUserId; private Long acceptApplyUserId;
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
public String getAcceptApplyUserIdString() { public String getAcceptApplyUserIdString() {
return getAcceptApplyUserId().toString(); return getAcceptApplyUserId().toString();

View File

@ -25,4 +25,9 @@ public class CpApplyDismissCmd extends AppExtCommand {
@NotNull @NotNull
private Long cpUserId; private Long cpUserId;
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
} }

View File

@ -0,0 +1,18 @@
package com.red.circle.other.app.dto.cmd.user.relation.cp;
import com.red.circle.common.business.dto.cmd.UserIdCmd;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* CP apply id query.
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CpApplyIdQueryCmd extends UserIdCmd {
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
}

View File

@ -0,0 +1,20 @@
package com.red.circle.other.app.dto.cmd.user.relation.cp;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* CP relationship query.
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class CpRelationshipQueryCmd extends AppExtCommand {
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
}

View File

@ -0,0 +1,26 @@
package com.red.circle.other.app.dto.cmd.user.relation.cp;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.core.dto.AppCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* CP rights config query.
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CpRightsConfigQueryCmd extends AppCommand {
/**
* Relationship type: CP/BROTHER/SISTERS, defaults to CP.
*/
private String relationType;
/**
* Optional relationship user id. If absent, only config can be returned safely.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long cpUserId;
}

View File

@ -29,4 +29,9 @@ public class UserCpLightCmd extends AppCommand {
@NotNull(message = "type required.") @NotNull(message = "type required.")
private String type; private String type;
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
} }

View File

@ -21,4 +21,9 @@ public class UserCpBlessRecordQueryCmd extends AppCommand {
* 最后一条记录id. * 最后一条记录id.
*/ */
private Long lastId; private Long lastId;
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
} }

View File

@ -0,0 +1,18 @@
package com.red.circle.other.app.dto.cmd.user.user;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* CP cabin query.
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class UserCpCabinQueryCmd extends UserIdCommonCmd {
/**
* 关系类型CP/BROTHER/SISTERS默认 CP.
*/
private String relationType;
}

View File

@ -1,13 +1,14 @@
package com.red.circle.other.app.service.user.relation; package com.red.circle.other.app.service.user.relation;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.common.business.dto.cmd.UserIdCmd; import com.red.circle.common.business.dto.cmd.UserIdCmd;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd; import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyIdQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd;
@ -22,7 +23,7 @@ public interface UserCpRelationService {
CpUserProfileCO getCpUser(UserIdCmd cmd); CpUserProfileCO getCpUser(UserIdCmd cmd);
List<CpPairUserProfileCO> getCpPairUserProfileCO(AppExtCommand cmd); List<CpPairUserProfileCO> getCpPairUserProfileCO(CpRelationshipQueryCmd cmd);
void sendApply(CpApplyCmd cmd); void sendApply(CpApplyCmd cmd);
@ -30,7 +31,7 @@ public interface UserCpRelationService {
void dismissApply(CpApplyDismissCmd cmd); void dismissApply(CpApplyDismissCmd cmd);
String getCpApplyId(UserIdCmd cmd); String getCpApplyId(CpApplyIdQueryCmd cmd);
CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd); CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd);

View File

@ -1,12 +1,14 @@
package com.red.circle.other.app.service.user.user; package com.red.circle.other.app.service.user.user;
import com.red.circle.common.business.dto.cmd.UserIdCommonCmd;
import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO; import com.red.circle.other.app.dto.clientobject.user.user.CpBlessRecordCO;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpRightsConfigCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinUnreadCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO; import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinValueCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRightsConfigQueryCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd; import com.red.circle.other.app.dto.cmd.user.user.UserCpBlessRecordQueryCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import java.util.List; import java.util.List;
/** /**
@ -15,11 +17,13 @@ import java.util.List;
*/ */
public interface UserCpCabinService { public interface UserCpCabinService {
UserCpCabinCO getCpCabinInfo(UserIdCommonCmd cmd); UserCpCabinCO getCpCabinInfo(UserCpCabinQueryCmd cmd);
UserCpCabinValueCO getCpCabinValue(UserIdCommonCmd cmd); UserCpCabinValueCO getCpCabinValue(UserCpCabinQueryCmd cmd);
UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserIdCommonCmd cmd); UserCpCabinUnreadCO getCpBlessRecordAndUpRead(UserCpCabinQueryCmd cmd);
List<CpBlessRecordCO> pageCpBlessRecord(UserCpBlessRecordQueryCmd cmd); List<CpBlessRecordCO> pageCpBlessRecord(UserCpBlessRecordQueryCmd cmd);
CpRightsConfigCO getCpRightsConfig(CpRightsConfigQueryCmd cmd);
} }

View File

@ -9,6 +9,7 @@ import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService; import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService; import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.model.dto.user.CpSimpleUserProfileCO; import com.red.circle.other.inner.model.dto.user.CpSimpleUserProfileCO;
import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.DateUtils; import com.red.circle.tool.core.date.DateUtils;
@ -80,7 +81,7 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
} }
BigDecimal cpValue = cpValueMap.getOrDefault(cpRelationship.getCpValId(), BigDecimal.ZERO); BigDecimal cpValue = cpValueMap.getOrDefault(cpRelationship.getCpValId(), BigDecimal.ZERO);
Long days = DateUtils.toDurationDays(cpRelationship.getCreateTime(), DateUtils.now()); Long days = relationshipDays(cpRelationship.getCreateTime());
String firstDay = cpRelationship.getCreateTime().toInstant() String firstDay = cpRelationship.getCreateTime().toInstant()
.atZone(ZoneId.systemDefault()).toLocalDate().toString(); .atZone(ZoneId.systemDefault()).toLocalDate().toString();
@ -89,6 +90,7 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
.setMeAccount(baseInfo.getAccount()) .setMeAccount(baseInfo.getAccount())
.setMeUserAvatar(baseInfo.getUserAvatar()) .setMeUserAvatar(baseInfo.getUserAvatar())
.setMeUserNickname(baseInfo.getUserNickname()) .setMeUserNickname(baseInfo.getUserNickname())
.setRelationType(CpRelationshipType.normalize(cpRelationship.getRelationType()))
.setCpUserId(cpUser.getId()) .setCpUserId(cpUser.getId())
.setCpAccount(cpUser.getAccount()) .setCpAccount(cpUser.getAccount())
.setCpUserAvatar(cpUser.getUserAvatar()) .setCpUserAvatar(cpUser.getUserAvatar())
@ -117,6 +119,14 @@ public class CpRelationshipCacheServiceImpl implements CpRelationshipCacheServic
return UserKey.CP.getKey(userId); return UserKey.CP.getKey(userId);
} }
private static long relationshipDays(Date createTime) {
Date now = DateUtils.now();
if (!createTime.before(now)) {
return 0L;
}
return DateUtils.toDurationDays(createTime, now);
}
@Override @Override
public void remove(List<Long> userIds) { public void remove(List<Long> userIds) {
userIds.forEach(userId -> { userIds.forEach(userId -> {

View File

@ -32,6 +32,11 @@ public class GiftValue implements Serializable {
*/ */
private String giftType; private String giftType;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
private String cpRelationType;
/** /**
* 礼物单价. * 礼物单价.
*/ */

View File

@ -5,6 +5,7 @@ import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTarget
import com.red.circle.other.inner.enums.team.*; import com.red.circle.other.inner.enums.team.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -245,6 +246,12 @@ public interface TeamMemberTargetService {
*/ */
TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong); TeamMemberTarget aggregateMonthlyTarget(Long teamId, Long userId, Integer monthlyBillBelong);
/**
* 按国家查询当前可手动发放工资的成员目标.
*/
List<TeamMemberTarget> listManualSalaryTargets(String sysOrigin, String countryCode,
Collection<Long> userIds, Integer billBelong);
/** /**
* 团队用户已结算的工资 * 团队用户已结算的工资
*/ */

View File

@ -6,6 +6,7 @@ import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyMana
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* 团队工资实时支付记录. * 团队工资实时支付记录.
@ -82,6 +83,18 @@ public interface TeamSalaryPaymentService {
*/ */
TeamSalaryPayment getOneById(String id); TeamSalaryPayment getOneById(String id);
/**
* 按用户和账期批量获得数据.
*/
Map<String, TeamSalaryPayment> mapByUserIdsAndDateNumbers(String sysOrigin, Set<Long> userIds,
Set<Integer> dateNumbers);
/**
* 按支付用户或实际收款用户批量获得数据.
*/
List<TeamSalaryPayment> listByPaymentOrRecipientUserIdsAndDateNumbers(String sysOrigin,
Set<Long> userIds, Set<Integer> dateNumbers);
/** /**
* 基于父ID修改子记录状态为已退款. * 基于父ID修改子记录状态为已退款.
* *

View File

@ -7,6 +7,7 @@ import com.mongodb.client.model.Filters;
import com.mongodb.client.result.UpdateResult; import com.mongodb.client.result.UpdateResult;
import com.red.circle.component.mongodb.config.MongoPrimaryService; import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.framework.mybatis.constant.PageConstant; import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget; import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTargetQuery; import com.red.circle.other.infra.database.mongo.dto.team.query.TeamMemberTargetQuery;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService; import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
@ -16,11 +17,13 @@ import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils; import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedId; import com.red.circle.tool.core.date.ZonedId;
import com.red.circle.tool.core.sequence.IdWorkerUtils; import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalAdjusters;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -51,6 +54,7 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
private final MongoTemplate mongoTemplate; private final MongoTemplate mongoTemplate;
private final MongoPrimaryService mongoPrimaryService; private final MongoPrimaryService mongoPrimaryService;
private final CountryCodeAliasSupport countryCodeAliasSupport;
@Override @Override
public void createMemberTarget(TeamMemberTarget param) { public void createMemberTarget(TeamMemberTarget param) {
@ -818,6 +822,26 @@ public class TeamMemberTargetServiceImpl implements TeamMemberTargetService {
return aggregated; return aggregated;
} }
@Override
public List<TeamMemberTarget> listManualSalaryTargets(String sysOrigin, String countryCode,
Collection<Long> userIds, Integer billBelong) {
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
.and("history").is(Boolean.FALSE)
.and("billBelong").is(billBelong);
if (StringUtils.isNotBlank(countryCode)) {
criteria.and("countryCode").in(countryCodeAliasSupport.getMatchCodes(countryCode));
}
if (CollectionUtils.isNotEmpty(userIds)) {
criteria.and("userId").in(userIds);
}
return mongoTemplate.find(Query.query(criteria)
.with(Sort.by(Order.desc("timeId"))),
TeamMemberTarget.class);
}
@Override @Override
public BigDecimal listMemberSettledAndSumSalary(Long teamId, Long userId, TeamMemberRole role) { public BigDecimal listMemberSettledAndSumSalary(Long teamId, Long userId, TeamMemberRole role) {
Query query = new Query(); Query query = new Query();

View File

@ -17,6 +17,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
@ -183,6 +185,46 @@ public class TeamSalaryPaymentServiceImpl implements TeamSalaryPaymentService {
return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), TeamSalaryPayment.class); return mongoTemplate.findOne(Query.query(Criteria.where("id").is(id)), TeamSalaryPayment.class);
} }
@Override
public Map<String, TeamSalaryPayment> mapByUserIdsAndDateNumbers(String sysOrigin,
Set<Long> userIds, Set<Integer> dateNumbers) {
if (StringUtils.isBlank(sysOrigin) || CollectionUtils.isEmpty(userIds)
|| CollectionUtils.isEmpty(dateNumbers)) {
return Maps.newHashMap();
}
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
.and("userId").in(userIds)
.and("dateNumber").in(dateNumbers);
return mongoTemplate.find(Query.query(criteria), TeamSalaryPayment.class)
.stream()
.filter(payment -> StringUtils.isNotBlank(payment.getId()))
.collect(Collectors.toMap(TeamSalaryPayment::getId, payment -> payment,
(first, second) -> first));
}
@Override
public List<TeamSalaryPayment> listByPaymentOrRecipientUserIdsAndDateNumbers(String sysOrigin,
Set<Long> userIds, Set<Integer> dateNumbers) {
if (StringUtils.isBlank(sysOrigin) || CollectionUtils.isEmpty(userIds)
|| CollectionUtils.isEmpty(dateNumbers)) {
return List.of();
}
Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
.and("dateNumber").in(dateNumbers)
.orOperator(
Criteria.where("userId").in(userIds),
Criteria.where("salarySendDetails.teamMemberId").in(userIds),
Criteria.where("salarySendDetails.teamOwnId").in(userIds)
);
return mongoTemplate.find(Query.query(criteria), TeamSalaryPayment.class);
}
@Override @Override
public void updateDetails(String id, List<TeamSalaryPaymentDetails> salarySendDetails) { public void updateDetails(String id, List<TeamSalaryPaymentDetails> salarySendDetails) {

View File

@ -99,7 +99,8 @@ public class SysRegionConfigServiceImpl implements SysRegionConfigService {
SysRegionConfig.class)) SysRegionConfig.class))
.map(region -> region.stream().collect( .map(region -> region.stream().collect(
Collectors.toMap(SysRegionConfig::getId, Collectors.toMap(SysRegionConfig::getId,
v -> StringUtils.isBlankOrElse(v.getRegionName(), "")))) v -> StringUtils.isBlankOrElse(v.getRegionName(), ""),
(first, second) -> first)))
.orElseGet(Maps::newHashMap); .orElseGet(Maps::newHashMap);
} }
@ -133,7 +134,8 @@ public class SysRegionConfigServiceImpl implements SysRegionConfigService {
SysRegionConfig.class)) SysRegionConfig.class))
.map(region -> region.stream().collect( .map(region -> region.stream().collect(
Collectors.toMap(SysRegionConfig::getId, Collectors.toMap(SysRegionConfig::getId,
v -> StringUtils.isBlankOrElse(v.getRegionName(), "")))) v -> StringUtils.isBlankOrElse(v.getRegionName(), ""),
(first, second) -> first)))
.orElseGet(Maps::newHashMap); .orElseGet(Maps::newHashMap);
} }
@ -177,7 +179,8 @@ public class SysRegionConfigServiceImpl implements SysRegionConfigService {
return CollectionUtils.newHashMap(); return CollectionUtils.newHashMap();
} }
return regionConfigs.stream().collect(Collectors.toMap(SysRegionConfig::getId, v -> v)); return regionConfigs.stream().collect(
Collectors.toMap(SysRegionConfig::getId, v -> v, (first, second) -> first));
} }
@Override @Override

View File

@ -112,6 +112,12 @@ public class GiftConfig extends TimestampBaseEntity implements Serializable {
@TableField("gift_tab") @TableField("gift_tab")
private String giftTab; private String giftTab;
/**
* CP礼物关系类型CP/BROTHER/SISTERS.
*/
@TableField("cp_relation_type")
private String cpRelationType;
/** /**
* 0.未删除 1.已删除. * 0.未删除 1.已删除.
*/ */

View File

@ -46,6 +46,12 @@ public class CpApply extends TimestampBaseEntity {
@TableField("accept_apply_user_id") @TableField("accept_apply_user_id")
private Long acceptApplyUserId; private Long acceptApplyUserId;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
@TableField("relation_type")
private String relationType;
/** /**
* 申请消费金币. * 申请消费金币.
*/ */

View File

@ -58,6 +58,12 @@ public class CpRelationship extends TimestampBaseEntity {
@TableField("cp_user_id") @TableField("cp_user_id")
private Long cpUserId; private Long cpUserId;
/**
* 关系类型CP/BROTHER/SISTERS.
*/
@TableField("relation_type")
private String relationType;
/** /**
* 0.发起人 1.接收人 * 0.发起人 1.接收人
*/ */

View File

@ -8,6 +8,7 @@ import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.gift.GiftConfigDAO; import com.red.circle.other.infra.database.rds.dao.gift.GiftConfigDAO;
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.material.GiftCurrencyType; import com.red.circle.other.inner.enums.material.GiftCurrencyType;
import com.red.circle.other.inner.enums.material.GiftSpecialEnum; import com.red.circle.other.inner.enums.material.GiftSpecialEnum;
import com.red.circle.other.inner.enums.material.GiftTabEnum; import com.red.circle.other.inner.enums.material.GiftTabEnum;
@ -225,6 +226,7 @@ public class GiftConfigServiceImpl extends BaseServiceImpl<GiftConfigDAO, GiftCo
.set(GiftConfig::getType, giftConfig.getType()) .set(GiftConfig::getType, giftConfig.getType())
.set(GiftConfig::getSort, giftConfig.getSort()) .set(GiftConfig::getSort, giftConfig.getSort())
.set(GiftConfig::getGiftTab, giftConfig.getGiftTab()) .set(GiftConfig::getGiftTab, giftConfig.getGiftTab())
.set(GiftConfig::getCpRelationType, normalizeCpRelationType(giftConfig))
.set(GiftConfig::getStandardId, giftConfig.getStandardId()) .set(GiftConfig::getStandardId, giftConfig.getStandardId())
.set(GiftConfig::getExpiredTime, giftConfig.getExpiredTime()) .set(GiftConfig::getExpiredTime, giftConfig.getExpiredTime())
.set(GiftConfig::getExplanationGift, .set(GiftConfig::getExplanationGift,
@ -236,6 +238,14 @@ public class GiftConfigServiceImpl extends BaseServiceImpl<GiftConfigDAO, GiftCo
.execute(); .execute();
} }
private String normalizeCpRelationType(GiftConfig giftConfig) {
if (Objects.isNull(giftConfig)
|| !Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.CP.name())) {
return null;
}
return CpRelationshipType.normalize(giftConfig.getCpRelationType());
}
@Override @Override
public Boolean switchDelStatus(Long id, Boolean status) { public Boolean switchDelStatus(Long id, Boolean status) {
return update().set(GiftConfig::getDel, status) return update().set(GiftConfig::getDel, status)

View File

@ -23,6 +23,11 @@ public interface CpApplyService extends BaseService<CpApply> {
*/ */
void dismiss(Long requestUserId); void dismiss(Long requestUserId);
/**
* 解散指定类型关系申请.
*/
void dismiss(Long requestUserId, String relationType);
/** /**
* 获取申请记录. * 获取申请记录.
* *
@ -32,6 +37,11 @@ public interface CpApplyService extends BaseService<CpApply> {
*/ */
Long getId(Long acceptApplyUserId, Long sendApplyUserId); Long getId(Long acceptApplyUserId, Long sendApplyUserId);
/**
* 获取指定类型申请记录.
*/
Long getId(Long acceptApplyUserId, Long sendApplyUserId, String relationType);
/** /**
* 申请记录是否存在. * 申请记录是否存在.
* *
@ -41,6 +51,11 @@ public interface CpApplyService extends BaseService<CpApply> {
*/ */
boolean exists(Long acceptApplyUserId, Long sendApplyUserId); boolean exists(Long acceptApplyUserId, Long sendApplyUserId);
/**
* 指定类型申请记录是否存在.
*/
boolean exists(Long acceptApplyUserId, Long sendApplyUserId, String relationType);
/** /**
* 是否存在发送记录. * 是否存在发送记录.
* *
@ -49,6 +64,11 @@ public interface CpApplyService extends BaseService<CpApply> {
*/ */
boolean existsNotProcessSendApply(Long sendApplyUserId); boolean existsNotProcessSendApply(Long sendApplyUserId);
/**
* 是否存在指定类型发送记录.
*/
boolean existsNotProcessSendApply(Long sendApplyUserId, String relationType);
/** /**
* 改变状态同意. * 改变状态同意.
* *
@ -65,6 +85,11 @@ public interface CpApplyService extends BaseService<CpApply> {
*/ */
void refuseStatusWait(Long acceptApplyUserId); void refuseStatusWait(Long acceptApplyUserId);
/**
* 拒绝指定类型所有等待状态.
*/
void refuseStatusWait(Long acceptApplyUserId, String relationType);
/** /**
* 获取我拒绝都信息. * 获取我拒绝都信息.
*/ */

View File

@ -31,11 +31,21 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
boolean existsCp(Long userId, Long cpUserId); boolean existsCp(Long userId, Long cpUserId);
/**
* 是否存在指定类型关系.
*/
boolean existsCp(Long userId, Long cpUserId, String relationType);
/** /**
* 获取分手中的cp信息集合 * 获取分手中的cp信息集合
*/ */
List<CpRelationship> getDismissingByUserId(Long userId); List<CpRelationship> getDismissingByUserId(Long userId);
/**
* 获取指定类型分手中的cp信息集合.
*/
List<CpRelationship> getDismissingByUserId(Long userId, String relationType);
/** /**
* 获取cp用户id. * 获取cp用户id.
* *
@ -49,11 +59,21 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
List<CpRelationship> getByUserId(Long userId); List<CpRelationship> getByUserId(Long userId);
/**
* 获取指定类型cp信息集合.
*/
List<CpRelationship> getByUserId(Long userId, String relationType);
/** /**
* 获取cp信息. * 获取cp信息.
*/ */
CpRelationship getByUserId(Long userId, Long cpUserId); CpRelationship getByUserId(Long userId, Long cpUserId);
/**
* 获取指定类型cp信息.
*/
CpRelationship getByUserId(Long userId, Long cpUserId, String relationType);
/** /**
* 获取cpValId * 获取cpValId
* *
@ -77,6 +97,12 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId, String sysOrigin); void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId, String sysOrigin);
/**
* 组建指定类型关系.
*/
void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId, String sysOrigin,
String relationType);
/** /**
* 获取cp信息. * 获取cp信息.
* *
@ -92,6 +118,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
CpRelationship getByUserIdOrCpUserId(Long userId); CpRelationship getByUserIdOrCpUserId(Long userId);
/**
* 按关系类型获取任一用户关联关系.
*/
CpRelationship getByUserIdOrCpUserId(Long userId, String relationType);
/** /**
* 获取用户正常CP数量. * 获取用户正常CP数量.
* *
@ -100,6 +131,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
int countCp(Long userId); int countCp(Long userId);
/**
* 获取用户指定类型正常关系数量.
*/
int countCp(Long userId, String relationType);
/** /**
* 获取分手中的CP关系. * 获取分手中的CP关系.
* *
@ -109,6 +145,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
CpRelationship getDismissingCp(Long userId, Long cpUserId); CpRelationship getDismissingCp(Long userId, Long cpUserId);
/**
* 获取指定类型分手中的关系.
*/
CpRelationship getDismissingCp(Long userId, Long cpUserId, String relationType);
/** /**
* 更新CP关系为分手中. * 更新CP关系为分手中.
* *
@ -116,6 +157,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
void updateToDismissing(Long userId, Long cpUserId); void updateToDismissing(Long userId, Long cpUserId);
/**
* 更新指定类型关系为分手中.
*/
void updateToDismissing(Long userId, Long cpUserId, String relationType);
/** /**
* 复合CP更新为正常状态. * 复合CP更新为正常状态.
* *
@ -124,6 +170,11 @@ public interface CpRelationshipService extends BaseService<CpRelationship> {
*/ */
void reconcile(Long userId, Long cpUserId); void reconcile(Long userId, Long cpUserId);
/**
* 复合指定类型关系.
*/
void reconcile(Long userId, Long cpUserId, String relationType);
/** /**
* 获取7天前分手中的CP关系列表. * 获取7天前分手中的CP关系列表.
* *

View File

@ -9,6 +9,7 @@ import com.red.circle.other.infra.database.rds.dao.user.user.CpApplyDAO;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply; import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService; import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus; import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.model.cmd.user.CpApplyQryCmd; import com.red.circle.other.inner.model.cmd.user.CpApplyQryCmd;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@ -30,10 +31,16 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
@Override @Override
public void dismiss(Long requestUserId) { public void dismiss(Long requestUserId) {
dismiss(requestUserId, CpRelationshipType.CP.name());
}
@Override
public void dismiss(Long requestUserId, String relationType) {
update() update()
.set(CpApply::getStatus, CpApplyStatus.DISMISS) .set(CpApply::getStatus, CpApplyStatus.DISMISS)
.set(CpApply::getUpdateTime, LocalDateTime.now()) .set(CpApply::getUpdateTime, LocalDateTime.now())
.set(CpApply::getUpdateUser, requestUserId) .set(CpApply::getUpdateUser, requestUserId)
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
.eq(CpApply::getStatus, CpApplyStatus.AGREE) .eq(CpApply::getStatus, CpApplyStatus.AGREE)
.and(where -> where.eq(CpApply::getSendApplyUserId, requestUserId) .and(where -> where.eq(CpApply::getSendApplyUserId, requestUserId)
.or().eq(CpApply::getAcceptApplyUserId, requestUserId)) .or().eq(CpApply::getAcceptApplyUserId, requestUserId))
@ -43,10 +50,16 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
@Override @Override
public Long getId(Long acceptApplyUserId, Long sendApplyUserId) { public Long getId(Long acceptApplyUserId, Long sendApplyUserId) {
return getId(acceptApplyUserId, sendApplyUserId, CpRelationshipType.CP.name());
}
@Override
public Long getId(Long acceptApplyUserId, Long sendApplyUserId, String relationType) {
return Optional.ofNullable( return Optional.ofNullable(
query() query()
.select(CpApply::getId) .select(CpApply::getId)
.eq(CpApply::getStatus, CpApplyStatus.WAIT) .eq(CpApply::getStatus, CpApplyStatus.WAIT)
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
.eq(CpApply::getAcceptApplyUserId, acceptApplyUserId) .eq(CpApply::getAcceptApplyUserId, acceptApplyUserId)
.eq(CpApply::getSendApplyUserId, sendApplyUserId) .eq(CpApply::getSendApplyUserId, sendApplyUserId)
.last(PageConstant.LIMIT_ONE) .last(PageConstant.LIMIT_ONE)
@ -56,14 +69,25 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
@Override @Override
public boolean exists(Long acceptApplyUserId, Long sendApplyUserId) { public boolean exists(Long acceptApplyUserId, Long sendApplyUserId) {
return Optional.ofNullable(getId(acceptApplyUserId, sendApplyUserId)).isPresent(); return exists(acceptApplyUserId, sendApplyUserId, CpRelationshipType.CP.name());
}
@Override
public boolean exists(Long acceptApplyUserId, Long sendApplyUserId, String relationType) {
return Optional.ofNullable(getId(acceptApplyUserId, sendApplyUserId, relationType)).isPresent();
} }
@Override @Override
public boolean existsNotProcessSendApply(Long sendApplyUserId) { public boolean existsNotProcessSendApply(Long sendApplyUserId) {
return existsNotProcessSendApply(sendApplyUserId, CpRelationshipType.CP.name());
}
@Override
public boolean existsNotProcessSendApply(Long sendApplyUserId, String relationType) {
return Optional.ofNullable(query() return Optional.ofNullable(query()
.select(CpApply::getId) .select(CpApply::getId)
.eq(CpApply::getSendApplyUserId, sendApplyUserId) .eq(CpApply::getSendApplyUserId, sendApplyUserId)
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
.eq(CpApply::getStatus, CpApplyStatus.WAIT) .eq(CpApply::getStatus, CpApplyStatus.WAIT)
.getOne()) .getOne())
.map(cpApply -> Objects.nonNull(cpApply.getId())) .map(cpApply -> Objects.nonNull(cpApply.getId()))
@ -80,9 +104,15 @@ public class CpApplyServiceImpl extends BaseServiceImpl<CpApplyDAO, CpApply> imp
@Override @Override
public void refuseStatusWait(Long acceptApplyUserId) { public void refuseStatusWait(Long acceptApplyUserId) {
refuseStatusWait(acceptApplyUserId, CpRelationshipType.CP.name());
}
@Override
public void refuseStatusWait(Long acceptApplyUserId, String relationType) {
update() update()
.set(CpApply::getStatus, CpApplyStatus.REFUSE) .set(CpApply::getStatus, CpApplyStatus.REFUSE)
.eq(CpApply::getStatus, CpApplyStatus.WAIT) .eq(CpApply::getStatus, CpApplyStatus.WAIT)
.eq(CpApply::getRelationType, CpRelationshipType.normalize(relationType))
.eq(CpApply::getAcceptApplyUserId, acceptApplyUserId) .eq(CpApply::getAcceptApplyUserId, acceptApplyUserId)
.execute(); .execute();
} }

View File

@ -10,6 +10,7 @@ import com.red.circle.other.infra.database.rds.dao.user.user.CpRelationshipDAO;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship; import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipStatus; import com.red.circle.other.infra.enums.user.user.CpRelationshipStatus;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.*; import java.util.*;
@ -47,9 +48,15 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public boolean existsCp(Long userId, Long cpUserId) { public boolean existsCp(Long userId, Long cpUserId) {
return existsCp(userId, cpUserId, CpRelationshipType.CP.name());
}
@Override
public boolean existsCp(Long userId, Long cpUserId, String relationType) {
return Optional.ofNullable( return Optional.ofNullable(
query().select(CpRelationship::getId) query().select(CpRelationship::getId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name()) .eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.and(where -> where .and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId)) .nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
.or() .or()
@ -62,9 +69,15 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public List<CpRelationship> getDismissingByUserId(Long userId) { public List<CpRelationship> getDismissingByUserId(Long userId) {
return getDismissingByUserId(userId, CpRelationshipType.CP.name());
}
@Override
public List<CpRelationship> getDismissingByUserId(Long userId, String relationType) {
return query() return query()
.eq(CpRelationship::getUserId, userId) .eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name()) .eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.list(); .list();
} }
@ -72,6 +85,7 @@ public class CpRelationshipServiceImpl extends
public List<Long> getCpUserId(Long userId) { public List<Long> getCpUserId(Long userId) {
return query().select(CpRelationship::getCpUserId, TimestampBaseEntity::getCreateTime) return query().select(CpRelationship::getCpUserId, TimestampBaseEntity::getCreateTime)
.eq(CpRelationship::getUserId, userId) .eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getRelationType, CpRelationshipType.CP.name())
.list() .list()
.stream() .stream()
.sorted(Comparator.comparing(CpRelationship::getCreateTime).reversed()) .sorted(Comparator.comparing(CpRelationship::getCreateTime).reversed())
@ -81,17 +95,30 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public List<CpRelationship> getByUserId(Long userId) { public List<CpRelationship> getByUserId(Long userId) {
return getByUserId(userId, CpRelationshipType.CP.name());
}
@Override
public List<CpRelationship> getByUserId(Long userId, String relationType) {
return query() return query()
.eq(CpRelationship::getUserId, userId) .eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name()) .eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.eq(Objects.nonNull(relationType), CpRelationship::getRelationType,
Objects.nonNull(relationType) ? CpRelationshipType.normalize(relationType) : null)
.orderByAsc(TimestampBaseEntity::getCreateTime) .orderByAsc(TimestampBaseEntity::getCreateTime)
.list(); .list();
} }
@Override @Override
public CpRelationship getByUserId(Long userId, Long cpUserId) { public CpRelationship getByUserId(Long userId, Long cpUserId) {
return getByUserId(userId, cpUserId, CpRelationshipType.CP.name());
}
@Override
public CpRelationship getByUserId(Long userId, Long cpUserId, String relationType) {
return query() return query()
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name()) .eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.and(where -> where .and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId)) .nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
.or() .or()
@ -119,10 +146,16 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId, public void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId,
String sysOrigin) { String sysOrigin) {
addCp(sendApplyUserId, acceptApplyUserId, bindCpValId, sysOrigin, CpRelationshipType.CP.name());
}
@Override
public void addCp(Long sendApplyUserId, Long acceptApplyUserId, Long bindCpValId,
String sysOrigin, String relationType) {
CpRelationship sendUser = createCpRelationshipBean(sendApplyUserId, acceptApplyUserId, CpRelationship sendUser = createCpRelationshipBean(sendApplyUserId, acceptApplyUserId,
bindCpValId, 0, sysOrigin); bindCpValId, 0, sysOrigin, relationType);
CpRelationship cpUser = createCpRelationshipBean(acceptApplyUserId, sendApplyUserId, CpRelationship cpUser = createCpRelationshipBean(acceptApplyUserId, sendApplyUserId,
bindCpValId, 1, sysOrigin); bindCpValId, 1, sysOrigin, relationType);
saveBatch(Arrays.asList(sendUser, cpUser)); saveBatch(Arrays.asList(sendUser, cpUser));
} }
@ -136,7 +169,14 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public CpRelationship getByUserIdOrCpUserId(Long userId) { public CpRelationship getByUserIdOrCpUserId(Long userId) {
return getByUserIdOrCpUserId(userId, CpRelationshipType.CP.name());
}
@Override
public CpRelationship getByUserIdOrCpUserId(Long userId, String relationType) {
return Optional.ofNullable(query() return Optional.ofNullable(query()
.eq(Objects.nonNull(relationType), CpRelationship::getRelationType,
Objects.nonNull(relationType) ? CpRelationshipType.normalize(relationType) : null)
.and(where -> where.eq(CpRelationship::getUserId, userId) .and(where -> where.eq(CpRelationship::getUserId, userId)
.or().eq(CpRelationship::getCpUserId, userId)) .or().eq(CpRelationship::getCpUserId, userId))
.last(PageConstant.LIMIT_ONE) .last(PageConstant.LIMIT_ONE)
@ -145,11 +185,12 @@ public class CpRelationshipServiceImpl extends
} }
private CpRelationship createCpRelationshipBean(Long userId, Long cpUserId, Long bindCpValId, private CpRelationship createCpRelationshipBean(Long userId, Long cpUserId, Long bindCpValId,
Integer initiate, String sysOrigin) { Integer initiate, String sysOrigin, String relationType) {
CpRelationship cpUser = new CpRelationship() CpRelationship cpUser = new CpRelationship()
.setUserId(userId) .setUserId(userId)
.setCpUserId(cpUserId) .setCpUserId(cpUserId)
.setCpValId(bindCpValId) .setCpValId(bindCpValId)
.setRelationType(CpRelationshipType.normalize(relationType))
.setInitiate(initiate) .setInitiate(initiate)
.setSysOrigin(sysOrigin) .setSysOrigin(sysOrigin)
.setAmount(BigDecimal.ZERO) .setAmount(BigDecimal.ZERO)
@ -164,11 +205,26 @@ public class CpRelationshipServiceImpl extends
public int countCp(Long userId) { public int countCp(Long userId) {
return Math.toIntExact(query() return Math.toIntExact(query()
.eq(CpRelationship::getUserId, userId) .eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.count()); .count());
} }
@Override
public int countCp(Long userId, String relationType) {
return Math.toIntExact(query()
.eq(CpRelationship::getUserId, userId)
.eq(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.count());
}
@Override @Override
public CpRelationship getDismissingCp(Long userId, Long cpUserId) { public CpRelationship getDismissingCp(Long userId, Long cpUserId) {
return getDismissingCp(userId, cpUserId, CpRelationshipType.CP.name());
}
@Override
public CpRelationship getDismissingCp(Long userId, Long cpUserId, String relationType) {
return query() return query()
.and(where -> where .and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId)) .nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
@ -176,16 +232,23 @@ public class CpRelationshipServiceImpl extends
.nested(w -> w.eq(CpRelationship::getUserId, cpUserId).eq(CpRelationship::getCpUserId, userId)) .nested(w -> w.eq(CpRelationship::getUserId, cpUserId).eq(CpRelationship::getCpUserId, userId))
) )
.eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name()) .eq(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.last(PageConstant.LIMIT_ONE) .last(PageConstant.LIMIT_ONE)
.getOne(); .getOne();
} }
@Override @Override
public void updateToDismissing(Long userId, Long cpUserId) { public void updateToDismissing(Long userId, Long cpUserId) {
updateToDismissing(userId, cpUserId, CpRelationshipType.CP.name());
}
@Override
public void updateToDismissing(Long userId, Long cpUserId, String relationType) {
update() update()
.set(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name()) .set(CpRelationship::getStatus, CpRelationshipStatus.DISMISSING.name())
.set(CpRelationship::getDismissTime, TimestampUtils.now().getTime()) .set(CpRelationship::getDismissTime, TimestampUtils.now().getTime())
.set(CpRelationship::getUpdateTime, TimestampUtils.now()) .set(CpRelationship::getUpdateTime, TimestampUtils.now())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.and(where -> where .and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId)) .nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
.or() .or()
@ -197,10 +260,16 @@ public class CpRelationshipServiceImpl extends
@Override @Override
public void reconcile(Long userId, Long cpUserId) { public void reconcile(Long userId, Long cpUserId) {
reconcile(userId, cpUserId, CpRelationshipType.CP.name());
}
@Override
public void reconcile(Long userId, Long cpUserId, String relationType) {
update() update()
.set(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name()) .set(CpRelationship::getStatus, CpRelationshipStatus.NORMAL.name())
.set(CpRelationship::getDismissTime, null) .set(CpRelationship::getDismissTime, null)
.set(CpRelationship::getUpdateTime, TimestampUtils.now()) .set(CpRelationship::getUpdateTime, TimestampUtils.now())
.eq(CpRelationship::getRelationType, CpRelationshipType.normalize(relationType))
.and(where -> where .and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId)) .nested(w -> w.eq(CpRelationship::getUserId, userId).eq(CpRelationship::getCpUserId, cpUserId))
.or() .or()

View File

@ -0,0 +1,59 @@
package com.red.circle.other.infra.enums.user.user;
import java.util.Locale;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
/**
* CP family relationship type.
*/
public enum CpRelationshipType {
/**
* Couple, requires different genders.
*/
CP,
/**
* Brothers.
*/
BROTHER,
/**
* Sisters.
*/
SISTERS;
public static CpRelationshipType of(String value) {
if (StringUtils.isBlank(value)) {
return CP;
}
String normalized = value.trim().toUpperCase(Locale.ROOT);
if (Objects.equals(normalized, "BROTHERS")) {
return BROTHER;
}
if (Objects.equals(normalized, "SISTER")) {
return SISTERS;
}
return CpRelationshipType.valueOf(normalized);
}
public static String normalize(String value) {
return of(value).name();
}
public boolean genderMatched(Integer sendUserSex, Integer acceptUserSex) {
return switch (this) {
case CP -> !Objects.equals(sendUserSex, acceptUserSex);
case BROTHER, SISTERS -> true;
};
}
public String applyTargetText() {
return switch (this) {
case CP -> "a couple";
case BROTHER -> "brothers";
case SISTERS -> "sisters";
};
}
}

View File

@ -408,7 +408,7 @@ public class UserRegionGatewayImpl implements UserRegionGateway {
} }
Map<String, SysRegionConfig> regionMap = sysRegionConfigs.stream() Map<String, SysRegionConfig> regionMap = sysRegionConfigs.stream()
.collect(Collectors.toMap(SysRegionConfig::getId, v -> v)); .collect(Collectors.toMap(SysRegionConfig::getId, v -> v, (first, second) -> first));
if (CollectionUtils.isEmpty(regionMap)) { if (CollectionUtils.isEmpty(regionMap)) {
return Boolean.FALSE; return Boolean.FALSE;
} }
@ -775,7 +775,7 @@ public class UserRegionGatewayImpl implements UserRegionGateway {
} }
Map<String, SysRegionConfig> sysRegionConfigMap = sysRegionConfigs.stream() Map<String, SysRegionConfig> sysRegionConfigMap = sysRegionConfigs.stream()
.collect(Collectors.toMap(SysRegionConfig::getId, v -> v)); .collect(Collectors.toMap(SysRegionConfig::getId, v -> v, (first, second) -> first));
return teamProfiles.stream() return teamProfiles.stream()
.collect( .collect(

View File

@ -11,6 +11,7 @@
FROM (SELECT DISTINCT(`cp_val_id`) vId FROM (SELECT DISTINCT(`cp_val_id`) vId
FROM `user_cp_relationship` FROM `user_cp_relationship`
WHERE `sys_origin` = #{sysOrigin} WHERE `sys_origin` = #{sysOrigin}
AND `relation_type` = 'CP'
ORDER BY id DESC ORDER BY id DESC
LIMIT #{size}) tmp) LIMIT #{size}) tmp)
</select> </select>
@ -25,6 +26,7 @@
SELECT DISTINCT(`cp_val_id`) vId SELECT DISTINCT(`cp_val_id`) vId
FROM `user_cp_relationship` FROM `user_cp_relationship`
WHERE `sys_origin`= #{sysOrigin} WHERE `sys_origin`= #{sysOrigin}
AND `relation_type` = 'CP'
<if test="excludeUserId != null and excludeUserId.size() > 0"> <if test="excludeUserId != null and excludeUserId.size() > 0">
AND user_id NOT IN AND user_id NOT IN
<foreach item="item" collection="excludeUserId" open="(" separator="," close=")"> <foreach item="item" collection="excludeUserId" open="(" separator="," close=")">

View File

@ -0,0 +1,64 @@
package com.red.circle.other.infra.database.mongo.service.user.region.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.red.circle.other.infra.database.mongo.entity.user.region.SysRegionConfig;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
class SysRegionConfigServiceImplTest {
private static final String REGION_ID = "2051777551323426818";
private static final String REGION_NAME = "东南亚区";
private MongoTemplate mongoTemplate;
private SysRegionConfigServiceImpl service;
@BeforeEach
void setUp() {
mongoTemplate = mock(MongoTemplate.class);
service = new SysRegionConfigServiceImpl(mongoTemplate);
}
@Test
void mapRegionName_shouldKeepFirstWhenMongoReturnsDuplicateIds() {
when(mongoTemplate.find(any(Query.class), eq(SysRegionConfig.class))).thenReturn(List.of(
region(REGION_ID, "SEA", REGION_NAME),
region(REGION_ID, "SEA", REGION_NAME)
));
Map<String, String> result = service.mapRegionName("LIKEI");
assertEquals(Map.of(REGION_ID, REGION_NAME), result);
}
@Test
void mapAvailable_shouldKeepFirstWhenMongoReturnsDuplicateIds() {
when(mongoTemplate.find(any(Query.class), eq(SysRegionConfig.class))).thenReturn(List.of(
region(REGION_ID, "SEA", REGION_NAME),
region(REGION_ID, "SEA2", REGION_NAME)
));
Map<String, SysRegionConfig> result = service.mapAvailable(Set.of(REGION_ID));
assertEquals(1, result.size());
assertEquals("SEA", result.get(REGION_ID).getRegionCode());
}
private SysRegionConfig region(String id, String regionCode, String regionName) {
return new SysRegionConfig()
.setId(id)
.setRegionCode(regionCode)
.setRegionName(regionName)
.setDel(Boolean.FALSE);
}
}

View File

@ -0,0 +1,30 @@
package com.red.circle.other.infra.enums.user.user;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class CpRelationshipTypeTest {
@Test
void cpRequiresDifferentGender() {
assertTrue(CpRelationshipType.CP.genderMatched(1, 0));
assertTrue(CpRelationshipType.CP.genderMatched(0, 1));
assertFalse(CpRelationshipType.CP.genderMatched(1, 1));
assertFalse(CpRelationshipType.CP.genderMatched(0, 0));
}
@Test
void brotherAndSistersDoNotRestrictGender() {
assertTrue(CpRelationshipType.BROTHER.genderMatched(1, 1));
assertTrue(CpRelationshipType.BROTHER.genderMatched(1, 0));
assertTrue(CpRelationshipType.BROTHER.genderMatched(0, 1));
assertTrue(CpRelationshipType.BROTHER.genderMatched(0, 0));
assertTrue(CpRelationshipType.SISTERS.genderMatched(1, 1));
assertTrue(CpRelationshipType.SISTERS.genderMatched(1, 0));
assertTrue(CpRelationshipType.SISTERS.genderMatched(0, 1));
assertTrue(CpRelationshipType.SISTERS.genderMatched(0, 0));
}
}

View File

@ -37,6 +37,7 @@ class UserRegionGatewayImplTest {
private static final String SYS_ORIGIN = "LIKEI"; private static final String SYS_ORIGIN = "LIKEI";
private static final String TEAM_REGION_ID = "middle-east-region"; private static final String TEAM_REGION_ID = "middle-east-region";
private static final String TEAM_REGION_CODE = "中东区"; private static final String TEAM_REGION_CODE = "中东区";
private static final String SEA_REGION_ID = "2051777551323426818";
private static final String SOUTH_ASIA_REGION_CODE = "南亚"; private static final String SOUTH_ASIA_REGION_CODE = "南亚";
private static final String PAK_ALEX_REGION_CODE = "Pakistan - rolex"; private static final String PAK_ALEX_REGION_CODE = "Pakistan - rolex";
@ -158,6 +159,57 @@ class UserRegionGatewayImplTest {
verify(countryCodeAliasSupport, never()).containsCode("PK,IN,BD,NP", "P2"); verify(countryCodeAliasSupport, never()).containsCode("PK,IN,BD,NP", "P2");
} }
@Test
void checkOpenDailyAutoSalary_shouldKeepFirstWhenCacheHasDuplicateRegionIds() {
when(sysRegionCacheService.getCache(SYS_ORIGIN)).thenReturn(List.of(
new SysRegionConfig()
.setId(SEA_REGION_ID)
.setRegionName("东南亚区")
.setMetadata(Map.of("openDailyAutoSalary", "true")),
new SysRegionConfig()
.setId(SEA_REGION_ID)
.setRegionName("东南亚区")
.setMetadata(Map.of("openDailyAutoSalary", "false"))
));
Boolean result = userRegionGateway.checkOpenDailyAutoSalary(SEA_REGION_ID, SYS_ORIGIN);
assertEquals(Boolean.TRUE, result);
}
@Test
void mapUserRegionConfig_shouldKeepFirstWhenTeamRegionConfigHasDuplicateIds() {
SysRegionConfig firstRegion = new SysRegionConfig()
.setId(SEA_REGION_ID)
.setRegionCode("SEA")
.setRegionName("东南亚区");
when(teamMemberService.listByMemberIds(Set.of(TEAM_USER_ID))).thenReturn(List.of(
new TeamMember()
.setMemberId(TEAM_USER_ID)
.setTeamId(TEAM_ID)
.setSysOrigin(SYS_ORIGIN)
.setRole(TeamMemberRole.OWN)
));
when(teamProfileService.listByIds(Set.of(TEAM_ID))).thenReturn(List.of(
new TeamProfile().setId(TEAM_ID).setRegion(SEA_REGION_ID)
));
when(sysRegionConfigService.listAvailable(Set.of(SEA_REGION_ID))).thenReturn(List.of(
firstRegion,
new SysRegionConfig()
.setId(SEA_REGION_ID)
.setRegionCode("SEA2")
.setRegionName("东南亚区")
));
when(userRegionInfraConvertor.toRegionConfig(firstRegion)).thenReturn(
new RegionConfig().setId(SEA_REGION_ID).setRegionCode("SEA")
);
Map<Long, RegionConfig> result = userRegionGateway.mapUserRegionConfig(
SYS_ORIGIN, Set.of(TEAM_USER_ID));
assertEquals("SEA", result.get(TEAM_USER_ID).getRegionCode());
}
private SysRegionConfig region(String regionCode, String countryCodes, String langeCodes) { private SysRegionConfig region(String regionCode, String countryCodes, String langeCodes) {
return new SysRegionConfig() return new SysRegionConfig()
.setId(regionCode) .setId(regionCode)

View File

@ -13,11 +13,15 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd; import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd; import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd; import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
@ -167,6 +171,18 @@ public class TeamProfileClientEndpoint implements TeamProfileClientApi {
return ResultResponse.success(teamProfileClientService.listByCondition(cmd)); return ResultResponse.success(teamProfileClientService.listByCondition(cmd));
} }
@Override
public ResultResponse<PageResult<TeamManualSalaryPaymentDTO>> manualSalaryPage(
TeamManualSalaryPaymentQryCmd cmd) {
return ResultResponse.success(teamProfileClientService.manualSalaryPage(cmd));
}
@Override
public ResultResponse<List<TeamManualSalaryPaymentResultDTO>> manualSalaryPay(
TeamManualSalaryPaymentCmd cmd) {
return ResultResponse.success(teamProfileClientService.manualSalaryPay(cmd));
}
@Override @Override
public ResultResponse<Set<Long>> listIdByRegion(String regionId) { public ResultResponse<Set<Long>> listIdByRegion(String regionId) {
return ResultResponse.success(teamProfileClientService.listIdByRegion(regionId)); return ResultResponse.success(teamProfileClientService.listIdByRegion(regionId));

View File

@ -9,6 +9,8 @@ import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig; import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService; import com.red.circle.other.infra.database.rds.service.gift.GiftConfigService;
import com.red.circle.other.infra.database.rds.service.gift.GiftWallService; import com.red.circle.other.infra.database.rds.service.gift.GiftWallService;
import com.red.circle.other.infra.enums.user.user.CpRelationshipType;
import com.red.circle.other.inner.enums.material.GiftTabEnum;
import com.red.circle.other.inner.model.cmd.sys.GiftQryCmd; import com.red.circle.other.inner.model.cmd.sys.GiftQryCmd;
import com.red.circle.other.inner.model.dto.gift.UserGiftWallDTO; import com.red.circle.other.inner.model.dto.gift.UserGiftWallDTO;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO; import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
@ -67,7 +69,8 @@ public class GiftConfigClientServiceImpl implements GiftConfigClientService {
@Override @Override
public Boolean updateGift(GiftConfigDTO giftConfigDTO) { public Boolean updateGift(GiftConfigDTO giftConfigDTO) {
return giftConfigService.updateGift(giftInnerConvertor.toGiftConfig(giftConfigDTO)); return giftConfigService.updateGift(
normalizeCpRelationType(giftInnerConvertor.toGiftConfig(giftConfigDTO)));
} }
@Override @Override
@ -82,7 +85,17 @@ public class GiftConfigClientServiceImpl implements GiftConfigClientService {
@Override @Override
public Boolean saveGift(GiftConfigDTO giftConfig) { public Boolean saveGift(GiftConfigDTO giftConfig) {
return giftConfigService.save(giftInnerConvertor.toGiftConfig(giftConfig)); return giftConfigService.save(normalizeCpRelationType(giftInnerConvertor.toGiftConfig(giftConfig)));
}
private GiftConfig normalizeCpRelationType(GiftConfig giftConfig) {
if (Objects.isNull(giftConfig)) {
return null;
}
if (!Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.CP.name())) {
return giftConfig.setCpRelationType(null);
}
return giftConfig.setCpRelationType(CpRelationshipType.normalize(giftConfig.getCpRelationType()));
} }
@Override @Override

View File

@ -0,0 +1,24 @@
package com.red.circle.other.app.inner.service.team;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import java.util.List;
/**
* 后台手动发放主播工资.
*/
public interface TeamManualSalaryPaymentClientService {
/**
* 预览当前账期可发放工资.
*/
PageResult<TeamManualSalaryPaymentDTO> page(TeamManualSalaryPaymentQryCmd cmd);
/**
* 发放当前筛选国家的工资.
*/
List<TeamManualSalaryPaymentResultDTO> pay(TeamManualSalaryPaymentCmd cmd);
}

View File

@ -8,6 +8,8 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessChangeCmd
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd; import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd; import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd; import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
@ -15,6 +17,8 @@ import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContact; import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
@ -87,6 +91,10 @@ public interface TeamProfileClientService {
PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd); PageResult<TeamSalaryPaymentDTO> listByCondition(TeamSalaryBackQryCmd cmd);
PageResult<TeamManualSalaryPaymentDTO> manualSalaryPage(TeamManualSalaryPaymentQryCmd cmd);
List<TeamManualSalaryPaymentResultDTO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd);
Set<Long> listIdByRegion(String regionId); Set<Long> listIdByRegion(String regionId);
List<TeamProfileDTO> listRegionAll(String region); List<TeamProfileDTO> listRegionAll(String region);

View File

@ -0,0 +1,903 @@
package com.red.circle.other.app.inner.service.team.impl;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.inner.service.team.TeamManualSalaryPaymentClientService;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy;
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
import com.red.circle.wallet.inner.endpoint.bank.UserBankRunningWaterClient;
import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd;
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
import com.red.circle.wallet.inner.model.dto.UserBankRunningWaterDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 后台手动发放主播工资.
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TeamManualSalaryPaymentClientServiceImpl implements
TeamManualSalaryPaymentClientService {
private static final String MANUAL_SALARY_LOCK_PREFIX = "TeamManualSalaryPayment:";
private static final int LOCK_SECONDS = 300;
private static final String SORT_FIELD_CURRENT_SALARY_BALANCE = "currentSalaryBalance";
private static final String SORT_FIELD_PAYABLE_SALARY = "payableSalary";
private static final String SORT_FIELD_TOTAL_ISSUED_SALARY = "totalIssuedSalary";
private static final String SORT_ORDER_ASCEND = "ascend";
private static final int LEGACY_HALF_MONTH_COMPAT_BILL_BELONG = 202605;
private final RedisService redisService;
private final TeamMemberService teamMemberService;
private final TeamProfileService teamProfileService;
private final UserBankBalanceClient userBankBalanceClient;
private final TeamMemberTargetService teamMemberTargetService;
private final TeamSalaryPaymentService teamSalaryPaymentService;
private final TeamPolicyManagerService teamPolicyManagerService;
private final UserBankRunningWaterClient userBankRunningWaterClient;
@Override
public PageResult<TeamManualSalaryPaymentDTO> page(TeamManualSalaryPaymentQryCmd cmd) {
validatePage(cmd);
List<ManualSalaryRow> rows = buildRows(cmd.getSysOrigin(), cmd.getCountryCode(),
singletonUserId(cmd.getUserId()), TeamBillCycleUtils.getCalcBillBelong());
rows = sortRows(rows, cmd.getSortField(), cmd.getSortOrder());
int cursor = 1;
int limit = 20;
if (Objects.nonNull(cmd.getPageQuery())) {
cursor = Math.max(1, Objects.isNull(cmd.getPageQuery().getCursor()) ? 1
: cmd.getPageQuery().getCursor());
limit = Math.max(1, Objects.isNull(cmd.getPageQuery().getLimit()) ? 20
: cmd.getPageQuery().getLimit());
}
int fromIndex = Math.min((cursor - 1) * limit, rows.size());
int toIndex = Math.min(fromIndex + limit, rows.size());
PageResult<TeamManualSalaryPaymentDTO> pageResult = new PageResult<>();
pageResult.setCurrent(cursor);
pageResult.setSize(limit);
pageResult.setTotal((long) rows.size());
pageResult.setRecords(rows.subList(fromIndex, toIndex).stream()
.map(ManualSalaryRow::getDto)
.collect(Collectors.toList()));
return pageResult;
}
@Override
public List<TeamManualSalaryPaymentResultDTO> pay(TeamManualSalaryPaymentCmd cmd) {
validatePay(cmd);
Integer billBelong = TeamBillCycleUtils.getCalcBillBelong();
String lockKey = MANUAL_SALARY_LOCK_PREFIX + cmd.getSysOrigin() + ":" + cmd.getCountryCode()
+ ":" + billBelong;
ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT,
redisService.lock(lockKey, LOCK_SECONDS));
try {
List<ManualSalaryRow> rows = buildRows(cmd.getSysOrigin(), cmd.getCountryCode(),
normalizeUserIds(cmd.getUserIds()), billBelong);
List<TeamManualSalaryPaymentResultDTO> results = new ArrayList<>();
for (ManualSalaryRow row : rows) {
if (CollectionUtils.isEmpty(row.getPayments())) {
continue;
}
TeamSalaryPayment payment = teamSalaryPaymentService.createIfAbsent(row.getUserId(),
billBelong, row.getSysOrigin());
for (PolicyPayment policyPayment : row.getPayments()) {
if (Boolean.TRUE.equals(teamSalaryPaymentService.checkRepeatPayment(payment.getId(),
row.getUserId(), row.getRegion(), policyPayment.getLevel(),
policyPayment.getAnchor()))) {
continue;
}
Long receiverUserId = policyPayment.isAnchor() ? row.getUserId() : row.getTeamOwnId();
if (Objects.isNull(receiverUserId)) {
continue;
}
BigDecimal issueSalary = policyPayment.getSalary().compareTo(BigDecimal.ZERO) > 0
? policyPayment.getSalary()
: BigDecimal.ZERO.setScale(2, RoundingMode.DOWN);
BigDecimal beforeBalance = getAccurateBalance(receiverUserId);
String detailsId = IdWorkerUtils.getIdStr();
if (issueSalary.compareTo(BigDecimal.ZERO) > 0) {
Boolean incrSuccess = ResponseAssert.requiredSuccess(userBankBalanceClient.incr(new UserBankBalanceIncrCmd()
.setUserId(receiverUserId)
.setSysOrigin(row.getSysOrigin())
.setAmount(PennyAmount.ofDollar(issueSalary))));
ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT, Boolean.TRUE.equals(incrSuccess));
}
BigDecimal afterBalance = getAccurateBalance(receiverUserId);
String salaryRoleName = policyPayment.isAnchor() ? "主播" : "代理";
if (issueSalary.compareTo(BigDecimal.ZERO) > 0) {
addRunningWater(detailsId, receiverUserId, row.getSysOrigin(),
issueSalary, afterBalance, cmd.getOperationUserId(), salaryRoleName);
}
String settlementRemark = issueSalary.compareTo(BigDecimal.ZERO) > 0
? "后台手动发放" + salaryRoleName + "工资"
: "后台手动结算" + salaryRoleName + "工资实收已大于等于应发本次发放0";
teamSalaryPaymentService.paymentSalary(payment.getId(), new TeamSalaryPaymentDetails()
.setId(detailsId)
.setSysOrigin(row.getSysOrigin())
.setTeamId(row.getTeamId())
.setMemberTargetId(row.getMemberTargetId())
.setTeamOwnId(row.getTeamOwnId())
.setTeamMemberId(row.getUserId())
.setTeamBillId(Long.valueOf(billBelong))
.setTeamRegionId(row.getRegion())
.setRemarks(List.of(new TeamSalaryPaymentRemark()
.setRemark(settlementRemark)
.setCreateTime(TimestampUtils.now())))
.setAnchor(policyPayment.getAnchor())
.setHostSalaryToAgent(Boolean.FALSE)
.setTarget(row.getCurrentTarget())
.setSalary(issueSalary)
.setPolicyLevel(policyPayment.getLevel())
.setPolicy(policyPayment.getPolicy()));
updatePaymentPolicyMap(payment.getId(), row.getRegion(), row.getPolicyManager());
results.add(new TeamManualSalaryPaymentResultDTO()
.setId(detailsId)
.setSysOrigin(row.getSysOrigin())
.setCountryCode(row.getCountryCode())
.setRegion(row.getRegion())
.setTeamId(row.getTeamId())
.setTeamOwnId(row.getTeamOwnId())
.setUserId(receiverUserId)
.setBillBelong(billBelong)
.setBeforeSalaryBalance(beforeBalance)
.setAfterSalaryBalance(afterBalance)
.setIssuedSalary(issueSalary)
.setCurrentTarget(row.getCurrentTarget())
.setPolicyLevel(policyPayment.getLevel()));
}
}
return results;
} finally {
redisService.unlock(lockKey);
}
}
private List<ManualSalaryRow> buildRows(String sysOrigin, String countryCode, Set<Long> userIds,
Integer billBelong) {
List<TeamMemberTarget> targets = teamMemberTargetService.listManualSalaryTargets(sysOrigin,
countryCode, userIds, billBelong);
if (CollectionUtils.isEmpty(targets)) {
return List.of();
}
targets = targets.stream()
.filter(target -> CollectionUtils.isNotEmpty(target.getDailyTargets()))
.filter(target -> target.sumAcceptGiftValue() > 0)
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(targets)) {
return List.of();
}
Set<Long> memberIds = targets.stream()
.map(TeamMemberTarget::getUserId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Set<Long> teamIds = targets.stream()
.map(TeamMemberTarget::getTeamId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Map<Long, TeamMember> memberMap = teamMemberService.mapByMemberIds(memberIds);
Map<Long, TeamProfile> teamProfileMap = teamProfileService.mapProfileByIds(teamIds);
Set<Integer> salaryPaymentBillBelongs = getMergedSalaryPaymentBillBelongs(billBelong);
List<TeamSalaryPayment> salaryPayments = teamSalaryPaymentService
.listByPaymentOrRecipientUserIdsAndDateNumbers(sysOrigin, memberIds,
salaryPaymentBillBelongs);
Map<String, TeamSalaryPayment> salaryPaymentMap = toSalaryPaymentMap(salaryPayments);
Map<String, TeamPolicyManager> policyCache = CollectionUtils.newHashMap();
return targets.stream()
.map(target -> buildRow(target, memberMap, teamProfileMap, policyCache, billBelong,
countryCode, salaryPaymentMap))
.filter(Objects::nonNull)
.sorted(Comparator.comparing(ManualSalaryRow::getCurrentTarget).reversed()
.thenComparing(ManualSalaryRow::getUserId))
.collect(Collectors.toList());
}
private Map<String, TeamSalaryPayment> toSalaryPaymentMap(List<TeamSalaryPayment> payments) {
if (CollectionUtils.isEmpty(payments)) {
return CollectionUtils.newHashMap();
}
return payments.stream()
.filter(Objects::nonNull)
.filter(payment -> StringUtils.isNotBlank(payment.getId()))
.collect(Collectors.toMap(TeamSalaryPayment::getId, payment -> payment,
(first, second) -> first));
}
private List<ManualSalaryRow> sortRows(List<ManualSalaryRow> rows, String sortField,
String sortOrder) {
if (CollectionUtils.isEmpty(rows) || StringUtils.isBlank(sortField)
|| StringUtils.isBlank(sortOrder)) {
return rows;
}
Comparator<ManualSalaryRow> comparator = getSalarySortComparator(sortField);
if (Objects.isNull(comparator)) {
return rows;
}
if (!SORT_ORDER_ASCEND.equalsIgnoreCase(sortOrder)) {
comparator = comparator.reversed();
}
return rows.stream()
.sorted(comparator.thenComparing(ManualSalaryRow::getUserId))
.collect(Collectors.toList());
}
private Comparator<ManualSalaryRow> getSalarySortComparator(String sortField) {
if (SORT_FIELD_PAYABLE_SALARY.equals(sortField)) {
return Comparator.comparing(row -> decimalValue(row.getPayableSalary()));
}
if (SORT_FIELD_TOTAL_ISSUED_SALARY.equals(sortField)) {
return Comparator.comparing(row -> decimalValue(row.getTotalIssuedSalary()));
}
if (SORT_FIELD_CURRENT_SALARY_BALANCE.equals(sortField)) {
return Comparator.comparing(row -> decimalValue(row.getCurrentSalaryBalance()));
}
return null;
}
private BigDecimal decimalValue(BigDecimal value) {
return Objects.isNull(value) ? BigDecimal.ZERO : value;
}
private Set<Long> singletonUserId(Long userId) {
if (Objects.isNull(userId)) {
return Set.of();
}
return Set.of(userId);
}
private Set<Long> normalizeUserIds(List<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return Set.of();
}
return userIds.stream()
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
private Set<Integer> getMergedSalaryPaymentBillBelongs(Integer billBelong) {
if (Objects.isNull(billBelong)) {
return Set.of();
}
Set<Integer> billBelongs = new HashSet<>();
billBelongs.add(billBelong);
Integer month = TeamBillCycleUtils.extractMonth(billBelong);
if (Objects.equals(month, LEGACY_HALF_MONTH_COMPAT_BILL_BELONG)) {
billBelongs.add(month);
billBelongs.add(month * 100 + 1);
billBelongs.add(month * 100 + 16);
}
return billBelongs;
}
private List<TeamSalaryPayment> listCyclePayments(
Map<String, TeamSalaryPayment> salaryPaymentMap, Long userId, Integer billBelong) {
if (Objects.isNull(userId) || CollectionUtils.isEmpty(salaryPaymentMap)) {
return List.of();
}
return getMergedSalaryPaymentBillBelongs(billBelong).stream()
.map(item -> salaryPaymentMap.get(paymentId(userId, item)))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private ManualSalaryRow buildRow(TeamMemberTarget target, Map<Long, TeamMember> memberMap,
Map<Long, TeamProfile> teamProfileMap, Map<String, TeamPolicyManager> policyCache,
Integer billBelong, String requestedCountryCode,
Map<String, TeamSalaryPayment> salaryPaymentMap) {
TeamMember member = memberMap.get(target.getUserId());
TeamProfile teamProfile = teamProfileMap.get(target.getTeamId());
if (Objects.isNull(member) || Objects.isNull(teamProfile)
|| !Objects.equals(member.getTeamId(), target.getTeamId())
|| TeamStatus.CLOSE.equals(teamProfile.getStatus())) {
return null;
}
String region = StringUtils.isNotBlank(target.getRegion()) ? target.getRegion()
: teamProfile.getRegion();
String countryCode = StringUtils.isNotBlank(target.getCountryCode()) ? target.getCountryCode()
: Optional.ofNullable(teamProfile.getCountry()).map(c -> c.getCountryCode()).orElse(null);
if (StringUtils.isBlank(countryCode)) {
countryCode = requestedCountryCode;
}
TeamPolicyManager policyManager = getPolicyManager(target.getSysOrigin(), region, countryCode,
policyCache);
TeamBillMemberTarget billTarget = calculateBillTarget(target, policyManager);
List<TeamSalaryPayment> cyclePayments = listCyclePayments(salaryPaymentMap,
target.getUserId(), billBelong);
PaymentSummary receivedPaymentSummary = summarizeReceivedPayments(
new ArrayList<>(salaryPaymentMap.values()), target.getUserId());
BigDecimal expectedMemberSalary = salaryValue(billTarget.getMemberSalary());
BigDecimal expectedAgentSalary = salaryValue(billTarget.getOwnSalary());
BigDecimal expectedSalary = expectedTotalSalary(billTarget);
BigDecimal payableSalary = expectedSalary.subtract(receivedPaymentSummary.getTotalIssuedSalary())
.setScale(2, RoundingMode.DOWN);
BigDecimal payableMemberSalary = expectedMemberSalary
.subtract(receivedPaymentSummary.getMemberIssuedSalary())
.setScale(2, RoundingMode.DOWN);
BigDecimal payableAgentSalary = expectedAgentSalary
.subtract(receivedPaymentSummary.getAgentIssuedSalary())
.setScale(2, RoundingMode.DOWN);
boolean currentLevelSettled = areCurrentRolesSettled(cyclePayments, target.getUserId(), region,
billTarget, payableSalary);
List<PolicyPayment> policyPayments = buildSettlementPayments(policyManager, billTarget,
cyclePayments, target.getUserId(), region, receivedPaymentSummary, payableSalary,
currentLevelSettled);
TeamManualSalaryPaymentDTO dto = new TeamManualSalaryPaymentDTO()
.setId(target.getId())
.setSysOrigin(target.getSysOrigin())
.setCountryCode(countryCode)
.setRegion(region)
.setTeamId(target.getTeamId())
.setTeamOwnId(teamProfile.getOwnUserId())
.setUserId(target.getUserId())
.setBillBelong(billBelong)
.setLastSettlementTarget(getLastSettlementTarget(cyclePayments))
.setCurrentTarget(target.sumAcceptGiftValue())
.setPolicyLevel(billTarget.getLevel())
.setSatisfyLevel(billTarget.getSatisfyLevel())
.setExpectedSalary(expectedSalary)
.setExpectedMemberSalary(expectedMemberSalary)
.setExpectedAgentSalary(expectedAgentSalary)
.setPayableSalary(payableSalary)
.setPayableMemberSalary(payableMemberSalary)
.setPayableAgentSalary(payableAgentSalary)
.setTotalIssuedSalary(receivedPaymentSummary.getTotalIssuedSalary())
.setMemberIssuedSalary(receivedPaymentSummary.getMemberIssuedSalary())
.setAgentIssuedSalary(receivedPaymentSummary.getAgentIssuedSalary())
.setOverIssuedSalary(receivedPaymentSummary.getOverIssuedSalary())
.setMemberOverIssuedSalary(receivedPaymentSummary.getMemberOverIssuedSalary())
.setAgentOverIssuedSalary(receivedPaymentSummary.getAgentOverIssuedSalary())
.setCurrentSalaryBalance(getAccurateBalance(target.getUserId()))
.setPaid(currentLevelSettled);
return new ManualSalaryRow(dto, target, teamProfile, policyManager, billTarget, policyPayments);
}
private TeamPolicyManager getPolicyManager(String sysOrigin, String region, String countryCode,
Map<String, TeamPolicyManager> policyCache) {
if (StringUtils.isBlank(sysOrigin) || StringUtils.isBlank(region)) {
return null;
}
String key = sysOrigin + ":" + region + ":" + countryCode;
if (policyCache.containsKey(key)) {
return policyCache.get(key);
}
TeamPolicyManager policyManager = teamPolicyManagerService.getReleaseByRegionAndType(sysOrigin,
region, null, countryCode);
if (Objects.isNull(policyManager)) {
policyManager = teamPolicyManagerService.listReleaseTeamPolicy(sysOrigin, region).stream()
.filter(this::moneyPolicy)
.filter(policy -> StringUtils.isBlank(policy.getCountryCode()))
.findFirst()
.orElse(null);
}
policyCache.put(key, policyManager);
return policyManager;
}
private boolean moneyPolicy(TeamPolicyManager policyManager) {
return Objects.nonNull(policyManager)
&& (StringUtils.isBlank(policyManager.getPolicyType())
|| Objects.equals(TeamPolicyTypeEnum.MONEY.name(), policyManager.getPolicyType()));
}
private TeamBillMemberTarget calculateBillTarget(TeamMemberTarget target,
TeamPolicyManager policyManager) {
if (Objects.nonNull(policyManager) && CollectionUtils.isNotEmpty(policyManager.getPolicy())) {
return TeamBillCycleUtils.calculatorPolicyTarget(target, policyManager);
}
return new TeamBillMemberTarget()
.setTargetId(target.getTimeId())
.setSysOrigin(target.getSysOrigin())
.setUserId(target.getUserId())
.setAcceptGiftValue(target.sumAcceptGiftValue())
.setGiveGiftValue(target.sumGiveGiftValue())
.setRoomValue(target.sumRoomValue())
.setOnlineTime(target.sumAllTargetTime())
.setMemberSalary(BigDecimal.ZERO)
.setOwnSalary(BigDecimal.ZERO)
.setTotalSalary(BigDecimal.ZERO)
.setSatisfyLevel(Boolean.FALSE);
}
private BigDecimal expectedTotalSalary(TeamBillMemberTarget billTarget) {
if (Objects.isNull(billTarget)) {
return BigDecimal.ZERO.setScale(2, RoundingMode.DOWN);
}
return salaryValue(billTarget.getMemberSalary())
.add(salaryValue(billTarget.getOwnSalary()))
.setScale(2, RoundingMode.DOWN);
}
private BigDecimal salaryValue(BigDecimal value) {
return Optional.ofNullable(value)
.orElse(BigDecimal.ZERO)
.setScale(2, RoundingMode.DOWN);
}
private List<PolicyPayment> buildSettlementPayments(TeamPolicyManager policyManager,
TeamBillMemberTarget billTarget, List<TeamSalaryPayment> cyclePayments, Long userId,
String region, PaymentSummary paymentSummary, BigDecimal payableSalary,
boolean currentLevelSettled) {
if (currentLevelSettled || Objects.isNull(billTarget)
|| Objects.isNull(billTarget.getLevel())) {
return List.of();
}
TeamPolicy policy = findPolicy(policyManager, billTarget.getLevel());
BigDecimal remainingPayable = payableSalary.compareTo(BigDecimal.ZERO) > 0
? payableSalary.setScale(2, RoundingMode.DOWN)
: BigDecimal.ZERO.setScale(2, RoundingMode.DOWN);
BigDecimal memberExpected = salaryValue(billTarget.getMemberSalary());
BigDecimal agentExpected = salaryValue(billTarget.getOwnSalary());
BigDecimal memberMissing = memberExpected.subtract(paymentSummary.getMemberIssuedSalary())
.setScale(2, RoundingMode.DOWN);
BigDecimal agentMissing = agentExpected.subtract(paymentSummary.getAgentIssuedSalary())
.setScale(2, RoundingMode.DOWN);
boolean memberSettled = isRoleSettled(cyclePayments, userId, region, billTarget.getLevel(),
Boolean.TRUE, memberExpected);
boolean agentSettled = isRoleSettled(cyclePayments, userId, region, billTarget.getLevel(),
Boolean.FALSE, agentExpected);
List<PolicyPayment> payments = new ArrayList<>();
if (remainingPayable.compareTo(BigDecimal.ZERO) <= 0) {
if (!memberSettled && memberExpected.compareTo(BigDecimal.ZERO) > 0) {
payments.add(new PolicyPayment(policy, billTarget.getLevel(),
BigDecimal.ZERO.setScale(2, RoundingMode.DOWN), Boolean.TRUE));
}
if (!agentSettled && agentExpected.compareTo(BigDecimal.ZERO) > 0) {
payments.add(new PolicyPayment(policy, billTarget.getLevel(),
BigDecimal.ZERO.setScale(2, RoundingMode.DOWN), Boolean.FALSE));
}
return payments;
}
if (!memberSettled && memberMissing.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal issueSalary = memberMissing.min(remainingPayable).setScale(2, RoundingMode.DOWN);
if (issueSalary.compareTo(BigDecimal.ZERO) > 0) {
payments.add(new PolicyPayment(policy, billTarget.getLevel(), issueSalary, Boolean.TRUE));
remainingPayable = remainingPayable.subtract(issueSalary).setScale(2, RoundingMode.DOWN);
}
}
if (!agentSettled && agentMissing.compareTo(BigDecimal.ZERO) > 0
&& remainingPayable.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal issueSalary = agentMissing.min(remainingPayable).setScale(2, RoundingMode.DOWN);
if (issueSalary.compareTo(BigDecimal.ZERO) > 0) {
payments.add(new PolicyPayment(policy, billTarget.getLevel(), issueSalary, Boolean.FALSE));
}
}
return payments;
}
private TeamPolicy findPolicy(TeamPolicyManager policyManager, Integer level) {
if (Objects.isNull(policyManager) || CollectionUtils.isEmpty(policyManager.getPolicy())
|| Objects.isNull(level)) {
return null;
}
return policyManager.getPolicy().stream()
.filter(policy -> Objects.equals(policy.getLevel(), level))
.findFirst()
.orElse(null);
}
private boolean areCurrentRolesSettled(List<TeamSalaryPayment> cyclePayments, Long userId,
String region, TeamBillMemberTarget billTarget, BigDecimal payableSalary) {
if (Objects.isNull(billTarget) || Objects.isNull(billTarget.getLevel())
|| payableSalary.compareTo(BigDecimal.ZERO) > 0) {
return false;
}
return isRoleSettled(cyclePayments, userId, region, billTarget.getLevel(), Boolean.TRUE,
salaryValue(billTarget.getMemberSalary()))
&& isRoleSettled(cyclePayments, userId, region, billTarget.getLevel(), Boolean.FALSE,
salaryValue(billTarget.getOwnSalary()));
}
private boolean isRoleSettled(List<TeamSalaryPayment> cyclePayments, Long userId, String region,
Integer policyLevel, Boolean anchor, BigDecimal expectedSalary) {
return expectedSalary.compareTo(BigDecimal.ZERO) <= 0
|| hasPaidPolicy(cyclePayments, userId, region, policyLevel, anchor);
}
private boolean hasPaidPolicy(List<TeamSalaryPayment> cyclePayments, Long userId, String region,
Integer policyLevel, Boolean anchor) {
return listValidSalaryDetails(cyclePayments).stream()
.anyMatch(details -> Objects.equals(details.getTeamMemberId(), userId)
&& Objects.equals(details.getTeamRegionId(), region)
&& Objects.equals(details.getPolicyLevel(), policyLevel)
&& Objects.equals(details.getAnchor(), anchor));
}
private Long getLastSettlementTarget(List<TeamSalaryPayment> cyclePayments) {
return listValidSalaryDetails(cyclePayments).stream()
.max(Comparator.comparing(details -> Optional.ofNullable(details.getCreateTime())
.orElse(new Timestamp(0))))
.map(TeamSalaryPaymentDetails::getTarget)
.orElse(0L);
}
private PaymentSummary summarizePayments(List<TeamSalaryPayment> cyclePayments) {
return summarizeDetails(listValidSalaryDetails(cyclePayments));
}
private PaymentSummary summarizeReceivedPayments(List<TeamSalaryPayment> cyclePayments,
Long receiverUserId) {
if (Objects.isNull(receiverUserId)) {
return emptyPaymentSummary();
}
List<TeamSalaryPaymentDetails> details = listValidSalaryDetails(cyclePayments).stream()
.filter(detail -> Objects.equals(receiverUserId, getSalaryReceiverUserId(detail)))
.collect(Collectors.toList());
return summarizeDetails(details);
}
private PaymentSummary summarizeDetails(List<TeamSalaryPaymentDetails> details) {
BigDecimal memberIssuedSalary = sumDetailsSalary(details, Boolean.TRUE);
BigDecimal agentIssuedSalary = sumDetailsSalary(details, Boolean.FALSE);
BigDecimal totalIssuedSalary = memberIssuedSalary.add(agentIssuedSalary)
.setScale(2, RoundingMode.DOWN);
BigDecimal memberOverIssuedSalary = getOverIssuedSalary(details, Boolean.TRUE);
BigDecimal agentOverIssuedSalary = getOverIssuedSalary(details, Boolean.FALSE);
BigDecimal overIssuedSalary = memberOverIssuedSalary.add(agentOverIssuedSalary)
.setScale(2, RoundingMode.DOWN);
return new PaymentSummary(totalIssuedSalary, memberIssuedSalary, agentIssuedSalary,
overIssuedSalary, memberOverIssuedSalary, agentOverIssuedSalary);
}
private PaymentSummary emptyPaymentSummary() {
BigDecimal zero = BigDecimal.ZERO.setScale(2, RoundingMode.DOWN);
return new PaymentSummary(zero, zero, zero, zero, zero, zero);
}
private Long getSalaryReceiverUserId(TeamSalaryPaymentDetails details) {
if (Objects.isNull(details)) {
return null;
}
if (Boolean.TRUE.equals(details.getAnchor())
&& !Boolean.TRUE.equals(details.getHostSalaryToAgent())) {
return details.getTeamMemberId();
}
return details.getTeamOwnId();
}
private BigDecimal sumDetailsSalary(List<TeamSalaryPaymentDetails> details, Boolean anchor) {
return details.stream()
.filter(detail -> Objects.equals(detail.getAnchor(), anchor))
.map(detail -> Optional.ofNullable(detail.getSalary()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(2, RoundingMode.DOWN);
}
private BigDecimal getOverIssuedSalary(List<TeamSalaryPaymentDetails> details) {
return getOverIssuedSalary(details, null);
}
private BigDecimal getOverIssuedSalary(List<TeamSalaryPaymentDetails> details, Boolean anchor) {
if (CollectionUtils.isEmpty(details)) {
return BigDecimal.ZERO.setScale(2, RoundingMode.DOWN);
}
Set<String> issuedKeys = new HashSet<>();
return details.stream()
.filter(detail -> Objects.isNull(anchor) || Objects.equals(detail.getAnchor(), anchor))
.sorted(Comparator.comparing(detail -> Optional.ofNullable(detail.getCreateTime())
.orElse(new Timestamp(0))))
.filter(detail -> !issuedKeys.add(paymentDetailsPolicyKey(detail)))
.map(detail -> Optional.ofNullable(detail.getSalary()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add)
.setScale(2, RoundingMode.DOWN);
}
private String paymentDetailsPolicyKey(TeamSalaryPaymentDetails details) {
if (Objects.isNull(details)
|| Objects.isNull(details.getTeamMemberId())
|| StringUtils.isBlank(details.getTeamRegionId())
|| Objects.isNull(details.getPolicyLevel())
|| Objects.isNull(details.getAnchor())) {
return Optional.ofNullable(details).map(TeamSalaryPaymentDetails::getId)
.orElse(String.valueOf(System.identityHashCode(details)));
}
return details.getTeamMemberId() + ":" + details.getTeamRegionId() + ":"
+ details.getPolicyLevel() + ":" + details.getAnchor();
}
private List<TeamSalaryPaymentDetails> listValidSalaryDetails(
List<TeamSalaryPayment> cyclePayments) {
if (CollectionUtils.isEmpty(cyclePayments)) {
return List.of();
}
return cyclePayments.stream()
.filter(Objects::nonNull)
.flatMap(payment -> Optional.ofNullable(payment.getSalarySendDetails())
.stream()
.flatMap(List::stream))
.filter(this::validSalaryDetails)
.collect(Collectors.toList());
}
private boolean validSalaryDetails(TeamSalaryPaymentDetails details) {
return Objects.nonNull(details)
&& Objects.nonNull(details.getAnchor())
&& !Boolean.TRUE.equals(details.getRecycled());
}
private BigDecimal getAccurateBalance(Long userId) {
BankBalanceDTO balance = ResponseAssert.requiredSuccess(userBankBalanceClient.getBalance(userId));
return Optional.ofNullable(balance)
.map(BankBalanceDTO::getAccurateBalance)
.orElse(BigDecimal.ZERO)
.setScale(2, RoundingMode.DOWN);
}
private void addRunningWater(String trackId, Long userId, String sysOrigin, BigDecimal salary,
BigDecimal afterBalance, Long operationUserId, String salaryRoleName) {
try {
userBankRunningWaterClient.add(new UserBankRunningWaterDTO()
.setId(IdWorkerUtils.getId())
.setUserId(userId)
.setSysOrigin(sysOrigin)
.setType(ReceiptType.INCOME.getType())
.setAmount(salary)
.setEvent(UserBankWaterEvent.SYSTEM_SETTLEMENT_WAGES.name())
.setEventDescribe(UserBankWaterEvent.SYSTEM_SETTLEMENT_WAGES.getDescribe())
.setTrackId(trackId)
.setRemark("后台手动发放" + salaryRoleName + "工资")
.setBalance(afterBalance)
.setCreateTime(TimestampUtils.now())
.setCreateUser(Optional.ofNullable(operationUserId).orElse(0L))
.setCreateUserOrigin(1));
} catch (Exception e) {
log.error("[后台手动发放{}工资] 插入用户银行流水失败, userId:{}, trackId:{}",
salaryRoleName, userId, trackId, e);
}
}
private void updatePaymentPolicyMap(String paymentId, String region,
TeamPolicyManager policyManager) {
if (StringUtils.isBlank(paymentId) || StringUtils.isBlank(region)
|| Objects.isNull(policyManager)) {
return;
}
Map<String, TeamPolicyManager> policyMap = teamSalaryPaymentService.mapPolicy(paymentId);
policyMap.put(region, policyManager);
teamSalaryPaymentService.updatePolicyMap(paymentId, policyMap);
}
private void validatePage(TeamManualSalaryPaymentQryCmd cmd) {
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd);
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getSysOrigin());
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getCountryCode());
}
private void validatePay(TeamManualSalaryPaymentCmd cmd) {
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd);
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getSysOrigin());
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getCountryCode());
}
private String paymentId(Long userId, Integer billBelong) {
return userId + "_" + billBelong;
}
private static class PaymentSummary {
private final BigDecimal totalIssuedSalary;
private final BigDecimal memberIssuedSalary;
private final BigDecimal agentIssuedSalary;
private final BigDecimal overIssuedSalary;
private final BigDecimal memberOverIssuedSalary;
private final BigDecimal agentOverIssuedSalary;
private PaymentSummary(BigDecimal totalIssuedSalary, BigDecimal memberIssuedSalary,
BigDecimal agentIssuedSalary, BigDecimal overIssuedSalary,
BigDecimal memberOverIssuedSalary, BigDecimal agentOverIssuedSalary) {
this.totalIssuedSalary = totalIssuedSalary;
this.memberIssuedSalary = memberIssuedSalary;
this.agentIssuedSalary = agentIssuedSalary;
this.overIssuedSalary = overIssuedSalary;
this.memberOverIssuedSalary = memberOverIssuedSalary;
this.agentOverIssuedSalary = agentOverIssuedSalary;
}
private BigDecimal getTotalIssuedSalary() {
return totalIssuedSalary;
}
private BigDecimal getMemberIssuedSalary() {
return memberIssuedSalary;
}
private BigDecimal getAgentIssuedSalary() {
return agentIssuedSalary;
}
private BigDecimal getOverIssuedSalary() {
return overIssuedSalary;
}
private BigDecimal getMemberOverIssuedSalary() {
return memberOverIssuedSalary;
}
private BigDecimal getAgentOverIssuedSalary() {
return agentOverIssuedSalary;
}
}
private static class ManualSalaryRow {
private final TeamManualSalaryPaymentDTO dto;
private final TeamMemberTarget target;
private final TeamProfile teamProfile;
private final TeamPolicyManager policyManager;
private final TeamBillMemberTarget billTarget;
private final List<PolicyPayment> payments;
private ManualSalaryRow(TeamManualSalaryPaymentDTO dto, TeamMemberTarget target,
TeamProfile teamProfile, TeamPolicyManager policyManager, TeamBillMemberTarget billTarget,
List<PolicyPayment> payments) {
this.dto = dto;
this.target = target;
this.teamProfile = teamProfile;
this.policyManager = policyManager;
this.billTarget = billTarget;
this.payments = payments;
}
private TeamManualSalaryPaymentDTO getDto() {
return dto;
}
private String getMemberTargetId() {
return target.getId();
}
private String getSysOrigin() {
return target.getSysOrigin();
}
private String getCountryCode() {
return dto.getCountryCode();
}
private String getRegion() {
return dto.getRegion();
}
private Long getTeamId() {
return target.getTeamId();
}
private Long getTeamOwnId() {
return teamProfile.getOwnUserId();
}
private Long getUserId() {
return target.getUserId();
}
private Long getCurrentTarget() {
return dto.getCurrentTarget();
}
private BigDecimal getPayableSalary() {
return dto.getPayableSalary();
}
private BigDecimal getTotalIssuedSalary() {
return dto.getTotalIssuedSalary();
}
private BigDecimal getCurrentSalaryBalance() {
return dto.getCurrentSalaryBalance();
}
private TeamPolicyManager getPolicyManager() {
return policyManager;
}
private List<PolicyPayment> getPayments() {
return payments;
}
}
private static class PolicyPayment {
private final TeamPolicy policy;
private final Integer level;
private final BigDecimal salary;
private final Boolean anchor;
private PolicyPayment(TeamPolicy policy, Integer level, BigDecimal salary, Boolean anchor) {
this.policy = policy;
this.level = level;
this.salary = salary;
this.anchor = anchor;
}
private TeamPolicy getPolicy() {
return policy;
}
private Integer getLevel() {
return level;
}
private BigDecimal getSalary() {
return salary;
}
private Boolean getAnchor() {
return anchor;
}
private boolean isAnchor() {
return Boolean.TRUE.equals(anchor);
}
}
}

View File

@ -15,6 +15,7 @@ import com.red.circle.mq.business.model.event.team.TeamSalaryCountEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService; import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.mq.rocket.business.service.MessageSenderService; import com.red.circle.mq.rocket.business.service.MessageSenderService;
import com.red.circle.mq.rocket.business.streams.TeamSalaryCountSink; import com.red.circle.mq.rocket.business.streams.TeamSalaryCountSink;
import com.red.circle.other.app.inner.service.team.TeamManualSalaryPaymentClientService;
import com.red.circle.other.app.inner.service.team.TeamProfileClientService; import com.red.circle.other.app.inner.service.team.TeamProfileClientService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway; import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
@ -54,6 +55,8 @@ import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd; import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd; import com.red.circle.other.inner.model.cmd.team.TeamChangeOwnerCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd; import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentCmd;
import com.red.circle.other.inner.model.cmd.team.TeamManualSalaryPaymentQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd; import com.red.circle.other.inner.model.cmd.team.TeamProfileCmd;
import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamSalaryBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd; import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
@ -64,6 +67,8 @@ import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContactDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamContactDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter; import com.red.circle.other.inner.model.dto.agency.agency.TeamCounter;
import com.red.circle.other.inner.model.dto.agency.agency.TeamCountry; import com.red.circle.other.inner.model.dto.agency.agency.TeamCountry;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamManualSalaryPaymentResultDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamPolicyManagerDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO; import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
@ -123,6 +128,7 @@ public class TeamProfileClientServiceImpl implements TeamProfileClientService {
private final BillDiamondBalanceDetailsService billDiamondBalanceDetailsService; private final BillDiamondBalanceDetailsService billDiamondBalanceDetailsService;
private final RegisterDeviceGateway registerDeviceGateway; private final RegisterDeviceGateway registerDeviceGateway;
private final UserMqMessageService userMqMessageService; private final UserMqMessageService userMqMessageService;
private final TeamManualSalaryPaymentClientService teamManualSalaryPaymentClientService;
@Override @Override
public List<TeamProfileDTO> listByIds(Set<Long> ids) { public List<TeamProfileDTO> listByIds(Set<Long> ids) {
@ -702,6 +708,18 @@ public class TeamProfileClientServiceImpl implements TeamProfileClientService {
.convert(teamInfraConvertor::toTeamSalaryPaymentDTO); .convert(teamInfraConvertor::toTeamSalaryPaymentDTO);
} }
@Override
public PageResult<TeamManualSalaryPaymentDTO> manualSalaryPage(
TeamManualSalaryPaymentQryCmd cmd) {
return teamManualSalaryPaymentClientService.page(cmd);
}
@Override
public List<TeamManualSalaryPaymentResultDTO> manualSalaryPay(
TeamManualSalaryPaymentCmd cmd) {
return teamManualSalaryPaymentClientService.pay(cmd);
}
@Override @Override
public Set<Long> listIdByRegion(String regionId) { public Set<Long> listIdByRegion(String regionId) {
return teamProfileService.listIdByRegion(regionId); return teamProfileService.listIdByRegion(regionId);

View File

@ -23,6 +23,17 @@
<artifactId>component-redis</artifactId> <artifactId>component-redis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- Apache POI for Excel processing --> <!-- Apache POI for Excel processing -->
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
@ -38,6 +49,11 @@
<build> <build>
<finalName>${parent.artifactId}-${revision}</finalName> <finalName>${parent.artifactId}-${revision}</finalName>
<testResources combine.self="override">
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins> <plugins>
<plugin> <plugin>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>

View File

@ -0,0 +1,475 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.red.circle.OtherServiceApplication;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.other.app.command.user.DismissCpApplyCmdExe;
import com.red.circle.other.app.command.user.ProcessCpApplyCmd;
import com.red.circle.other.app.command.user.SendCpApplyCmdExe;
import com.red.circle.other.app.command.user.query.UserCpPairUserProfileQryExe;
import com.red.circle.other.app.command.user.query.UserCpCabinQueryExe;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO;
import com.red.circle.other.app.dto.clientobject.user.user.UserCpCabinCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpRelationshipQueryCmd;
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
import com.red.circle.other.app.dto.cmd.user.user.UserCpCabinQueryCmd;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftGiveRunningWater;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftValue;
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.entity.user.user.CpBlessRecord;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.inner.enums.material.GiftCurrencyType;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.AopTestUtils;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@IfProfileValue(name = "cp.local.flow", value = "true")
@AutoConfigureMockMvc
@SpringBootTest(classes = OtherServiceApplication.class)
public class CpLocalFlowSmokeTest {
private static final String SYS_ORIGIN = "LIKEI";
private static final ReqSysOrigin REQ_SYS_ORIGIN = ReqSysOrigin.of(SYS_ORIGIN, SYS_ORIGIN);
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private MockMvc mockMvc;
@Autowired
private BaseInfoService baseInfoService;
@Autowired
private UserRegionGateway userRegionGateway;
@Autowired
private SendCpApplyCmdExe sendCpApplyCmdExe;
@Autowired
private ProcessCpApplyCmd processCpApplyCmd;
@Autowired
private DismissCpApplyCmdExe dismissCpApplyCmdExe;
@Autowired
private UserCpPairUserProfileQryExe pairUserProfileQryExe;
@Autowired
private UserCpCabinQueryExe userCpCabinQueryExe;
@Autowired
private CpApplyService cpApplyService;
@Autowired
private CpRelationshipService cpRelationshipService;
@Autowired
private CpValueService cpValueService;
@Autowired
private CpBlessRecordService cpBlessRecordService;
@Autowired
private GiftCacheService giftCacheService;
private final List<Long> createdApplyIds = new ArrayList<>();
private final List<Long> createdCpValIds = new ArrayList<>();
private final List<RelationPair> createdPairs = new ArrayList<>();
@Before
public void ensureSchema() {
executeIgnoreDuplicate(
"ALTER TABLE `user_cp_apply` ADD COLUMN `relation_type` varchar(32) NOT NULL DEFAULT 'CP' "
+ "COMMENT '关系类型CP/BROTHER/SISTERS' AFTER `accept_apply_user_id`");
executeIgnoreDuplicate(
"ALTER TABLE `user_cp_relationship` ADD COLUMN `relation_type` varchar(32) NOT NULL DEFAULT 'CP' "
+ "COMMENT '关系类型CP/BROTHER/SISTERS' AFTER `cp_user_id`");
jdbcTemplate.update("UPDATE `user_cp_apply` SET `relation_type` = 'CP' "
+ "WHERE `relation_type` IS NULL OR `relation_type` = ''");
jdbcTemplate.update("UPDATE `user_cp_relationship` SET `relation_type` = 'CP' "
+ "WHERE `relation_type` IS NULL OR `relation_type` = ''");
}
@After
public void cleanup() {
for (RelationPair pair : createdPairs) {
cpRelationshipService.remove(new LambdaQueryWrapper<CpRelationship>()
.eq(CpRelationship::getRelationType, pair.relationType)
.and(where -> where
.nested(w -> w.eq(CpRelationship::getUserId, pair.userId)
.eq(CpRelationship::getCpUserId, pair.cpUserId))
.or()
.nested(w -> w.eq(CpRelationship::getUserId, pair.cpUserId)
.eq(CpRelationship::getCpUserId, pair.userId))));
}
for (Long cpValId : createdCpValIds) {
cpBlessRecordService.deleteByCpValId(cpValId);
cpValueService.deleteById(cpValId);
}
if (!createdApplyIds.isEmpty()) {
cpApplyService.removeByIds(createdApplyIds);
}
}
@Test
public void walkThroughCpBrotherAndSistersFlowsWithLocalData() throws Exception {
List<BaseInfo> candidates = baseInfoService.query()
.select(BaseInfo::getId, BaseInfo::getUserSex, BaseInfo::getOriginSys,
BaseInfo::getAccount, BaseInfo::getUserNickname, BaseInfo::getCountryCode)
.eq(BaseInfo::getOriginSys, SYS_ORIGIN)
.in(BaseInfo::getUserSex, Arrays.asList(0, 1))
.orderByDesc(BaseInfo::getId)
.last("LIMIT 500")
.list();
assertFalse("local LIKEI candidates should not be empty", candidates.isEmpty());
UserPair cpPair = findPair(candidates, 1, 0, "CP");
runCpGiftApplyBlessDismissFlow(cpPair);
UserPair brotherPair = findPair(candidates, 1, 0, "BROTHER");
runNoChargeApplyAndAgreeFlow(brotherPair, "BROTHER");
UserPair sistersPair = findPair(candidates, 0, 1, "SISTERS");
runNoChargeApplyAndAgreeFlow(sistersPair, "SISTERS");
}
@Test
public void cpCabinReturnsEmptyStateWhenUserHasNoRelationship() throws Exception {
Long queryUserId = 2042274349343506434L;
Long reqUserId = 4569421795454615555L;
assertCpCabinEmptyState(queryUserId, reqUserId, "CP");
assertCpCabinEmptyState(queryUserId, reqUserId, "BROTHER");
assertCpCabinEmptyState(queryUserId, reqUserId, "SISTERS");
assertCpCabinRestEmptyState(queryUserId, reqUserId, "CP");
assertCpCabinRestEmptyState(queryUserId, reqUserId, "BROTHER");
assertCpCabinRestEmptyState(queryUserId, reqUserId, "SISTERS");
assertCpRightsRestConfig(queryUserId, "CP");
assertCpRightsRestConfig(queryUserId, "BROTHER");
assertCpRightsRestConfig(queryUserId, "SISTERS");
}
@Test
public void cpGiftAutoApplySkipsWhenRulesDoNotMatch() {
List<BaseInfo> candidates = baseInfoService.query()
.select(BaseInfo::getId, BaseInfo::getUserSex, BaseInfo::getOriginSys)
.eq(BaseInfo::getOriginSys, SYS_ORIGIN)
.eq(BaseInfo::getUserSex, 1)
.orderByDesc(BaseInfo::getId)
.last("LIMIT 500")
.list();
assertFalse("local LIKEI male candidates should not be empty", candidates.isEmpty());
UserPair sameGenderPair = findPair(candidates, 1, 1, "CP");
boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(sameGenderPair.sendUserId,
sameGenderPair.acceptUserId, SYS_ORIGIN);
assertFalse("CP gift auto apply should skip same gender users", created);
assertNull("CP gift auto apply should not create WAIT apply when skipped",
cpApplyService.getId(sameGenderPair.acceptUserId, sameGenderPair.sendUserId, "CP"));
}
@Test
public void cpGiftAutoApplyUsesGiftRelationType() {
List<BaseInfo> candidates = baseInfoService.query()
.select(BaseInfo::getId, BaseInfo::getUserSex, BaseInfo::getOriginSys,
BaseInfo::getCountryCode)
.eq(BaseInfo::getOriginSys, SYS_ORIGIN)
.in(BaseInfo::getUserSex, Arrays.asList(0, 1))
.orderByDesc(BaseInfo::getId)
.last("LIMIT 500")
.list();
assertFalse("local LIKEI candidates should not be empty", candidates.isEmpty());
assertGiftAutoApplyRelationType(findPair(candidates, 1, 0, "BROTHER"), "BROTHER");
assertGiftAutoApplyRelationType(findPair(candidates, 0, 1, "SISTERS"), "SISTERS");
}
private void assertGiftAutoApplyRelationType(UserPair pair, String relationType) {
boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId,
pair.acceptUserId, SYS_ORIGIN, relationType);
assertTrue(relationType + " gift should auto create apply", created);
Long applyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, relationType);
assertNotNull(relationType + " auto apply id", applyId);
createdApplyIds.add(applyId);
CpApply apply = cpApplyService.getById(applyId);
assertEquals(relationType, apply.getRelationType());
assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold()));
}
private void runCpGiftApplyBlessDismissFlow(UserPair pair) throws Exception {
boolean created = sendCpApplyCmdExe.autoCreateFromCpGift(pair.sendUserId, pair.acceptUserId,
SYS_ORIGIN);
Long pendingApplyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, "CP");
if (pendingApplyId != null && !createdApplyIds.contains(pendingApplyId)) {
createdApplyIds.add(pendingApplyId);
}
assertTrue("CP gift should auto create CP apply", created);
Long applyId = pendingApplyId;
assertNotNull("CP auto apply id", applyId);
createdApplyIds.add(applyId);
CpApply apply = cpApplyService.getById(applyId);
assertEquals("CP", apply.getRelationType());
assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold()));
agree(applyId, pair.acceptUserId);
CpRelationship relationship = cpRelationshipService.getByUserId(pair.sendUserId,
pair.acceptUserId, "CP");
assertNotNull("CP relationship", relationship);
createdPairs.add(new RelationPair(pair.sendUserId, pair.acceptUserId, "CP"));
createdCpValIds.add(relationship.getCpValId());
sendBlessGift(relationship, pair);
List<CpBlessRecord> blessRecords = cpBlessRecordService.query()
.eq(CpBlessRecord::getCpValId, relationship.getCpValId())
.list();
assertEquals("bless records for both users", 2, blessRecords.size());
CpApplyDismissCmd dismissCmd = new CpApplyDismissCmd()
.setCpUserId(pair.acceptUserId)
.setRelationType("CP");
dismissCmd.setReqUserId(pair.sendUserId);
dismissCmd.setReqSysOrigin(REQ_SYS_ORIGIN);
dismissCpApplyCmdExe.execute(dismissCmd);
CpPairUserProfileCO dismissing = findPairView(pair.sendUserId, pair.acceptUserId, "CP");
assertNotNull("CP pair after dismiss", dismissing);
assertEquals("DISMISSING", dismissing.getStatus());
assertNotNull("dismissEndTime", dismissing.getDismissEndTime());
assertNotNull("dismissRemainSeconds", dismissing.getDismissRemainSeconds());
assertTrue("dismissRemainSeconds should be positive",
dismissing.getDismissRemainSeconds() > 0);
}
private void assertCpCabinEmptyState(Long queryUserId, Long reqUserId, String relationType) {
UserCpCabinQueryCmd cmd = new UserCpCabinQueryCmd();
cmd.setUserId(queryUserId);
cmd.setReqUserId(reqUserId);
cmd.setReqSysOrigin(REQ_SYS_ORIGIN);
cmd.setRelationType(relationType);
UserCpCabinCO cabin = userCpCabinQueryExe.execute(cmd);
assertNotNull(relationType + " empty cabin", cabin);
assertEquals("0", cabin.getCpVal());
assertEquals(Long.valueOf(0L), cabin.getBlessVal());
assertNull(cabin.getCpPairUserProfileCO());
assertNotNull(cabin.getCpCabinConfigCO());
assertTrue(cabin.getCpCabinConfigCO().isEmpty());
assertNull(cabin.getCurrentCpCabinConfigCO());
assertNull(cabin.getUserCpCabinSpecial());
assertFalse(cabin.getCheckSelfCabin());
}
private void assertCpCabinRestEmptyState(Long queryUserId, Long reqUserId,
String relationType) throws Exception {
mockMvc.perform(get("/user/cp/cabin")
.param("userId", String.valueOf(queryUserId))
.param("relationType", relationType)
.header("Req-Sys-Origin", "origin=LIKEI;originChild=LIKEI;")
.header("Req-App-Intel", "version=1.0.0;build=1;channel=local;")
.header("Req-Client", "Android")
.header("Req-Inner-Internal",
"time=" + System.currentTimeMillis() + ";uid=" + reqUserId + ";"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(true))
.andExpect(jsonPath("$.body.cpVal").value("0"))
.andExpect(jsonPath("$.body.blessVal").value("0"))
.andExpect(jsonPath("$.body.cpPairUserProfileCO").doesNotExist())
.andExpect(jsonPath("$.body.cpCabinConfigCO").isArray())
.andExpect(jsonPath("$.body.cpCabinConfigCO").isEmpty())
.andExpect(jsonPath("$.body.currentCpCabinConfigCO").doesNotExist())
.andExpect(jsonPath("$.body.userCpCabinSpecial").doesNotExist())
.andExpect(jsonPath("$.body.checkSelfCabin").value(false));
}
private void assertCpRightsRestConfig(Long reqUserId, String relationType) throws Exception {
mockMvc.perform(get("/user/cp/rights/config")
.param("relationType", relationType)
.header("Req-Sys-Origin", "origin=LIKEI;originChild=LIKEI;")
.header("Req-App-Intel", "version=1.0.0;build=1;channel=local;")
.header("Req-Client", "Android")
.header("Req-Inner-Internal",
"time=" + System.currentTimeMillis() + ";uid=" + reqUserId + ";"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value(true))
.andExpect(jsonPath("$.body.relationType").value(relationType))
.andExpect(jsonPath("$.body.relationshipStatus").exists())
.andExpect(jsonPath("$.body.intimacyValue").exists())
.andExpect(jsonPath("$.body.levels").isArray());
}
private void runNoChargeApplyAndAgreeFlow(UserPair pair, String relationType) throws Exception {
Method process = SendCpApplyCmdExe.class.getDeclaredMethod("process", Long.class, Long.class,
String.class, String.class, boolean.class);
process.setAccessible(true);
process.invoke(sendCpApplyCmdExe, pair.sendUserId, pair.acceptUserId, SYS_ORIGIN, relationType,
false);
Long applyId = cpApplyService.getId(pair.acceptUserId, pair.sendUserId, relationType);
assertNotNull(relationType + " apply id", applyId);
createdApplyIds.add(applyId);
CpApply apply = cpApplyService.getById(applyId);
assertEquals(relationType, apply.getRelationType());
assertEquals(0, BigDecimal.ZERO.compareTo(apply.getApplyConsumeGold()));
agree(applyId, pair.acceptUserId);
CpRelationship relationship = cpRelationshipService.getByUserId(pair.sendUserId,
pair.acceptUserId, relationType);
assertNotNull(relationType + " relationship", relationship);
assertEquals(relationType, relationship.getRelationType());
createdPairs.add(new RelationPair(pair.sendUserId, pair.acceptUserId, relationType));
createdCpValIds.add(relationship.getCpValId());
CpPairUserProfileCO pairView = findPairView(pair.sendUserId, pair.acceptUserId, relationType);
assertNotNull(relationType + " pair view", pairView);
assertEquals(relationType, pairView.getRelationType());
assertEquals("NORMAL", pairView.getStatus());
assertNull(pairView.getDismissEndTime());
}
private void agree(Long applyId, Long acceptUserId) {
ProcessApplyCmd cmd = new ProcessApplyCmd()
.setApplyId(applyId)
.setAgree(Boolean.TRUE);
cmd.setReqUserId(acceptUserId);
cmd.setReqSysOrigin(REQ_SYS_ORIGIN);
processCpApplyCmd.execute(cmd);
}
private void sendBlessGift(CpRelationship relationship, UserPair pair) throws Exception {
GiftConfigDTO gift = giftCacheService.listBySysOrigin(
com.red.circle.common.business.core.enums.SysOriginPlatformEnum.LIKEI)
.stream()
.filter(g -> Objects.nonNull(g.getGiftPhoto()))
.findFirst()
.orElse(null);
assertNotNull("local gift config", gift);
GiftGiveRunningWater runningWater = GiftGiveRunningWater.builder()
.id(System.currentTimeMillis())
.sysOrigin(SYS_ORIGIN)
.originId("cp-local-flow")
.dynamicContentId(null)
.userId(pair.sendUserId)
.giftId(gift.getId())
.checkBlessingsGift(Boolean.TRUE)
.acceptUsers(Lists.newArrayList(GiftAcceptUser.builder()
.acceptUserId(pair.acceptUserId)
.build()))
.giftValue(GiftValue.builder()
.currencyType(GiftCurrencyType.GOLD.name())
.giftType("CP")
.unitPrice(BigDecimal.ONE)
.quantity(1)
.userSize(1)
.giftValue(BigDecimal.ONE)
.actualAmount(BigDecimal.ONE)
.build())
.build();
Object target = AopTestUtils.getUltimateTargetObject(giftCountStrategy);
ReflectionTestUtils.invokeMethod(target, "saveBlessingsRecord", runningWater);
}
@Autowired
private com.red.circle.other.app.listener.gift.strategy.GiftCountStrategy giftCountStrategy;
private CpPairUserProfileCO findPairView(Long userId, Long cpUserId, String relationType) {
CpRelationshipQueryCmd cmd = new CpRelationshipQueryCmd().setRelationType(relationType);
cmd.setReqUserId(userId);
cmd.setReqSysOrigin(REQ_SYS_ORIGIN);
return pairUserProfileQryExe.execute(cmd)
.stream()
.filter(item -> Objects.nonNull(item.getCpUserProfile())
&& Objects.equals(item.getCpUserProfile().getId(), cpUserId))
.findFirst()
.orElse(null);
}
private UserPair findPair(List<BaseInfo> candidates, Integer sendSex, Integer acceptSex,
String relationType) {
for (BaseInfo send : candidates) {
if (!Objects.equals(send.getUserSex(), sendSex) || hasWaitApply(send.getId(), relationType)
|| cpRelationshipService.countCp(send.getId(), relationType) >= relationMaxCount(relationType)) {
continue;
}
for (BaseInfo accept : candidates) {
if (Objects.equals(send.getId(), accept.getId())
|| !Objects.equals(accept.getUserSex(), acceptSex)
|| hasReceivedWaitApply(accept.getId(), relationType)
|| cpRelationshipService.countCp(accept.getId(), relationType) >= relationMaxCount(relationType)
|| cpRelationshipService.existsCp(send.getId(), accept.getId(), relationType)) {
continue;
}
if (userRegionGateway.checkEqRegion(send.getId(), accept.getId())) {
return new UserPair(send.getId(), accept.getId());
}
}
}
throw new AssertionError("No local candidate pair for " + relationType);
}
private boolean hasWaitApply(Long sendUserId, String relationType) {
return cpApplyService.existsNotProcessSendApply(sendUserId, relationType);
}
private int relationMaxCount(String relationType) {
return Objects.equals("CP", relationType) ? 1 : 3;
}
private boolean hasReceivedWaitApply(Long acceptUserId, String relationType) {
return cpApplyService.query()
.eq(CpApply::getAcceptApplyUserId, acceptUserId)
.eq(CpApply::getRelationType, relationType)
.eq(CpApply::getStatus, "WAIT")
.count() > 0;
}
private void executeIgnoreDuplicate(String sql) {
try {
jdbcTemplate.execute(sql);
} catch (Exception ignored) {
// Column already exists in local DB.
}
}
private record UserPair(Long sendUserId, Long acceptUserId) {
}
private record RelationPair(Long userId, Long cpUserId, String relationType) {
}
}

View File

@ -0,0 +1,131 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientConfigurationBuilder;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.StaticSessionCredentialsProvider;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.SimpleConsumer;
import org.apache.rocketmq.client.apis.message.MessageView;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;
import org.junit.Test;
import org.springframework.test.annotation.IfProfileValue;
/**
* Local RocketMQ real-connection smoke test. Run explicitly with:
*
* <pre>
* mvn -pl rc-service/rc-service-other/other-start -am \
* -Drocketmq.local.real=true \
* -Dtest=RocketMqLocalSmokeTest#sendAndReceiveOrdinaryMessage test
*
* mvn -pl rc-service/rc-service-other/other-start -am \
* -Drocketmq.local.real=true \
* -Dtest=RocketMqLocalSmokeTest#sendAndReceiveDelayedMessage test
* </pre>
*/
@IfProfileValue(name = "rocketmq.local.real", value = "true")
public class RocketMqLocalSmokeTest {
private static final String DEFAULT_ENDPOINT = "127.0.0.1:18081";
private static final String DEFAULT_TOPIC = "RC_DEFAULT_APP_ORDINARY";
private static final String DEFAULT_DELAY_TOPIC = "RC_DEFAULT_APP_DELAYED";
private static final String TAG = "codex_local_mq_smoke";
@Test
public void sendAndReceiveOrdinaryMessage() throws Exception {
String endpoint = System.getProperty("rocketmq.endpoint", DEFAULT_ENDPOINT);
String topic = System.getProperty("rocketmq.topic", DEFAULT_TOPIC);
sendAndReceive(endpoint, topic, false);
}
@Test
public void sendAndReceiveDelayedMessage() throws Exception {
String endpoint = System.getProperty("rocketmq.endpoint", DEFAULT_ENDPOINT);
String topic = System.getProperty("rocketmq.delayTopic", DEFAULT_DELAY_TOPIC);
sendAndReceive(endpoint, topic, true);
}
private void sendAndReceive(String endpoint, String topic, boolean delayed) throws Exception {
String accessKey = System.getProperty("rocketmq.accessKey", "local");
String secretKey = System.getProperty("rocketmq.secretKey", "local");
String key = "codex-local-mq-" + UUID.randomUUID();
String body = "rocketmq-local-smoke:" + key;
ClientServiceProvider provider = ClientServiceProvider.loadService();
ClientConfiguration clientConfiguration = clientConfiguration(endpoint, accessKey, secretKey);
Producer producer = provider.newProducerBuilder()
.setClientConfiguration(clientConfiguration)
.setTopics(topic)
.build();
try {
org.apache.rocketmq.client.apis.message.MessageBuilder messageBuilder = provider.newMessageBuilder()
.setTopic(topic)
.setTag(TAG)
.setKeys(key)
.setBody(body.getBytes(StandardCharsets.UTF_8));
if (delayed) {
messageBuilder.setDeliveryTimestamp(System.currentTimeMillis() + Duration.ofSeconds(2).toMillis());
}
SendReceipt receipt = producer.send(messageBuilder.build());
assertTrue("message id should not be blank", receipt.getMessageId().toString().length() > 0);
} finally {
producer.close();
}
String group = "codex_local_mq_smoke_" + UUID.randomUUID().toString().replace("-", "");
SimpleConsumer consumer = provider.newSimpleConsumerBuilder()
.setClientConfiguration(clientConfiguration)
.setConsumerGroup(group)
.setSubscriptionExpressions(Collections.singletonMap(
topic, new FilterExpression(TAG, FilterExpressionType.TAG)))
.setAwaitDuration(Duration.ofSeconds(5))
.build();
try {
MessageView matched = receiveByKey(consumer, key);
assertEquals(body, decodeBody(matched.getBody()));
consumer.ack(matched);
} finally {
consumer.close();
}
}
private ClientConfiguration clientConfiguration(String endpoint, String accessKey, String secretKey) {
ClientConfigurationBuilder builder = ClientConfiguration.newBuilder()
.setEndpoints(endpoint)
.setRequestTimeout(Duration.ofSeconds(30));
if (accessKey != null && !accessKey.isEmpty()) {
builder.setCredentialProvider(new StaticSessionCredentialsProvider(accessKey, secretKey));
}
return builder.build();
}
private MessageView receiveByKey(SimpleConsumer consumer, String key) throws Exception {
long deadline = System.currentTimeMillis() + Duration.ofSeconds(30).toMillis();
while (System.currentTimeMillis() < deadline) {
List<MessageView> messages = consumer.receive(16, Duration.ofSeconds(15));
for (MessageView message : messages) {
if (message.getKeys().contains(key)) {
return message;
}
}
}
throw new AssertionError("message not received by key: " + key);
}
private String decodeBody(ByteBuffer body) {
ByteBuffer duplicate = body.asReadOnlyBuffer();
byte[] bytes = new byte[duplicate.remaining()];
duplicate.get(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
}

View File

@ -26,7 +26,7 @@ import java.util.Objects;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SalaryAccountServiceImpl implements SalaryAccountService { public class SalaryAccountServiceImpl implements SalaryAccountService {
private static final boolean SALARY_WALLET_OUT_PAUSED = true; private static final boolean SALARY_WALLET_OUT_PAUSED = false;
private final SalaryAccountBalanceQryExe salaryAccountBalanceQryExe; private final SalaryAccountBalanceQryExe salaryAccountBalanceQryExe;
private final SalaryRunningWaterQryExe salaryRunningWaterQryExe; private final SalaryRunningWaterQryExe salaryRunningWaterQryExe;

View File

@ -33,7 +33,7 @@ import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SalaryDiamondServiceImpl implements SalaryDiamondService { public class SalaryDiamondServiceImpl implements SalaryDiamondService {
private static final boolean SALARY_EXCHANGE_PAUSED = true; private static final boolean SALARY_EXCHANGE_PAUSED = false;
private final UserBillAgentSalaryDiamondCheckExchangeGoldQryExe userBillAgentSalaryDiamondCheckExchangeGoldQryExe; private final UserBillAgentSalaryDiamondCheckExchangeGoldQryExe userBillAgentSalaryDiamondCheckExchangeGoldQryExe;
private final UserSalaryDiamondBalanceQryExe userSalaryDiamondBalanceQryExe; private final UserSalaryDiamondBalanceQryExe userSalaryDiamondBalanceQryExe;

View File

@ -41,7 +41,7 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class UserBankServiceImpl implements UserBankService { public class UserBankServiceImpl implements UserBankService {
private static final boolean USER_TO_COIN_SELLER_TRANSFER_PAUSED = true; private static final boolean USER_TO_COIN_SELLER_TRANSFER_PAUSED = false;
private final UserBankWaterQryExe userBankWaterQryExe; private final UserBankWaterQryExe userBankWaterQryExe;
private final UserBankBalanceQryExe userBankBalanceQryExe; private final UserBankBalanceQryExe userBankBalanceQryExe;

View File

@ -0,0 +1,13 @@
ALTER TABLE `user_cp_apply`
ADD COLUMN `relation_type` varchar(32) NOT NULL DEFAULT 'CP' COMMENT '关系类型CP/BROTHER/SISTERS' AFTER `accept_apply_user_id`;
ALTER TABLE `user_cp_relationship`
ADD COLUMN `relation_type` varchar(32) NOT NULL DEFAULT 'CP' COMMENT '关系类型CP/BROTHER/SISTERS' AFTER `cp_user_id`;
UPDATE `user_cp_apply`
SET `relation_type` = 'CP'
WHERE `relation_type` IS NULL OR `relation_type` = '';
UPDATE `user_cp_relationship`
SET `relation_type` = 'CP'
WHERE `relation_type` IS NULL OR `relation_type` = '';

View File

@ -0,0 +1,7 @@
ALTER TABLE `sys_gift_config`
ADD COLUMN `cp_relation_type` varchar(32) DEFAULT NULL COMMENT 'CP礼物关系类型CP/BROTHER/SISTERS' AFTER `gift_tab`;
UPDATE `sys_gift_config`
SET `cp_relation_type` = 'CP'
WHERE `gift_tab` = 'CP'
AND (`cp_relation_type` IS NULL OR `cp_relation_type` = '');