Compare commits
No commits in common. "main" and "release/redis-memory-fix-20260521" have entirely different histories.
main
...
release/re
@ -175,7 +175,6 @@ gateway:
|
||||
- /product/apple/config
|
||||
- /game/sud/**
|
||||
- /web/pay/**
|
||||
- /order/web/pay/**
|
||||
- /external/oss/upload
|
||||
- /telegram/webhook
|
||||
- /account/create/getRegion
|
||||
@ -198,12 +197,6 @@ gateway:
|
||||
- /go/resident-activity/specified-gift-weekly-rank/**
|
||||
- /resident-activity/specified-gift-weekly-rank/**
|
||||
- /go/resident-activity/week-star/**
|
||||
- /getUserInfo
|
||||
- /go/getUserInfo
|
||||
- /updateBalance
|
||||
- /go/updateBalance
|
||||
- /game/hotgame/**
|
||||
- /go/game/hotgame/**
|
||||
- /resident-activity/week-star/**
|
||||
- /resident-activity/sign-in-reward/**
|
||||
- /resident-activity/vip/**
|
||||
@ -213,7 +206,6 @@ gateway:
|
||||
- /resident-activity/voice-room-rocket/**
|
||||
- /resident-activity/wheel/**
|
||||
- /resident-activity/smash-golden-egg/**
|
||||
- /operate/hotgame-game/**
|
||||
- /operate/baishun-game/**
|
||||
- /operate/lingxian-game/**
|
||||
- /app-system/home-popups/**
|
||||
|
||||
@ -128,13 +128,6 @@ task:
|
||||
topic: ${TASK_CENTER_EVENT_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
|
||||
tag: ${TASK_CENTER_EVENT_MQ_TAG:task_center_event}
|
||||
|
||||
cp:
|
||||
relation-broadcast:
|
||||
mq:
|
||||
enabled: ${CP_RELATION_BROADCAST_MQ_ENABLED:true}
|
||||
topic: ${CP_RELATION_BROADCAST_MQ_TOPIC:RC_DEFAULT_APP_ORDINARY}
|
||||
tag: ${CP_RELATION_BROADCAST_MQ_TAG:cp_relation_broadcast}
|
||||
|
||||
voice-room:
|
||||
region-broadcast:
|
||||
go:
|
||||
|
||||
@ -39,11 +39,10 @@ public class LoginAccessValidationService {
|
||||
"香港", "澳门", "澳門", "台湾", "台灣", "Hong Kong", "Macau", "Macao", "Taiwan"
|
||||
);
|
||||
private static final List<String> BLOCKED_COUNTRY_CODES = List.of(
|
||||
"HK", "MO", "TW", "SG"
|
||||
"MO"
|
||||
);
|
||||
private static final List<String> BLOCKED_COUNTRY_NAMES = List.of(
|
||||
"香港", "澳门", "澳門", "台湾", "台灣", "新加坡",
|
||||
"Hong Kong", "Macau", "Macao", "Taiwan", "Singapore"
|
||||
"澳门", "澳門", "Macau", "Macao"
|
||||
);
|
||||
private static final List<IpGeoProvider> IP_GEO_PROVIDERS = List.of(
|
||||
new IpGeoProvider("ip.sb", "https://api.ip.sb/geoip/%s"),
|
||||
|
||||
@ -70,22 +70,22 @@ public class LoginAccessValidationServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockHongKongRegion() {
|
||||
public void allowHongKongRegion() {
|
||||
JSONObject data = JSON.parseObject("""
|
||||
{"country_id":"CN","country":"中国","region":"香港"}
|
||||
""");
|
||||
|
||||
assertFalse(LoginAccessValidationService.isMainlandChina(data));
|
||||
assertTrue(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
assertFalse(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockHongKongCountryCode() {
|
||||
public void allowHongKongCountryCode() {
|
||||
JSONObject data = JSON.parseObject("""
|
||||
{"country_id":"HK","country":"Hong Kong","region":"Kwai Tsing District"}
|
||||
""");
|
||||
|
||||
assertTrue(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
assertFalse(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -98,30 +98,30 @@ public class LoginAccessValidationServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockSingaporeCountryCode() {
|
||||
public void allowSingaporeCountryCode() {
|
||||
JSONObject data = JSON.parseObject("""
|
||||
{"country_id":"SG","country":"Singapore","region":""}
|
||||
""");
|
||||
|
||||
assertTrue(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
assertFalse(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockTaiwanRegion() {
|
||||
public void allowTaiwanRegion() {
|
||||
JSONObject data = JSON.parseObject("""
|
||||
{"country_id":"CN","country":"中国","region":"台湾"}
|
||||
""");
|
||||
|
||||
assertFalse(LoginAccessValidationService.isMainlandChina(data));
|
||||
assertTrue(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
assertFalse(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockTaiwanCountryCode() {
|
||||
public void allowTaiwanCountryCode() {
|
||||
JSONObject data = JSON.parseObject("""
|
||||
{"country_id":"TW","country":"Taiwan","region":""}
|
||||
""");
|
||||
|
||||
assertTrue(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
assertFalse(LoginAccessValidationService.isBlockedIpRegion(data));
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ public enum ConsoleAccountStatusEnum {
|
||||
/**
|
||||
* 账号解封.
|
||||
*/
|
||||
UNTIE_ACOOUNT("UNTIE_ACOOUNT", "账号解封"),
|
||||
UNTIE_ACOOUNT("UNTIE_DEVICE", "账号解封"),
|
||||
|
||||
/**
|
||||
* 设备+账户解封.
|
||||
|
||||
@ -51,9 +51,4 @@ public class GiveGiftConfig implements Serializable {
|
||||
*/
|
||||
private String giftTab;
|
||||
|
||||
/**
|
||||
* CP礼物关系类型:CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private String cpRelationType;
|
||||
|
||||
}
|
||||
|
||||
@ -56,10 +56,6 @@ public class BuySuccessEvent implements Serializable {
|
||||
*/
|
||||
String payPlatform;
|
||||
|
||||
/**
|
||||
* 本次充值请求的App版本.
|
||||
*/
|
||||
String appVersion;
|
||||
/**
|
||||
* 来源.
|
||||
*/
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
package com.red.circle.mq.business.model.event.pay;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 通用充值成功事件。
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class RechargeSuccessEvent implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String eventId;
|
||||
private String sysOrigin;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long amountCents;
|
||||
|
||||
private String amountUsd;
|
||||
private String currency;
|
||||
private String payPlatform;
|
||||
private String paymentMethod;
|
||||
private String googleProductId;
|
||||
private String productId;
|
||||
private String appVersion;
|
||||
private String sourceOrderId;
|
||||
private String orderId;
|
||||
private String occurredAt;
|
||||
private Map<String, Object> payload;
|
||||
}
|
||||
@ -22,11 +22,6 @@ public interface QueueNameConstant {
|
||||
*/
|
||||
String BUY_SUCCESS = "BUY_SUCCESS";
|
||||
|
||||
/**
|
||||
* 通用充值成功。
|
||||
*/
|
||||
String RECHARGE_SUCCESS = "RECHARGE_SUCCESS";
|
||||
|
||||
/**
|
||||
* 视频语音房间会话
|
||||
*/
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
package com.red.circle.mq.rocket.business.streams;
|
||||
|
||||
import com.red.circle.mq.rocket.business.constant.QueueNameConstant;
|
||||
|
||||
/**
|
||||
* 通用充值成功事件。
|
||||
*/
|
||||
public interface RechargeSuccessSink {
|
||||
|
||||
String INPUT = QueueNameConstant.RECHARGE_SUCCESS;
|
||||
String TAG = "recharge_success_v1";
|
||||
}
|
||||
@ -1,11 +1,7 @@
|
||||
package com.red.circle.external.inner.endpoint.message.api;
|
||||
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
import jakarta.validation.Valid;
|
||||
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.RequestParam;
|
||||
|
||||
/**
|
||||
@ -33,10 +29,4 @@ public interface ImMessageClientApi {
|
||||
@RequestParam("toAccount") Long toAccount,
|
||||
@RequestParam("text") String text);
|
||||
|
||||
/**
|
||||
* 发送 C2C 自定义消息.
|
||||
*/
|
||||
@PostMapping("/sendCustomMessage")
|
||||
ResultResponse<Void> sendCustomMessage(@RequestBody @Valid CustomC2cMsgBodyCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
package com.red.circle.external.inner.model.cmd.message;
|
||||
|
||||
import com.red.circle.framework.dto.Command;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* C2C custom IM message.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CustomC2cMsgBodyCmd extends Command {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "fromAccount required.")
|
||||
private String fromAccount;
|
||||
|
||||
@NotBlank(message = "toAccount required.")
|
||||
private String toAccount;
|
||||
|
||||
@NotBlank(message = "desc required.")
|
||||
private String desc;
|
||||
|
||||
@NotNull(message = "data required.")
|
||||
private Object data;
|
||||
|
||||
private Integer syncOtherMachine;
|
||||
|
||||
public static CustomC2cMsgBodyBuilder builder() {
|
||||
return new CustomC2cMsgBodyBuilder();
|
||||
}
|
||||
|
||||
public static class CustomC2cMsgBodyBuilder {
|
||||
|
||||
private String fromAccount;
|
||||
private String toAccount;
|
||||
private String desc;
|
||||
private Object data;
|
||||
private Integer syncOtherMachine;
|
||||
|
||||
public CustomC2cMsgBodyBuilder fromAccount(String fromAccount) {
|
||||
this.fromAccount = fromAccount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder fromAccount(Long fromAccount) {
|
||||
this.fromAccount = String.valueOf(fromAccount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder toAccount(String toAccount) {
|
||||
this.toAccount = toAccount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder toAccount(Long toAccount) {
|
||||
this.toAccount = String.valueOf(toAccount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder desc(String desc) {
|
||||
this.desc = desc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder data(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyBuilder syncOtherMachine(Integer syncOtherMachine) {
|
||||
this.syncOtherMachine = syncOtherMachine;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2cMsgBodyCmd build() {
|
||||
CustomC2cMsgBodyCmd cmd = new CustomC2cMsgBodyCmd();
|
||||
cmd.setFromAccount(this.fromAccount);
|
||||
cmd.setToAccount(this.toAccount);
|
||||
cmd.setDesc(this.desc);
|
||||
cmd.setData(this.data);
|
||||
cmd.setSyncOtherMachine(this.syncOtherMachine);
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,9 +6,7 @@ import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryChannelDetailsCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryDTO;
|
||||
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDTO;
|
||||
import com.red.circle.order.inner.model.dto.SysPayCountryChannelDetailsDTO;
|
||||
@ -90,10 +88,6 @@ public interface PayCountryClientApi {
|
||||
@GetMapping("/shelf")
|
||||
ResultResponse<Void> shelf(@RequestParam("id") Long id, @RequestParam("shelf") Boolean shelf);
|
||||
|
||||
@PostMapping("/syncCurrencyRates")
|
||||
ResultResponse<PayCountryCurrencyRateSyncDTO> syncCurrencyRates(
|
||||
@RequestBody SysPayCountryCurrencyRateSyncCmd cmd);
|
||||
|
||||
@GetMapping("/listPayCountryByRegionId")
|
||||
ResultResponse<List<PayCountryDTO>> listPayCountryByRegionId(
|
||||
@RequestParam("regionId") String regionId);
|
||||
|
||||
@ -7,7 +7,6 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serial;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
@ -47,12 +46,6 @@ public class SysPayApplicationCommodityCmd extends CommonCommand {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long payCountryId;
|
||||
|
||||
/**
|
||||
* 批量创建时的国家列表;表结构仍是一条商品对应一个国家,所以只在新增时拆成多条记录.
|
||||
*/
|
||||
@JsonSerialize(contentUsing = ToStringSerializer.class)
|
||||
private List<Long> payCountryIds;
|
||||
|
||||
/**
|
||||
* 区域ID.
|
||||
*/
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
package com.red.circle.order.inner.model.cmd;
|
||||
|
||||
import com.red.circle.framework.core.dto.CommonCommand;
|
||||
import jakarta.validation.constraints.DecimalMax;
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 支付国家货币汇率同步命令.
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class SysPayCountryCurrencyRateSyncCmd extends CommonCommand {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 上浮比例,5 表示在实时汇率基础上增加 5%.
|
||||
*/
|
||||
@NotNull
|
||||
@DecimalMin("0")
|
||||
@DecimalMax("100")
|
||||
@Digits(integer = 3, fraction = 4)
|
||||
private BigDecimal markupPercent;
|
||||
}
|
||||
@ -102,9 +102,4 @@ public class InAppPurchaseDetailsQryCmd extends Command {
|
||||
* 条数.
|
||||
*/
|
||||
private Integer limit;
|
||||
|
||||
/**
|
||||
* 是否导出全部筛选结果;导出时不使用 lastId 和 limit 分页.
|
||||
*/
|
||||
private Boolean exportAll;
|
||||
}
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
package com.red.circle.order.inner.model.dto;
|
||||
|
||||
import com.red.circle.framework.dto.DTO;
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 支付国家货币汇率同步结果.
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class PayCountryCurrencyRateSyncDTO extends DTO {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 参与同步的支付国家数量.
|
||||
*/
|
||||
private Integer totalCount;
|
||||
|
||||
/**
|
||||
* 已更新的支付国家数量.
|
||||
*/
|
||||
private Integer updatedCount;
|
||||
|
||||
/**
|
||||
* 跳过的支付国家数量.
|
||||
*/
|
||||
private Integer skippedCount;
|
||||
|
||||
/**
|
||||
* 汇率来源.
|
||||
*/
|
||||
private String sourceName;
|
||||
|
||||
/**
|
||||
* 被跳过的国家说明.
|
||||
*/
|
||||
private List<String> skippedCountries;
|
||||
}
|
||||
@ -11,15 +11,11 @@ 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.TeamChangeOwnerCmd;
|
||||
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.TeamTableQryCmd;
|
||||
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.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.TeamProfileDTO;
|
||||
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentDTO;
|
||||
@ -154,20 +150,6 @@ public interface TeamProfileClientApi {
|
||||
ResultResponse<PageResult<TeamSalaryPaymentDTO>> listByCondition(
|
||||
@RequestBody TeamSalaryBackQryCmd cmd);
|
||||
|
||||
/**
|
||||
* 后台手动发放主播工资预览.
|
||||
*/
|
||||
@PostMapping("/manualSalary/page")
|
||||
ResultResponse<PageResult<TeamManualSalaryPaymentDTO>> manualSalaryPage(
|
||||
@RequestBody TeamManualSalaryPaymentQryCmd cmd);
|
||||
|
||||
/**
|
||||
* 后台手动发放主播工资.
|
||||
*/
|
||||
@PostMapping("/manualSalary/pay")
|
||||
ResultResponse<List<TeamManualSalaryPaymentResultDTO>> manualSalaryPay(
|
||||
@RequestBody TeamManualSalaryPaymentCmd cmd);
|
||||
|
||||
/**
|
||||
* 获得区域下的所有团队ID.
|
||||
*/
|
||||
|
||||
@ -222,21 +222,6 @@ public enum EnumConfigKey {
|
||||
*/
|
||||
CP_PRICE,
|
||||
|
||||
/**
|
||||
* CP 关系解绑金币.
|
||||
*/
|
||||
CP_DISMISS_CONSUME_GOLD,
|
||||
|
||||
/**
|
||||
* Brother 关系解绑金币.
|
||||
*/
|
||||
BROTHER_DISMISS_CONSUME_GOLD,
|
||||
|
||||
/**
|
||||
* Sisters 关系解绑金币.
|
||||
*/
|
||||
SISTERS_DISMISS_CONSUME_GOLD,
|
||||
|
||||
/**
|
||||
* Aswat 超级管理员ID.
|
||||
*/
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
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 Integer billBelong;
|
||||
|
||||
/**
|
||||
* 指定发放用户,为空时按国家全量发放.
|
||||
*/
|
||||
private List<Long> userIds;
|
||||
|
||||
/**
|
||||
* 后台操作人.
|
||||
*/
|
||||
private Long operationUserId;
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 工资账期,为空时使用当前整月账期.
|
||||
*/
|
||||
private Integer billBelong;
|
||||
|
||||
/**
|
||||
* 用户 id.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 排序字段.
|
||||
*/
|
||||
private String sortField;
|
||||
|
||||
/**
|
||||
* 排序方向.
|
||||
*/
|
||||
private String sortOrder;
|
||||
}
|
||||
@ -29,16 +29,6 @@ public class TeamManagerAddCmd extends Command {
|
||||
@NotBlank(message = "regionId required.")
|
||||
private String regionId;
|
||||
|
||||
private Boolean canAddBdLeader;
|
||||
|
||||
private Boolean canGiftProps;
|
||||
|
||||
private Boolean canBanUser;
|
||||
|
||||
private Boolean canUnbanUser;
|
||||
|
||||
private Boolean canChangeCountry;
|
||||
|
||||
@NotNull(message = "createUser required.")
|
||||
private Long createUser;
|
||||
|
||||
|
||||
@ -1,158 +0,0 @@
|
||||
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 Boolean agentMember;
|
||||
|
||||
/**
|
||||
* 当前账期按积分档位计算出的工资.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -32,16 +32,6 @@ public class TeamManagerDTO extends DTO {
|
||||
|
||||
private String regionId;
|
||||
|
||||
private Boolean canAddBdLeader;
|
||||
|
||||
private Boolean canGiftProps;
|
||||
|
||||
private Boolean canBanUser;
|
||||
|
||||
private Boolean canUnbanUser;
|
||||
|
||||
private Boolean canChangeCountry;
|
||||
|
||||
private Timestamp createTime;
|
||||
|
||||
private Timestamp updateTime;
|
||||
|
||||
@ -96,11 +96,6 @@ public class GiftConfigDTO implements Serializable {
|
||||
*/
|
||||
private String giftTab;
|
||||
|
||||
/**
|
||||
* CP礼物关系类型:CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private String cpRelationType;
|
||||
|
||||
/**
|
||||
* 0.未删除 1.已删除.
|
||||
*/
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.red.circle.wallet.inner.model.cmd;
|
||||
|
||||
import com.red.circle.common.business.dto.cmd.HistoryRangeTimeQryPageCmd;
|
||||
import com.red.circle.framework.core.dto.CommonCommand;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
@ -13,7 +13,7 @@ import lombok.experimental.Accessors;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class UserFreightBalanceRunningWaterExportQryCmd extends HistoryRangeTimeQryPageCmd {
|
||||
public class UserFreightBalanceRunningWaterExportQryCmd extends CommonCommand {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -27,31 +27,7 @@ public class UserFreightBalanceRunningWaterExportQryCmd extends HistoryRangeTime
|
||||
/**
|
||||
* 日期yyyyMM.
|
||||
*/
|
||||
@NotBlank
|
||||
private String monthDate;
|
||||
|
||||
/**
|
||||
* 发送人用户id.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 接收人用户id.
|
||||
*/
|
||||
private Long acceptUserId;
|
||||
|
||||
/**
|
||||
* 来源.
|
||||
*/
|
||||
private String origin;
|
||||
|
||||
/**
|
||||
* 查询后台操作流水.
|
||||
*/
|
||||
private Boolean queryBackOperationUser;
|
||||
|
||||
/**
|
||||
* 操作用户ID,多个用逗号分隔.
|
||||
*/
|
||||
private String createUsers;
|
||||
|
||||
}
|
||||
|
||||
@ -42,9 +42,5 @@ public class UserFreightBalanceRunningWaterPageQryCmd extends HistoryRangeTimeQr
|
||||
*/
|
||||
private Boolean queryBackOperationUser;
|
||||
|
||||
/**
|
||||
* 操作用户ID,多个用逗号分隔.
|
||||
*/
|
||||
private String createUsers;
|
||||
|
||||
}
|
||||
|
||||
@ -50,11 +50,6 @@ public class UserGoldRunningWaterBackQryCmd extends Command {
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long backOperationUser;
|
||||
|
||||
/**
|
||||
* 后台操作用户ID,多个用逗号分隔.
|
||||
*/
|
||||
private String backOperationUsers;
|
||||
|
||||
/**
|
||||
* 跟踪id.
|
||||
*/
|
||||
|
||||
@ -755,11 +755,6 @@ public enum GoldOrigin {
|
||||
*/
|
||||
CP_LOVE_LETTER("CP love letter"),
|
||||
|
||||
/**
|
||||
* 解除 CP/Brother/Sisters 关系.
|
||||
*/
|
||||
CP_DISMISS("CP dismiss"),
|
||||
|
||||
/**
|
||||
* cp榜单(周榜和季度榜)
|
||||
*/
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
package com.red.circle.console.adapter.app.activity;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpConfigCO;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpConfigSaveCmd;
|
||||
import com.red.circle.console.app.service.app.activity.ResidentCpActivityService;
|
||||
import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
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.RestController;
|
||||
|
||||
/**
|
||||
* 常驻 CP 后台接口.
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/resident-activity/cp")
|
||||
public class ResidentCpActivityRestController {
|
||||
|
||||
private final ResidentCpActivityService residentCpActivityService;
|
||||
|
||||
@GetMapping("/config")
|
||||
public ResidentCpConfigCO getConfig(String sysOrigin) {
|
||||
return residentCpActivityService.getConfig(sysOrigin);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog(value = "保存 CP 等级积分配置")
|
||||
@PostMapping("/config/save")
|
||||
public void saveConfig(@RequestBody @Validated ResidentCpConfigSaveCmd cmd) {
|
||||
residentCpActivityService.saveConfig(cmd);
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,8 @@ package com.red.circle.console.adapter.app.order;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.order.InAppPurchaseDetailsCO;
|
||||
import com.red.circle.console.app.service.app.order.InAppPurchaseBackService;
|
||||
import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.order.inner.model.cmd.inapp.InAppPurchaseDetailsQryCmd;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -53,14 +50,4 @@ public class InAppPurchaseRestController extends BaseController {
|
||||
return inAppPurchaseBackService.statistics(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单详情导出.
|
||||
*/
|
||||
@OpsOperationReqLog(value = "导出-订单详情", ignoreRequestParam = true)
|
||||
@GetMapping("/details/export")
|
||||
public void exportDetails(HttpServletResponse response, InAppPurchaseDetailsQryCmd query)
|
||||
throws IOException {
|
||||
inAppPurchaseBackService.exportDetails(response, query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,9 +6,7 @@ import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
@ -118,14 +116,5 @@ public class SysPayOpenCountryController extends BaseController {
|
||||
sysPayCountryService.shelf(id, shelf);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步全球支付国家货币和实时汇率.
|
||||
*/
|
||||
@OpsOperationReqLog("同步全球支付国家货币汇率")
|
||||
@PostMapping("/sync-currency-rates")
|
||||
public PayCountryCurrencyRateSyncDTO syncCurrencyRates(
|
||||
@RequestBody @Validated SysPayCountryCurrencyRateSyncCmd cmd) {
|
||||
return sysPayCountryService.syncCurrencyRates(cmd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,19 +1,12 @@
|
||||
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.service.app.team.TeamSalaryBackService;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
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 java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.RestController;
|
||||
|
||||
@ -37,22 +30,4 @@ public class TeamSalaryRestController extends BaseController {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ import com.red.circle.console.app.dto.cmd.datav.CountryDashboardGameMetricQryCmd
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardQryCmd;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardRechargeDetailQryCmd;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardDailyMetricService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardBackfillLockService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardGameMetricService;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardService;
|
||||
import com.red.circle.console.app.service.app.user.DatavService;
|
||||
@ -42,7 +41,6 @@ public class DatavRestController extends BaseController {
|
||||
private final DatavService userExpandService;
|
||||
private final CountryDashboardService countryDashboardService;
|
||||
private final CountryDashboardDailyMetricService countryDashboardDailyMetricService;
|
||||
private final CountryDashboardBackfillLockService countryDashboardBackfillLockService;
|
||||
private final CountryDashboardGameMetricService countryDashboardGameMetricService;
|
||||
|
||||
@Value("${red-circle.country-dashboard.query-enabled:true}")
|
||||
@ -118,21 +116,14 @@ public class DatavRestController extends BaseController {
|
||||
}
|
||||
LocalDate parsedStartDate = LocalDate.parse(startDate);
|
||||
LocalDate parsedEndDate = LocalDate.parse(endDate);
|
||||
Map<String, Object> refreshed = countryDashboardBackfillLockService.call(() -> {
|
||||
List<String> countryTimezones = countryDashboardDailyMetricService.refreshRange(
|
||||
List<String> refreshedTimezones = countryDashboardDailyMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
List<String> gameTimezones = countryDashboardGameMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
return Map.of(
|
||||
"countryStatTimezones", countryTimezones,
|
||||
"gameStatTimezones", gameTimezones);
|
||||
});
|
||||
return Map.of(
|
||||
"status", "success",
|
||||
"startDate", parsedStartDate.toString(),
|
||||
"endDate", parsedEndDate.toString(),
|
||||
"sysOrigin", sysOrigin,
|
||||
"statTimezones", refreshed);
|
||||
"statTimezones", refreshedTimezones);
|
||||
}
|
||||
|
||||
@PostMapping("/country-dashboard/game-metrics/backfill")
|
||||
@ -146,21 +137,14 @@ public class DatavRestController extends BaseController {
|
||||
}
|
||||
LocalDate parsedStartDate = LocalDate.parse(startDate);
|
||||
LocalDate parsedEndDate = LocalDate.parse(endDate);
|
||||
Map<String, Object> refreshed = countryDashboardBackfillLockService.call(() -> {
|
||||
List<String> countryTimezones = countryDashboardDailyMetricService.refreshRange(
|
||||
List<String> refreshedTimezones = countryDashboardGameMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
List<String> gameTimezones = countryDashboardGameMetricService.refreshRange(
|
||||
parsedStartDate, parsedEndDate, sysOrigin, statTimezones);
|
||||
return Map.of(
|
||||
"countryStatTimezones", countryTimezones,
|
||||
"gameStatTimezones", gameTimezones);
|
||||
});
|
||||
return Map.of(
|
||||
"status", "success",
|
||||
"startDate", parsedStartDate.toString(),
|
||||
"endDate", parsedEndDate.toString(),
|
||||
"sysOrigin", sysOrigin,
|
||||
"statTimezones", refreshed);
|
||||
"statTimezones", refreshedTimezones);
|
||||
}
|
||||
|
||||
private CountryDashboardCO emptyCountryDashboard(CountryDashboardQryCmd cmd) {
|
||||
|
||||
@ -141,22 +141,15 @@ public class UserFreightRestController extends BaseController {
|
||||
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
String fileName = URLEncoder.encode("Export Freight RunningWater " + getExportNameSuffix(query),
|
||||
String fileName = URLEncoder.encode("Export Freight RunningWater " + query.getMonthDate(),
|
||||
StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
String sheetName = URLEncoder.encode("Export Freight RunningWater " + query.getMonthDate(),
|
||||
StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
EasyExcel.write(response.getOutputStream(), UserFreightBalanceRunningWaterExportModel.class)
|
||||
.sheet("FreightRunningWater").doWrite(userFreightService.listExport(query));
|
||||
}
|
||||
|
||||
private String getExportNameSuffix(UserFreightBalanceRunningWaterExportQryCmd query) {
|
||||
if (query.getMonthDate() != null && !query.getMonthDate().isBlank()) {
|
||||
return query.getMonthDate();
|
||||
}
|
||||
if (query.getStartTime() != null || query.getEndTime() != null) {
|
||||
return query.getStartTime() + "-" + query.getEndTime();
|
||||
}
|
||||
return "Filtered";
|
||||
.sheet(sheetName).doWrite(userFreightService.listExport(query));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -11,8 +11,6 @@ import com.red.circle.wallet.inner.model.cmd.DeductGoldCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.DiamondCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.SendGoldCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.UserGoldRunningWaterBackQryCmd;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -54,16 +52,6 @@ public class UserWalletRestController extends BaseController {
|
||||
return userWalletService.getGoldRunningWaterCls(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 流水导出.
|
||||
*/
|
||||
@GetMapping("/gold/running-water/cls/export")
|
||||
public void exportGoldRunningWaterCls(
|
||||
HttpServletResponse response,
|
||||
@Validated UserGoldRunningWaterBackQryCmd query) throws IOException {
|
||||
userWalletService.exportGoldRunningWater(response, query);
|
||||
}
|
||||
|
||||
@OpsOperationReqLog("发送金币")
|
||||
@PostMapping("/send-gold")
|
||||
public void sendGold(@RequestBody @Validated SendGoldCmd param) {
|
||||
|
||||
@ -29,12 +29,6 @@
|
||||
<version>${easyexcel.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<parent>
|
||||
|
||||
@ -94,12 +94,6 @@ public class UserFreightBalanceRunningWaterExportModel implements Serializable {
|
||||
@ExcelProperty(value = "Origin Name")
|
||||
private String originName;
|
||||
|
||||
/**
|
||||
* 操作人.
|
||||
*/
|
||||
@ExcelProperty(value = "Operator")
|
||||
private String operationUserNickname;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
package com.red.circle.console.app.excel.model.order;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
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 InAppPurchaseDetailsExportModel implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty(value = "Env")
|
||||
private String env;
|
||||
|
||||
@ExcelProperty(value = "Sys Origin")
|
||||
private String sysOrigin;
|
||||
|
||||
@ExcelProperty(value = "Platform")
|
||||
private String platform;
|
||||
|
||||
@ExcelProperty(value = "Factory")
|
||||
private String factoryCode;
|
||||
|
||||
@ExcelProperty(value = "Channel")
|
||||
private String channel;
|
||||
|
||||
@ExcelProperty(value = "Buyer User ID")
|
||||
private String buyerUserId;
|
||||
|
||||
@ExcelProperty(value = "Buyer Account")
|
||||
private String buyerAccount;
|
||||
|
||||
@ExcelProperty(value = "Buyer Nickname")
|
||||
private String buyerNickname;
|
||||
|
||||
@ExcelProperty(value = "Accept User ID")
|
||||
private String acceptUserId;
|
||||
|
||||
@ExcelProperty(value = "Accept Account")
|
||||
private String acceptAccount;
|
||||
|
||||
@ExcelProperty(value = "Accept Nickname")
|
||||
private String acceptNickname;
|
||||
|
||||
@ExcelProperty(value = "Amount(USD)")
|
||||
private BigDecimal amountUsd;
|
||||
|
||||
@ExcelProperty(value = "Amount")
|
||||
private BigDecimal amount;
|
||||
|
||||
@ExcelProperty(value = "Currency")
|
||||
private String currency;
|
||||
|
||||
@ExcelProperty(value = "Description")
|
||||
private String productDescriptor;
|
||||
|
||||
@ExcelProperty(value = "Status")
|
||||
private String status;
|
||||
|
||||
@ExcelProperty(value = "Receipt Type")
|
||||
private String receiptType;
|
||||
|
||||
@ExcelProperty(value = "Third Party Order ID")
|
||||
private String orderId;
|
||||
|
||||
@ExcelProperty(value = "App Order ID")
|
||||
private String appOrderId;
|
||||
|
||||
@ExcelProperty(value = "Track ID")
|
||||
private String trackId;
|
||||
|
||||
@ExcelProperty(value = "Country Code")
|
||||
private String countryCode;
|
||||
|
||||
@ExcelProperty(value = "Create Time")
|
||||
private String createTime;
|
||||
|
||||
@ExcelProperty(value = "Update Time")
|
||||
private String updateTime;
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
package com.red.circle.console.app.excel.model.wallet;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
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 UserGoldRunningWaterExportModel implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流水ID.
|
||||
*/
|
||||
@ExcelProperty(value = "Record ID")
|
||||
private String recordId;
|
||||
|
||||
/**
|
||||
* 系统来源.
|
||||
*/
|
||||
@ExcelProperty(value = "Sys Origin")
|
||||
private String sysOrigin;
|
||||
|
||||
/**
|
||||
* 用户ID.
|
||||
*/
|
||||
@ExcelProperty(value = "User ID")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 用户账号.
|
||||
*/
|
||||
@ExcelProperty(value = "User Account")
|
||||
private String userAccount;
|
||||
|
||||
/**
|
||||
* 用户昵称.
|
||||
*/
|
||||
@ExcelProperty(value = "User Nickname")
|
||||
private String userNickname;
|
||||
|
||||
/**
|
||||
* 类型.
|
||||
*/
|
||||
@ExcelProperty(value = "Type")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 金币金额.
|
||||
*/
|
||||
@ExcelProperty(value = "Amount")
|
||||
private BigDecimal quantity;
|
||||
|
||||
/**
|
||||
* 操作后余额.
|
||||
*/
|
||||
@ExcelProperty(value = "Balance")
|
||||
private BigDecimal balance;
|
||||
|
||||
/**
|
||||
* 原因.
|
||||
*/
|
||||
@ExcelProperty(value = "Reason")
|
||||
private String originName;
|
||||
|
||||
/**
|
||||
* 备注.
|
||||
*/
|
||||
@ExcelProperty(value = "Remark")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 操作人.
|
||||
*/
|
||||
@ExcelProperty(value = "Operator")
|
||||
private String backOperationName;
|
||||
|
||||
/**
|
||||
* 操作时间.
|
||||
*/
|
||||
@ExcelProperty(value = "Create Time")
|
||||
private String createTime;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.red.circle.console.app.scheduler;
|
||||
|
||||
import com.red.circle.component.redis.annotation.TaskCacheLock;
|
||||
import com.red.circle.console.app.service.app.datav.CountryDashboardDailyMetricService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 国家数据大屏活跃和留存增量聚合任务.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(
|
||||
name = "red-circle.country-dashboard.active-retention-refresh-enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
@RequiredArgsConstructor
|
||||
public class CountryDashboardActiveRetentionMetricTask {
|
||||
|
||||
private final CountryDashboardDailyMetricService countryDashboardDailyMetricService;
|
||||
|
||||
@Value("${red-circle.country-dashboard.active-retention-refresh-days:32}")
|
||||
private int refreshDays;
|
||||
|
||||
@Scheduled(cron = "${red-circle.country-dashboard.active-retention-refresh-cron:0 */10 * * * ?}",
|
||||
zone = "Asia/Shanghai")
|
||||
@TaskCacheLock(key = "COUNTRY_DASHBOARD_ACTIVE_RETENTION_REFRESH", expireSecond = 60 * 8)
|
||||
public void refreshActiveRetention() {
|
||||
long start = System.currentTimeMillis();
|
||||
countryDashboardDailyMetricService.refreshActiveRetentionRecentDays(refreshDays);
|
||||
log.info("country dashboard active retention refresh task finished, days={}, costMs={}",
|
||||
refreshDays, System.currentTimeMillis() - start);
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,6 @@ import com.red.circle.console.app.service.app.datav.CountryDashboardGameMetricSe
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -15,7 +14,6 @@ import org.springframework.stereotype.Component;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "red-circle.country-dashboard.refresh-enabled", havingValue = "true")
|
||||
@RequiredArgsConstructor
|
||||
public class CountryDashboardDailyMetricTask {
|
||||
|
||||
|
||||
@ -1,286 +0,0 @@
|
||||
package com.red.circle.console.app.service.app.activity;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpConfigCO;
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpLevelConfigCO;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpConfigSaveCmd;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpLevelConfigSaveCmd;
|
||||
import com.red.circle.console.app.service.app.sys.CpCabinConfigService;
|
||||
import com.red.circle.console.app.service.app.sys.SysEnumConfigService;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.response.ResponseErrorCode;
|
||||
import com.red.circle.framework.dto.PageQuery;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysCpCabinConfigQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.sys.EnumConfigDTO;
|
||||
import com.red.circle.other.inner.model.dto.sys.SysCpCabinConfigDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 常驻 CP 配置服务实现.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ResidentCpActivityServiceImpl implements ResidentCpActivityService {
|
||||
|
||||
private static final int CP_LEVEL_COUNT = 6;
|
||||
private static final int CP_MIN_LEVEL = 0;
|
||||
private static final int QUERY_LIMIT = 50;
|
||||
private static final String CONFIG_GROUP_NAME = "CP";
|
||||
private static final String CONFIG_DATA_TYPE = "int";
|
||||
private static final String RELATION_TYPE_CP = "CP";
|
||||
private static final String RELATION_TYPE_BROTHER = "BROTHER";
|
||||
private static final String RELATION_TYPE_SISTERS = "SISTERS";
|
||||
|
||||
private final CpCabinConfigService cpCabinConfigService;
|
||||
private final SysEnumConfigService sysEnumConfigService;
|
||||
|
||||
@Override
|
||||
public ResidentCpConfigCO getConfig(String sysOrigin) {
|
||||
String normalizedSysOrigin = normalizeSysOrigin(sysOrigin);
|
||||
List<SysCpCabinConfigDTO> configs = listCpCabinConfigs(normalizedSysOrigin);
|
||||
List<ResidentCpLevelConfigCO> levels = new ArrayList<>(CP_LEVEL_COUNT);
|
||||
for (int level = CP_MIN_LEVEL; level < CP_LEVEL_COUNT; level++) {
|
||||
SysCpCabinConfigDTO config = level < configs.size() ? configs.get(level) : null;
|
||||
levels.add(toLevelCO(level, config));
|
||||
}
|
||||
return new ResidentCpConfigCO()
|
||||
.setConfigured(!configs.isEmpty())
|
||||
.setSysOrigin(normalizedSysOrigin)
|
||||
.setDismissConsumeGolds(getDismissConsumeGolds(normalizedSysOrigin))
|
||||
.setLevels(levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig(ResidentCpConfigSaveCmd cmd) {
|
||||
validateConfig(cmd);
|
||||
String normalizedSysOrigin = normalizeSysOrigin(cmd.getSysOrigin());
|
||||
List<SysCpCabinConfigDTO> configs = listCpCabinConfigs(normalizedSysOrigin);
|
||||
Map<Long, SysCpCabinConfigDTO> configById = configs.stream()
|
||||
.filter(item -> Objects.nonNull(item.getId()))
|
||||
.collect(Collectors.toMap(SysCpCabinConfigDTO::getId, Function.identity(), (left, right) -> left));
|
||||
|
||||
for (ResidentCpLevelConfigSaveCmd levelCmd : sortedLevels(cmd.getLevels())) {
|
||||
SysCpCabinConfigDTO config = Optional.ofNullable(levelCmd.getCabinConfigId())
|
||||
.map(configById::get)
|
||||
.orElseGet(() -> levelCmd.getLevel() < configs.size()
|
||||
? configs.get(levelCmd.getLevel())
|
||||
: null);
|
||||
boolean exists = Objects.nonNull(config) && Objects.nonNull(config.getId());
|
||||
if (!exists) {
|
||||
config = new SysCpCabinConfigDTO();
|
||||
}
|
||||
config.setSysOrigin(normalizedSysOrigin)
|
||||
.setName(defaultLevelName(levelCmd.getLevel(), levelCmd.getLevelName()))
|
||||
.setCabinUnlockValue(defaultRequiredValue(levelCmd.getRequiredIntimacyValue()))
|
||||
.setSort(levelCmd.getLevel().longValue());
|
||||
|
||||
if (exists) {
|
||||
cpCabinConfigService.updateCpCabin(config);
|
||||
} else {
|
||||
cpCabinConfigService.addCpCabin(config);
|
||||
}
|
||||
}
|
||||
saveDismissConsumeGolds(normalizedSysOrigin, cmd.getDismissConsumeGolds());
|
||||
}
|
||||
|
||||
private List<SysCpCabinConfigDTO> listCpCabinConfigs(String sysOrigin) {
|
||||
SysCpCabinConfigQryCmd query = new SysCpCabinConfigQryCmd().setSysOrigin(sysOrigin);
|
||||
PageQuery pageQuery = new PageQuery();
|
||||
pageQuery.setCursor(1);
|
||||
pageQuery.setLimit(QUERY_LIMIT);
|
||||
pageQuery.setSearchCount(false);
|
||||
query.setPageQuery(pageQuery);
|
||||
PageResult<SysCpCabinConfigDTO> result = cpCabinConfigService.getCpCabin(query);
|
||||
return Optional.ofNullable(result.getRecords())
|
||||
.stream()
|
||||
.flatMap(java.util.Collection::stream)
|
||||
.sorted(Comparator
|
||||
.comparing((SysCpCabinConfigDTO item) -> Optional.ofNullable(item.getSort())
|
||||
.orElse(Long.MAX_VALUE))
|
||||
.thenComparing(item -> Optional.ofNullable(item.getCabinUnlockValue())
|
||||
.orElse(Long.MAX_VALUE))
|
||||
.thenComparing(item -> Optional.ofNullable(item.getId()).orElse(Long.MAX_VALUE)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private ResidentCpLevelConfigCO toLevelCO(int level, SysCpCabinConfigDTO config) {
|
||||
return new ResidentCpLevelConfigCO()
|
||||
.setLevel(level)
|
||||
.setLevelName(Objects.isNull(config)
|
||||
? defaultLevelName(level, null)
|
||||
: defaultLevelName(level, config.getName()))
|
||||
.setRequiredIntimacyValue(Objects.isNull(config)
|
||||
? 0L
|
||||
: defaultRequiredValue(config.getCabinUnlockValue()))
|
||||
.setCabinConfigId(Objects.isNull(config) ? null : config.getId());
|
||||
}
|
||||
|
||||
private void validateConfig(ResidentCpConfigSaveCmd cmd) {
|
||||
ResponseAssert.notBlank(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getSysOrigin());
|
||||
List<ResidentCpLevelConfigSaveCmd> levels = sortedLevels(cmd.getLevels());
|
||||
Set<Integer> levelSet = new HashSet<>();
|
||||
long previousRequiredValue = -1L;
|
||||
for (ResidentCpLevelConfigSaveCmd level : levels) {
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"CP level must be between 0 and 5.",
|
||||
Objects.nonNull(level.getLevel()) && level.getLevel() >= CP_MIN_LEVEL
|
||||
&& level.getLevel() < CP_LEVEL_COUNT && levelSet.add(level.getLevel()));
|
||||
long requiredValue = defaultRequiredValue(level.getRequiredIntimacyValue());
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"requiredIntimacyValue must be greater than or equal to zero.", requiredValue >= 0);
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"requiredIntimacyValue must be ascending by level.",
|
||||
requiredValue >= previousRequiredValue);
|
||||
previousRequiredValue = requiredValue;
|
||||
}
|
||||
validateDismissConsumeGolds(cmd.getDismissConsumeGolds());
|
||||
}
|
||||
|
||||
private Map<String, Long> getDismissConsumeGolds(String sysOrigin) {
|
||||
Map<String, Long> result = defaultDismissConsumeGolds();
|
||||
dismissConsumeGoldKeys().forEach((relationType, key) -> result.put(relationType,
|
||||
enumConfigGoldValue(sysEnumConfigService.getByCode(key.name(), sysOrigin))));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void saveDismissConsumeGolds(String sysOrigin, Map<String, Long> dismissConsumeGolds) {
|
||||
if (Objects.isNull(dismissConsumeGolds)) {
|
||||
return;
|
||||
}
|
||||
dismissConsumeGoldKeys().forEach((relationType, key) -> {
|
||||
if (dismissConsumeGolds.containsKey(relationType)) {
|
||||
saveDismissConsumeGold(sysOrigin, key, dismissConsumeGolds.get(relationType));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveDismissConsumeGold(String sysOrigin, EnumConfigKey key, Long value) {
|
||||
Long normalizedValue = defaultGold(value);
|
||||
EnumConfigDTO enumConfig = sysEnumConfigService.getByCode(key.name(), sysOrigin);
|
||||
boolean exists = Objects.nonNull(enumConfig) && Objects.nonNull(enumConfig.getId());
|
||||
if (!exists) {
|
||||
enumConfig = new EnumConfigDTO();
|
||||
}
|
||||
enumConfig.setSysOrigin(sysOrigin)
|
||||
.setName(key.name())
|
||||
.setTitle(dismissConsumeGoldTitle(key))
|
||||
.setVal(String.valueOf(normalizedValue))
|
||||
.setDescription(dismissConsumeGoldTitle(key))
|
||||
.setDataType(CONFIG_DATA_TYPE)
|
||||
.setReturnApp(Boolean.FALSE)
|
||||
.setGroupName(CONFIG_GROUP_NAME);
|
||||
if (exists) {
|
||||
sysEnumConfigService.updateSysEnumConfig(enumConfig);
|
||||
} else {
|
||||
sysEnumConfigService.saveSysEnumConfig(enumConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDismissConsumeGolds(Map<String, Long> dismissConsumeGolds) {
|
||||
if (Objects.isNull(dismissConsumeGolds)) {
|
||||
return;
|
||||
}
|
||||
dismissConsumeGolds.forEach((relationType, gold) -> {
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"relationType is not supported.",
|
||||
dismissConsumeGoldKeys().containsKey(normalizeRelationType(relationType)));
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
"dismissConsumeGold must be greater than or equal to zero.",
|
||||
defaultGold(gold) >= 0);
|
||||
});
|
||||
}
|
||||
|
||||
private Long enumConfigGoldValue(EnumConfigDTO enumConfig) {
|
||||
if (Objects.isNull(enumConfig) || StringUtils.isBlank(enumConfig.getVal())) {
|
||||
return 0L;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(enumConfig.getVal()).longValue();
|
||||
} catch (NumberFormatException ex) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Long> defaultDismissConsumeGolds() {
|
||||
Map<String, Long> result = new LinkedHashMap<>();
|
||||
dismissConsumeGoldKeys().keySet().forEach(relationType -> result.put(relationType, 0L));
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, EnumConfigKey> dismissConsumeGoldKeys() {
|
||||
Map<String, EnumConfigKey> result = new LinkedHashMap<>();
|
||||
result.put(RELATION_TYPE_CP, EnumConfigKey.CP_DISMISS_CONSUME_GOLD);
|
||||
result.put(RELATION_TYPE_BROTHER, EnumConfigKey.BROTHER_DISMISS_CONSUME_GOLD);
|
||||
result.put(RELATION_TYPE_SISTERS, EnumConfigKey.SISTERS_DISMISS_CONSUME_GOLD);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String dismissConsumeGoldTitle(EnumConfigKey key) {
|
||||
return switch (key) {
|
||||
case CP_DISMISS_CONSUME_GOLD -> "CP 解绑金币";
|
||||
case BROTHER_DISMISS_CONSUME_GOLD -> "Brother 解绑金币";
|
||||
case SISTERS_DISMISS_CONSUME_GOLD -> "Sisters 解绑金币";
|
||||
default -> "关系解绑金币";
|
||||
};
|
||||
}
|
||||
|
||||
private List<ResidentCpLevelConfigSaveCmd> sortedLevels(
|
||||
List<ResidentCpLevelConfigSaveCmd> levels) {
|
||||
if (Objects.isNull(levels)) {
|
||||
return List.of();
|
||||
}
|
||||
return levels.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(ResidentCpLevelConfigSaveCmd::getLevel,
|
||||
Comparator.nullsLast(Integer::compareTo)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String normalizeSysOrigin(String sysOrigin) {
|
||||
return Objects.toString(sysOrigin, "").trim().toUpperCase();
|
||||
}
|
||||
|
||||
private Long defaultRequiredValue(Long requiredValue) {
|
||||
return requiredValue == null ? 0L : requiredValue;
|
||||
}
|
||||
|
||||
private Long defaultGold(Long gold) {
|
||||
return gold == null ? 0L : gold;
|
||||
}
|
||||
|
||||
private String normalizeRelationType(String relationType) {
|
||||
if (StringUtils.isBlank(relationType)) {
|
||||
return "";
|
||||
}
|
||||
String normalized = relationType.trim().toUpperCase();
|
||||
if (Objects.equals(normalized, "BROTHERS")) {
|
||||
return RELATION_TYPE_BROTHER;
|
||||
}
|
||||
if (Objects.equals(normalized, "SISTER")) {
|
||||
return RELATION_TYPE_SISTERS;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String defaultLevelName(Integer level, String levelName) {
|
||||
if (StringUtils.isNotBlank(levelName)) {
|
||||
return levelName.trim();
|
||||
}
|
||||
return "CP " + level + "级";
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
package com.red.circle.console.app.service.app.datav;
|
||||
|
||||
import com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Coordinates dashboard backfill and CDC worker writes through a MySQL named lock.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CountryDashboardBackfillLockService {
|
||||
|
||||
private static final String LOCK_NAME = "country_dashboard_backfill_lock";
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
|
||||
@Value("${red-circle.country-dashboard.backfill-lock-timeout-seconds:10}")
|
||||
private Integer lockTimeoutSeconds;
|
||||
|
||||
public <T> T call(Supplier<T> supplier) {
|
||||
Integer acquired = countryDashboardDAO.getDashboardBackfillLock(
|
||||
LOCK_NAME, Math.max(Objects.requireNonNullElse(lockTimeoutSeconds, 10), 0));
|
||||
if (!Objects.equals(acquired, 1)) {
|
||||
throw new IllegalStateException("country dashboard backfill lock is held by CDC worker");
|
||||
}
|
||||
try {
|
||||
return supplier.get();
|
||||
} finally {
|
||||
try {
|
||||
countryDashboardDAO.releaseDashboardBackfillLock(LOCK_NAME);
|
||||
} catch (Exception ex) {
|
||||
log.warn("release country dashboard backfill lock failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,10 +5,7 @@ import com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardDailyMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardPeriodMetricDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO;
|
||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
@ -20,7 +17,6 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -28,17 +24,9 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
@ -61,18 +49,14 @@ public class CountryDashboardDailyMetricService {
|
||||
private static final ZoneId STORAGE_ZONE_ID = ZoneId.of("Asia/Riyadh");
|
||||
private static final String STORAGE_TIMEZONE = "Asia/Riyadh";
|
||||
private static final String STORAGE_TIMEZONE_OFFSET = "+03:00";
|
||||
private static final String COLLECTION_USER_DAILY_ACTIVE_LOG = "user_daily_active_log";
|
||||
private static final String COLLECTION_GIFT_GIVE_RUNNING_WATER = "gift_give_running_water";
|
||||
private static final int USER_BATCH_SIZE = 1000;
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Value("${red-circle.country-dashboard.refresh-sys-origins:LIKEI}")
|
||||
private String refreshSysOrigins;
|
||||
|
||||
@Value("${red-circle.country-dashboard.refresh-timezones:Asia/Riyadh}")
|
||||
@Value("${red-circle.country-dashboard.refresh-timezones:Asia/Riyadh,UTC,Asia/Shanghai}")
|
||||
private String refreshTimezones;
|
||||
|
||||
private volatile boolean metricSchemaEnsured;
|
||||
@ -114,39 +98,6 @@ public class CountryDashboardDailyMetricService {
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshActiveRetentionRecentDays(int days) {
|
||||
ensureMetricSchema();
|
||||
int safeDays = Math.max(days, 1);
|
||||
List<String> sysOrigins = resolveSysOrigins();
|
||||
List<StatTimezone> statTimezones = resolveStatTimezones(null);
|
||||
for (String sysOrigin : sysOrigins) {
|
||||
for (StatTimezone statTimezone : statTimezones) {
|
||||
List<LocalDate> statDates = recentDates(safeDays, statTimezone.zoneId());
|
||||
for (LocalDate statDate : statDates) {
|
||||
try {
|
||||
refreshActiveRetentionPeriod(toDayWindow(statDate), sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard active retention day failed, statDate={}, sysOrigin={}, statTimezone={}",
|
||||
statDate, sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
Set<PeriodWindow> windows = new LinkedHashSet<>();
|
||||
for (LocalDate statDate : statDates) {
|
||||
windows.add(toWeekWindow(statDate, statTimezone.zoneId()));
|
||||
windows.add(toMonthWindow(statDate, statTimezone.zoneId()));
|
||||
}
|
||||
for (PeriodWindow window : windows) {
|
||||
try {
|
||||
refreshActiveRetentionPeriod(window, sysOrigin, statTimezone);
|
||||
} catch (Exception ex) {
|
||||
log.error("refresh country dashboard active retention period failed, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshRange(LocalDate startDate, LocalDate endDate, String sysOrigin) {
|
||||
refreshRange(startDate, endDate, sysOrigin, null);
|
||||
}
|
||||
@ -208,8 +159,6 @@ public class CountryDashboardDailyMetricService {
|
||||
countryDashboardDAO.createPeriodMetricTable();
|
||||
countryDashboardDAO.createPeriodUserMetricTable();
|
||||
countryDashboardDAO.ensurePeriodMetricTimezoneSchema();
|
||||
countryDashboardDAO.ensurePeriodMetricUserColumnsSchema();
|
||||
countryDashboardDAO.ensureMetricValueColumnsSchema();
|
||||
metricSchemaEnsured = true;
|
||||
log.info("country dashboard metric tables ensured");
|
||||
}
|
||||
@ -266,14 +215,6 @@ public class CountryDashboardDailyMetricService {
|
||||
PERIOD_DAY, startTime, endTime, startTimeMillis, endTimeMillis, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, STORAGE_TIMEZONE_OFFSET),
|
||||
statDate, sysOrigin);
|
||||
TimeWindow timeWindow = new TimeWindow(startTime, endTime, startTimeMillis, endTimeMillis,
|
||||
statDate.atStartOfDay(STORAGE_ZONE_ID).toInstant(),
|
||||
statDate.plusDays(1).atStartOfDay(STORAGE_ZONE_ID).toInstant());
|
||||
mergeDailyMongo(rows, listGiftConsume(timeWindow, sysOrigin, STORAGE_TIMEZONE),
|
||||
(row, amount) -> row.setGiftConsume(add(row.getGiftConsume(), amount)), statDate, sysOrigin);
|
||||
mergeDailyMongo(rows, listLuckyGiftAnchorShare(timeWindow, sysOrigin, STORAGE_TIMEZONE),
|
||||
(row, amount) -> row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), amount)),
|
||||
statDate, sysOrigin);
|
||||
|
||||
List<CountryDashboardDailyMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
@ -343,15 +284,6 @@ public class CountryDashboardDailyMetricService {
|
||||
mergePeriodAmounts(rows, countryDashboardDAO.listLuckyBoxGame(
|
||||
window.periodType(), startTime, endTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset()), window, sysOrigin, statTimezone);
|
||||
mergePeriodMongo(rows, listGiftConsume(timeWindow, sysOrigin, statTimezone.id()),
|
||||
(row, amount) -> row.setGiftConsume(add(row.getGiftConsume(), amount)),
|
||||
window, sysOrigin, statTimezone);
|
||||
mergePeriodMongo(rows, listLuckyGiftAnchorShare(timeWindow, sysOrigin, statTimezone.id()),
|
||||
(row, amount) -> row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), amount)),
|
||||
window, sysOrigin, statTimezone);
|
||||
mergePeriodDailyActive(rows, listDailyActive(window, sysOrigin), window, sysOrigin,
|
||||
statTimezone);
|
||||
mergePeriodRetention(rows, window, sysOrigin, statTimezone, startTime, endTime);
|
||||
|
||||
RefreshWriteResult writeResult = transactionTemplate.execute(status -> {
|
||||
countryDashboardDAO.deletePeriodUserMetrics(window.periodType(), window.periodKey(),
|
||||
@ -385,24 +317,6 @@ public class CountryDashboardDailyMetricService {
|
||||
System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private void refreshActiveRetentionPeriod(PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
TimeWindow timeWindow = toStorageTimeWindow(window, statTimezone);
|
||||
Map<String, CountryDashboardPeriodMetricDTO> rows = new LinkedHashMap<>();
|
||||
mergePeriodDailyActive(rows, listDailyActive(window, sysOrigin), window, sysOrigin,
|
||||
statTimezone);
|
||||
mergePeriodRetention(rows, window, sysOrigin, statTimezone, timeWindow.startTime(),
|
||||
timeWindow.endTime());
|
||||
List<CountryDashboardPeriodMetricDTO> metrics = new ArrayList<>(rows.values());
|
||||
int upserted = metrics.isEmpty() ? 0
|
||||
: transactionTemplate.execute(status ->
|
||||
countryDashboardDAO.upsertPeriodActiveRetentionMetrics(metrics));
|
||||
log.info("refresh country dashboard active retention success, periodType={}, periodKey={}, sysOrigin={}, statTimezone={}, countries={}, upserted={}, costMs={}",
|
||||
window.periodType(), window.periodKey(), sysOrigin, statTimezone.id(), metrics.size(),
|
||||
upserted, System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private void refreshAll(String sysOrigin, StatTimezone statTimezone) {
|
||||
long start = System.currentTimeMillis();
|
||||
PeriodWindow window = new PeriodWindow(PERIOD_ALL, PERIOD_ALL, "全部", null, null);
|
||||
@ -438,20 +352,15 @@ public class CountryDashboardDailyMetricService {
|
||||
for (CountryDashboardMetricDTO metric : metrics) {
|
||||
CountryDashboardDailyMetricDTO row = getRow(rows, metric, statDate, sysOrigin);
|
||||
row.setCountryNewUser(safeLong(row.getCountryNewUser()) + safeLong(metric.getCountryNewUser()));
|
||||
row.setDailyRechargeUser(safeLong(row.getDailyRechargeUser()) + safeLong(metric.getDailyRechargeUser()));
|
||||
row.setNewUserRecharge(add(row.getNewUserRecharge(), metric.getNewUserRecharge()));
|
||||
row.setOfficialRecharge(add(row.getOfficialRecharge(), metric.getOfficialRecharge()));
|
||||
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
|
||||
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
|
||||
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
|
||||
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
|
||||
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
|
||||
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
|
||||
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
|
||||
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
|
||||
row.setLuckyGiftUser(safeLong(row.getLuckyGiftUser()) + safeLong(metric.getLuckyGiftUser()));
|
||||
row.setLuckyGiftPayout(add(row.getLuckyGiftPayout(), metric.getLuckyGiftPayout()));
|
||||
row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), metric.getLuckyGiftAnchorShare()));
|
||||
row.setGameTotalFlow(add(row.getGameTotalFlow(), metric.getGameTotalFlow()));
|
||||
row.setGameUser(safeLong(row.getGameUser()) + safeLong(metric.getGameUser()));
|
||||
row.setGamePayout(add(row.getGamePayout(), metric.getGamePayout()));
|
||||
@ -472,13 +381,9 @@ public class CountryDashboardDailyMetricService {
|
||||
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
|
||||
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
|
||||
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
|
||||
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
|
||||
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
|
||||
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
|
||||
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
|
||||
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
|
||||
row.setLuckyGiftPayout(add(row.getLuckyGiftPayout(), metric.getLuckyGiftPayout()));
|
||||
row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), metric.getLuckyGiftAnchorShare()));
|
||||
row.setGameTotalFlow(add(row.getGameTotalFlow(), metric.getGameTotalFlow()));
|
||||
row.setGamePayout(add(row.getGamePayout(), metric.getGamePayout()));
|
||||
}
|
||||
@ -494,302 +399,11 @@ public class CountryDashboardDailyMetricService {
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
|
||||
statTimezone);
|
||||
row.setCountryNewUser(safeLong(metric.getCountryNewUser()));
|
||||
row.setDailyRechargeUser(safeLong(metric.getDailyRechargeUser()));
|
||||
row.setLuckyGiftUser(safeLong(metric.getLuckyGiftUser()));
|
||||
row.setGameUser(safeLong(metric.getGameUser()));
|
||||
}
|
||||
}
|
||||
|
||||
private void mergePeriodDailyActive(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
List<CountryDashboardMetricDTO> metrics, PeriodWindow window, String sysOrigin,
|
||||
StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (CountryDashboardMetricDTO metric : metrics) {
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
|
||||
statTimezone);
|
||||
row.setDailyActiveUser(safeLong(row.getDailyActiveUser()) + safeLong(metric.getDailyActiveUser()));
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeDailyMongo(Map<String, CountryDashboardDailyMetricDTO> rows,
|
||||
List<MongoMetric> metrics, BiConsumer<CountryDashboardDailyMetricDTO, BigDecimal> merger,
|
||||
LocalDate statDate, String sysOrigin) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, CountryDashboardUserCountryDTO> countryMap = mapUserCountries(
|
||||
metrics.stream().map(MongoMetric::userId).filter(Objects::nonNull).distinct().toList());
|
||||
for (MongoMetric metric : metrics) {
|
||||
CountryDashboardUserCountryDTO country = countryMap.get(metric.userId());
|
||||
if (country == null) {
|
||||
continue;
|
||||
}
|
||||
CountryDashboardMetricDTO rowKey = new CountryDashboardMetricDTO()
|
||||
.setCountryCode(country.getCountryCode())
|
||||
.setCountryName(country.getCountryName());
|
||||
merger.accept(getRow(rows, rowKey, statDate, sysOrigin), metric.amount());
|
||||
}
|
||||
}
|
||||
|
||||
private void mergePeriodMongo(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
List<MongoMetric> metrics, BiConsumer<CountryDashboardPeriodMetricDTO, BigDecimal> merger,
|
||||
PeriodWindow window, String sysOrigin, StatTimezone statTimezone) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, CountryDashboardUserCountryDTO> countryMap = mapUserCountries(
|
||||
metrics.stream().map(MongoMetric::userId).filter(Objects::nonNull).distinct().toList());
|
||||
for (MongoMetric metric : metrics) {
|
||||
CountryDashboardUserCountryDTO country = countryMap.get(metric.userId());
|
||||
if (country == null) {
|
||||
continue;
|
||||
}
|
||||
CountryDashboardMetricDTO rowKey = new CountryDashboardMetricDTO()
|
||||
.setCountryCode(country.getCountryCode())
|
||||
.setCountryName(country.getCountryName());
|
||||
merger.accept(getPeriodRow(rows, rowKey, window, sysOrigin, statTimezone), metric.amount());
|
||||
}
|
||||
}
|
||||
|
||||
private void mergePeriodRetention(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
PeriodWindow window, String sysOrigin, StatTimezone statTimezone, LocalDateTime startTime,
|
||||
LocalDateTime endTime) {
|
||||
mergePeriodRetentionDays(rows, window, sysOrigin, statTimezone, startTime.minusDays(1),
|
||||
endTime.minusDays(1), 1);
|
||||
mergePeriodRetentionDays(rows, window, sysOrigin, statTimezone, startTime.minusDays(7),
|
||||
endTime.minusDays(7), 7);
|
||||
mergePeriodRetentionDays(rows, window, sysOrigin, statTimezone, startTime.minusDays(30),
|
||||
endTime.minusDays(30), 30);
|
||||
}
|
||||
|
||||
private void mergePeriodRetentionDays(Map<String, CountryDashboardPeriodMetricDTO> rows,
|
||||
PeriodWindow window, String sysOrigin, StatTimezone statTimezone, LocalDateTime cohortStartTime,
|
||||
LocalDateTime cohortEndTime, int retentionDays) {
|
||||
List<CountryDashboardRetentionUserDTO> cohorts = countryDashboardDAO.listNewUserCohorts(
|
||||
window.periodType(), cohortStartTime, cohortEndTime, null, sysOrigin,
|
||||
STORAGE_TIMEZONE_OFFSET, statTimezone.offset());
|
||||
if (cohorts == null || cohorts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDate today = LocalDate.now(statTimezone.zoneId());
|
||||
Set<Long> userIds = new HashSet<>();
|
||||
Set<String> activeDates = new HashSet<>();
|
||||
for (CountryDashboardRetentionUserDTO cohort : cohorts) {
|
||||
if (cohort.getUserId() == null || !hasText(cohort.getRegisterDate())) {
|
||||
continue;
|
||||
}
|
||||
userIds.add(cohort.getUserId());
|
||||
collectRetentionActiveDate(activeDates, cohort.getRegisterDate(), retentionDays, today);
|
||||
}
|
||||
|
||||
Set<String> activeUserDates = listActiveUserDateSet(userIds, activeDates, sysOrigin);
|
||||
for (CountryDashboardRetentionUserDTO cohort : cohorts) {
|
||||
if (cohort.getUserId() == null || !hasText(cohort.getRegisterDate())) {
|
||||
continue;
|
||||
}
|
||||
CountryDashboardMetricDTO metric = new CountryDashboardMetricDTO()
|
||||
.setCountryCode(cohort.getCountryCode())
|
||||
.setCountryName(cohort.getCountryName());
|
||||
CountryDashboardPeriodMetricDTO row = getPeriodRow(rows, metric, window, sysOrigin,
|
||||
statTimezone);
|
||||
mergeRetentionDay(row, cohort.getUserId(), cohort.getRegisterDate(), retentionDays, today,
|
||||
activeUserDates);
|
||||
}
|
||||
}
|
||||
|
||||
private List<CountryDashboardMetricDTO> listDailyActive(PeriodWindow window, String sysOrigin) {
|
||||
if (window.periodStartDate() == null || window.periodEndDate() == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<Criteria> criteria = new ArrayList<>();
|
||||
criteria.add(Criteria.where("activeDate")
|
||||
.gte(window.periodStartDate().format(DATE_FORMATTER))
|
||||
.lt(window.periodEndDate().format(DATE_FORMATTER)));
|
||||
if (hasText(sysOrigin)) {
|
||||
criteria.add(Criteria.where("sysOrigin").is(sysOrigin));
|
||||
}
|
||||
|
||||
List<AggregationOperation> operations = new ArrayList<>();
|
||||
operations.add(Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))));
|
||||
operations.add(Aggregation.group("registerCountryCode", "userId"));
|
||||
operations.add(Aggregation.group("_id.registerCountryCode").count().as("dailyActiveUser"));
|
||||
|
||||
return mongoTemplate.aggregate(Aggregation.newAggregation(operations),
|
||||
COLLECTION_USER_DAILY_ACTIVE_LOG, Document.class)
|
||||
.getMappedResults()
|
||||
.stream()
|
||||
.map(this::toDailyActiveMetric)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private CountryDashboardMetricDTO toDailyActiveMetric(Document document) {
|
||||
String countryCode = normalizeCountryCode(document.get("_id"));
|
||||
Long dailyActiveUser = toLong(document.get("dailyActiveUser"));
|
||||
if (dailyActiveUser == null || dailyActiveUser <= 0) {
|
||||
return null;
|
||||
}
|
||||
return new CountryDashboardMetricDTO()
|
||||
.setCountryCode(countryCode)
|
||||
.setCountryName(countryCode)
|
||||
.setDailyActiveUser(dailyActiveUser);
|
||||
}
|
||||
|
||||
private List<MongoMetric> listGiftConsume(TimeWindow timeWindow, String sysOrigin,
|
||||
String statTimezone) {
|
||||
List<Criteria> criteria = baseGiftCriteria(timeWindow, sysOrigin);
|
||||
criteria.add(Criteria.where("luckyGift").ne(Boolean.TRUE));
|
||||
criteria.add(Criteria.where("originId").regex("^\\d+$"));
|
||||
criteria.add(new Criteria().orOperator(
|
||||
Criteria.where("dynamicContentId").exists(false),
|
||||
Criteria.where("dynamicContentId").is(null),
|
||||
Criteria.where("dynamicContentId").is("")
|
||||
));
|
||||
return aggregateGiftAmount(criteria, "$giftValue.actualAmount", false, statTimezone);
|
||||
}
|
||||
|
||||
private List<MongoMetric> listLuckyGiftAnchorShare(TimeWindow timeWindow, String sysOrigin,
|
||||
String statTimezone) {
|
||||
List<Criteria> criteria = baseGiftCriteria(timeWindow, sysOrigin);
|
||||
criteria.add(Criteria.where("luckyGift").is(Boolean.TRUE));
|
||||
return aggregateGiftAmount(criteria, "$acceptUsers.targetAmount", true, statTimezone);
|
||||
}
|
||||
|
||||
private List<Criteria> baseGiftCriteria(TimeWindow timeWindow, String sysOrigin) {
|
||||
List<Criteria> criteria = new ArrayList<>();
|
||||
criteria.add(Criteria.where("refunded").ne(Boolean.TRUE));
|
||||
criteria.add(Criteria.where("userId").ne(null));
|
||||
criteria.add(Criteria.where("createTime").gte(Timestamp.from(timeWindow.startInstant())));
|
||||
criteria.add(Criteria.where("createTime").lt(Timestamp.from(timeWindow.endInstant())));
|
||||
if (hasText(sysOrigin)) {
|
||||
criteria.add(Criteria.where("sysOrigin").is(sysOrigin));
|
||||
}
|
||||
return criteria;
|
||||
}
|
||||
|
||||
private List<MongoMetric> aggregateGiftAmount(List<Criteria> criteria, String amountPath,
|
||||
boolean unwindAcceptUsers, String statTimezone) {
|
||||
List<AggregationOperation> operations = new ArrayList<>();
|
||||
operations.add(Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))));
|
||||
if (unwindAcceptUsers) {
|
||||
operations.add(Aggregation.unwind("acceptUsers"));
|
||||
}
|
||||
operations.add(context -> new Document("$project",
|
||||
new Document("userId", "$userId")
|
||||
.append("amount", amountPath)
|
||||
.append("day", new Document("$dateToString",
|
||||
new Document("format", "%Y-%m-%d")
|
||||
.append("date", "$createTime")
|
||||
.append("timezone", statTimezone)))));
|
||||
operations.add(Aggregation.group("userId", "day").sum("amount").as("amount"));
|
||||
|
||||
return mongoTemplate.aggregate(Aggregation.newAggregation(operations),
|
||||
COLLECTION_GIFT_GIVE_RUNNING_WATER, Document.class)
|
||||
.getMappedResults()
|
||||
.stream()
|
||||
.map(this::toMongoMetric)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private MongoMetric toMongoMetric(Document document) {
|
||||
Object id = document.get("_id");
|
||||
if (!(id instanceof Document idDoc)) {
|
||||
return null;
|
||||
}
|
||||
Long userId = toLong(idDoc.get("userId"));
|
||||
String day = Objects.toString(idDoc.get("day"), null);
|
||||
BigDecimal amount = toBigDecimal(document.get("amount"));
|
||||
if (userId == null || !hasText(day) || amount == null) {
|
||||
return null;
|
||||
}
|
||||
return new MongoMetric(userId, day, amount);
|
||||
}
|
||||
|
||||
private void collectRetentionActiveDate(Set<String> activeDates, String registerDate,
|
||||
int retentionDays, LocalDate today) {
|
||||
LocalDate targetDate = LocalDate.parse(registerDate, DATE_FORMATTER).plusDays(retentionDays);
|
||||
if (!targetDate.isAfter(today)) {
|
||||
activeDates.add(targetDate.format(DATE_FORMATTER));
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeRetentionDay(CountryDashboardPeriodMetricDTO row, Long userId,
|
||||
String registerDate, int retentionDays, LocalDate today, Set<String> activeUserDates) {
|
||||
LocalDate targetDate = LocalDate.parse(registerDate, DATE_FORMATTER).plusDays(retentionDays);
|
||||
if (targetDate.isAfter(today)) {
|
||||
return;
|
||||
}
|
||||
String activeKey = userId + "|" + targetDate.format(DATE_FORMATTER);
|
||||
boolean retained = activeUserDates.contains(activeKey);
|
||||
if (retentionDays == 1) {
|
||||
row.setD1RetentionBaseUser(safeLong(row.getD1RetentionBaseUser()) + 1);
|
||||
row.setD1RetentionUser(safeLong(row.getD1RetentionUser()) + (retained ? 1 : 0));
|
||||
return;
|
||||
}
|
||||
if (retentionDays == 7) {
|
||||
row.setD7RetentionBaseUser(safeLong(row.getD7RetentionBaseUser()) + 1);
|
||||
row.setD7RetentionUser(safeLong(row.getD7RetentionUser()) + (retained ? 1 : 0));
|
||||
return;
|
||||
}
|
||||
row.setD30RetentionBaseUser(safeLong(row.getD30RetentionBaseUser()) + 1);
|
||||
row.setD30RetentionUser(safeLong(row.getD30RetentionUser()) + (retained ? 1 : 0));
|
||||
}
|
||||
|
||||
private Set<String> listActiveUserDateSet(Set<Long> userIds, Set<String> activeDates,
|
||||
String sysOrigin) {
|
||||
Set<String> result = new HashSet<>();
|
||||
if (userIds == null || userIds.isEmpty() || activeDates == null || activeDates.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
List<Long> ids = new ArrayList<>(userIds);
|
||||
for (int start = 0; start < ids.size(); start += USER_BATCH_SIZE) {
|
||||
int end = Math.min(start + USER_BATCH_SIZE, ids.size());
|
||||
List<Criteria> criteria = new ArrayList<>();
|
||||
criteria.add(Criteria.where("userId").in(ids.subList(start, end)));
|
||||
criteria.add(Criteria.where("activeDate").in(activeDates));
|
||||
if (hasText(sysOrigin)) {
|
||||
criteria.add(Criteria.where("sysOrigin").is(sysOrigin));
|
||||
}
|
||||
Query query = Query.query(new Criteria().andOperator(criteria.toArray(new Criteria[0])));
|
||||
query.fields().include("userId").include("activeDate");
|
||||
List<Document> documents = mongoTemplate.find(query, Document.class,
|
||||
COLLECTION_USER_DAILY_ACTIVE_LOG);
|
||||
for (Document document : documents) {
|
||||
Long userId = toLong(document.get("userId"));
|
||||
String activeDate = Objects.toString(document.get("activeDate"), null);
|
||||
if (userId != null && hasText(activeDate)) {
|
||||
result.add(userId + "|" + activeDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<Long, CountryDashboardUserCountryDTO> mapUserCountries(List<Long> userIds) {
|
||||
Map<Long, CountryDashboardUserCountryDTO> result = new LinkedHashMap<>();
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (int start = 0; start < userIds.size(); start += USER_BATCH_SIZE) {
|
||||
int end = Math.min(start + USER_BATCH_SIZE, userIds.size());
|
||||
List<CountryDashboardUserCountryDTO> batch = countryDashboardDAO.listUserCountryByIds(
|
||||
userIds.subList(start, end));
|
||||
if (batch == null) {
|
||||
continue;
|
||||
}
|
||||
for (CountryDashboardUserCountryDTO item : batch) {
|
||||
if (item.getUserId() != null) {
|
||||
result.put(item.getUserId(), item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private CountryDashboardDailyMetricDTO getRow(Map<String, CountryDashboardDailyMetricDTO> rows,
|
||||
CountryDashboardMetricDTO metric, LocalDate statDate, String sysOrigin) {
|
||||
String countryCode = hasText(metric.getCountryCode()) ? metric.getCountryCode() : "UNKNOWN";
|
||||
@ -863,9 +477,7 @@ public class CountryDashboardDailyMetricService {
|
||||
LocalDateTime.ofInstant(startInstant, STORAGE_ZONE_ID),
|
||||
LocalDateTime.ofInstant(endInstant, STORAGE_ZONE_ID),
|
||||
startInstant.toEpochMilli(),
|
||||
endInstant.toEpochMilli(),
|
||||
startInstant,
|
||||
endInstant);
|
||||
endInstant.toEpochMilli());
|
||||
}
|
||||
|
||||
private List<StatTimezone> resolveStatTimezones(String statTimezonesValue) {
|
||||
@ -874,7 +486,7 @@ public class CountryDashboardDailyMetricService {
|
||||
config = trimToNull(refreshTimezones);
|
||||
}
|
||||
if (config == null) {
|
||||
config = "Asia/Riyadh";
|
||||
config = "Asia/Riyadh,UTC,Asia/Shanghai";
|
||||
}
|
||||
|
||||
Map<String, StatTimezone> zones = new LinkedHashMap<>();
|
||||
@ -947,37 +559,6 @@ public class CountryDashboardDailyMetricService {
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
|
||||
private static Long toLong(Object value) {
|
||||
if (value instanceof Number number) {
|
||||
return number.longValue();
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return Long.valueOf(Objects.toString(value));
|
||||
}
|
||||
|
||||
private static BigDecimal toBigDecimal(Object value) {
|
||||
if (value == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
if (value instanceof BigDecimal bigDecimal) {
|
||||
return bigDecimal;
|
||||
}
|
||||
if (value instanceof Decimal128 decimal128) {
|
||||
return decimal128.bigDecimalValue();
|
||||
}
|
||||
if (value instanceof Number number) {
|
||||
return new BigDecimal(number.toString());
|
||||
}
|
||||
return new BigDecimal(Objects.toString(value, "0"));
|
||||
}
|
||||
|
||||
private static String normalizeCountryCode(Object value) {
|
||||
String countryCode = Objects.toString(value, "").trim();
|
||||
return countryCode.isEmpty() ? "UNKNOWN" : countryCode;
|
||||
}
|
||||
|
||||
private static boolean hasText(String value) {
|
||||
return value != null && !value.trim().isEmpty();
|
||||
}
|
||||
@ -998,12 +579,9 @@ public class CountryDashboardDailyMetricService {
|
||||
}
|
||||
|
||||
private record TimeWindow(LocalDateTime startTime, LocalDateTime endTime,
|
||||
Long startTimeMillis, Long endTimeMillis, Instant startInstant, Instant endInstant) {
|
||||
Long startTimeMillis, Long endTimeMillis) {
|
||||
}
|
||||
|
||||
private record RefreshWriteResult(int deleted, int inserted, int userRows) {
|
||||
}
|
||||
|
||||
private record MongoMetric(Long userId, String day, BigDecimal amount) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGa
|
||||
@Value("${red-circle.country-dashboard.refresh-sys-origins:LIKEI}")
|
||||
private String refreshSysOrigins;
|
||||
|
||||
@Value("${red-circle.country-dashboard.refresh-timezones:Asia/Riyadh}")
|
||||
@Value("${red-circle.country-dashboard.refresh-timezones:Asia/Riyadh,UTC,Asia/Shanghai}")
|
||||
private String refreshTimezones;
|
||||
|
||||
private volatile boolean metricSchemaEnsured;
|
||||
@ -478,7 +478,7 @@ public class CountryDashboardGameMetricServiceImpl implements CountryDashboardGa
|
||||
config = trimToNull(refreshTimezones);
|
||||
}
|
||||
if (config == null) {
|
||||
config = "Asia/Riyadh";
|
||||
config = "Asia/Riyadh,UTC,Asia/Shanghai";
|
||||
}
|
||||
|
||||
Map<String, StatTimezone> zones = new LinkedHashMap<>();
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.red.circle.console.app.service.app.datav;
|
||||
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardCO;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardArpuSummaryCO;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardMetricCO;
|
||||
import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardRechargeDetailCO;
|
||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardQryCmd;
|
||||
@ -73,24 +72,33 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
|
||||
private final CountryDashboardDAO countryDashboardDAO;
|
||||
private final MongoTemplate mongoTemplate;
|
||||
private volatile boolean rechargeDetailSchemaEnsured;
|
||||
private volatile boolean periodMetricSchemaEnsured;
|
||||
|
||||
@Override
|
||||
public CountryDashboardCO query(CountryDashboardQryCmd cmd) {
|
||||
ensurePeriodMetricSchema();
|
||||
QueryCondition condition = QueryCondition.of(cmd);
|
||||
CountryDashboardCO dashboard = queryPrecomputed(condition);
|
||||
dashboard.setArpuSummary(buildArpuSummary(condition));
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
private CountryDashboardCO queryPrecomputed(QueryCondition condition) {
|
||||
Map<String, CountryDashboardMetricCO> rows = new LinkedHashMap<>();
|
||||
|
||||
boolean usePrecomputedMetrics = shouldUsePrecomputedMetrics(condition);
|
||||
if (usePrecomputedMetrics) {
|
||||
mergeSql(rows, countryDashboardDAO.listPrecomputedPeriodMetrics(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
condition.countryKeyword, condition.statTimezone, condition.sysOrigin));
|
||||
} else {
|
||||
mergeLiveSql(rows, condition);
|
||||
mergeBankSalaryExchange(rows, condition);
|
||||
}
|
||||
if (usePrecomputedMetrics && condition.shouldOverlayLiveSalaryExchange()) {
|
||||
overlayLiveSalaryExchange(rows, condition);
|
||||
}
|
||||
|
||||
mergeMongo(rows, condition, listGiftConsume(condition),
|
||||
(row, amount) -> row.setGiftConsume(add(row.getGiftConsume(), amount)));
|
||||
mergeMongo(rows, condition, listLuckyGiftAnchorShare(condition),
|
||||
(row, amount) -> row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), amount)));
|
||||
mergeMongo(rows, condition, listBankSalaryTransfer(condition),
|
||||
(row, amount) -> row.setSalaryTransfer(add(row.getSalaryTransfer(), amount)));
|
||||
mergeDailyActive(rows, condition, listDailyActive(condition));
|
||||
mergeRetention(rows, condition);
|
||||
|
||||
List<CountryDashboardMetricCO> records = rows.values().stream()
|
||||
.peek(this::calculateFormulaFields)
|
||||
@ -112,13 +120,6 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
.setRecords(records);
|
||||
}
|
||||
|
||||
private CountryDashboardArpuSummaryCO buildArpuSummary(QueryCondition condition) {
|
||||
return new CountryDashboardArpuSummaryCO()
|
||||
.setDay(queryPrecomputed(condition.recentDays(1)))
|
||||
.setThreeDay(queryPrecomputed(condition.recentDays(3)))
|
||||
.setWeek(queryPrecomputed(condition.recentDays(7)));
|
||||
}
|
||||
|
||||
private void mergeLiveSql(Map<String, CountryDashboardMetricCO> rows, QueryCondition condition) {
|
||||
mergeSql(rows, countryDashboardDAO.listNewUsers(
|
||||
condition.periodType, condition.startTime, condition.endTime,
|
||||
@ -302,60 +303,32 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
@Override
|
||||
public PageResult<CountryDashboardRechargeDetailCO> pageRechargeDetails(
|
||||
CountryDashboardRechargeDetailQryCmd cmd) {
|
||||
ensureRechargeDetailSchema();
|
||||
QueryCondition condition = QueryCondition.of(cmd);
|
||||
int cursor = Math.max(1, cmd == null || cmd.getCursor() == null ? 1 : cmd.getCursor());
|
||||
int limit = Math.max(1, cmd == null || cmd.getLimit() == null ? 100 : cmd.getLimit());
|
||||
limit = Math.min(100, limit);
|
||||
int offset = (cursor - 1) * limit;
|
||||
String countryCodes = normalizeCountryCodes(cmd == null ? null : cmd.getCountryCodes());
|
||||
|
||||
QueryCondition condition = QueryCondition.of(cmd);
|
||||
String countryCodes = QueryCondition.trimToNull(cmd == null ? null : cmd.getCountryCodes());
|
||||
long total = safeLong(countryDashboardDAO.countRechargeDetails(
|
||||
Long total = countryDashboardDAO.countRechargeDetails(
|
||||
condition.startTime, condition.endTime, condition.countryKeyword, countryCodes,
|
||||
condition.sysOrigin, condition.storageTimezoneOffset, condition.statTimezoneOffset));
|
||||
List<CountryDashboardRechargeDetailCO> records = total <= 0 ? List.of()
|
||||
: countryDashboardDAO.listRechargeDetails(
|
||||
condition.sysOrigin, condition.storageTimezoneOffset, condition.statTimezoneOffset);
|
||||
List<CountryDashboardRechargeDetailCO> records = countryDashboardDAO.listRechargeDetails(
|
||||
condition.startTime, condition.endTime, condition.countryKeyword, countryCodes,
|
||||
condition.sysOrigin, condition.storageTimezoneOffset, condition.statTimezoneOffset,
|
||||
(cursor - 1) * limit, limit)
|
||||
offset, limit)
|
||||
.stream()
|
||||
.map(this::toRechargeDetailCO)
|
||||
.toList();
|
||||
|
||||
PageResult<CountryDashboardRechargeDetailCO> result = new PageResult<>();
|
||||
result.setCurrent(cursor);
|
||||
result.setSize(limit);
|
||||
result.setTotal(total);
|
||||
result.setTotal(total == null ? 0L : total);
|
||||
result.setRecords(records);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ensureRechargeDetailSchema() {
|
||||
if (rechargeDetailSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (rechargeDetailSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
countryDashboardDAO.createRechargeDetailTable();
|
||||
rechargeDetailSchemaEnsured = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensurePeriodMetricSchema() {
|
||||
if (periodMetricSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (periodMetricSchemaEnsured) {
|
||||
return;
|
||||
}
|
||||
countryDashboardDAO.createPeriodMetricTable();
|
||||
countryDashboardDAO.ensurePeriodMetricUserColumnsSchema();
|
||||
countryDashboardDAO.ensureMetricValueColumnsSchema();
|
||||
periodMetricSchemaEnsured = true;
|
||||
}
|
||||
}
|
||||
|
||||
private CountryDashboardRechargeDetailCO toRechargeDetailCO(
|
||||
CountryDashboardRechargeDetailDTO dto) {
|
||||
return new CountryDashboardRechargeDetailCO()
|
||||
@ -382,31 +355,18 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
CountryDashboardMetricCO row = getRow(rows, metric.getCountryCode(), metric.getCountryName(),
|
||||
metric.getPeriodKey(), metric.getPeriodName());
|
||||
row.setCountryNewUser(row.getCountryNewUser() + safeLong(metric.getCountryNewUser()));
|
||||
row.setDailyActiveUser(row.getDailyActiveUser() + safeLong(metric.getDailyActiveUser()));
|
||||
row.setDailyRechargeUser(row.getDailyRechargeUser() + safeLong(metric.getDailyRechargeUser()));
|
||||
row.setNewUserRecharge(add(row.getNewUserRecharge(), metric.getNewUserRecharge()));
|
||||
row.setOfficialRecharge(add(row.getOfficialRecharge(), metric.getOfficialRecharge()));
|
||||
row.setMifapayRecharge(add(row.getMifapayRecharge(), metric.getMifapayRecharge()));
|
||||
row.setGoogleRecharge(add(row.getGoogleRecharge(), metric.getGoogleRecharge()));
|
||||
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
|
||||
row.setUserRecharge(add(row.getUserRecharge(), metric.getUserRecharge()));
|
||||
row.setNewDealerUserRecharge(add(row.getNewDealerUserRecharge(), metric.getNewDealerUserRecharge()));
|
||||
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
|
||||
row.setSalaryTransfer(add(row.getSalaryTransfer(), metric.getSalaryTransfer()));
|
||||
row.setGiftConsume(add(row.getGiftConsume(), metric.getGiftConsume()));
|
||||
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
|
||||
row.setLuckyGiftUser(row.getLuckyGiftUser() + safeLong(metric.getLuckyGiftUser()));
|
||||
row.setLuckyGiftPayout(add(row.getLuckyGiftPayout(), metric.getLuckyGiftPayout()));
|
||||
row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), metric.getLuckyGiftAnchorShare()));
|
||||
row.setGameTotalFlow(add(row.getGameTotalFlow(), metric.getGameTotalFlow()));
|
||||
row.setGameUser(row.getGameUser() + safeLong(metric.getGameUser()));
|
||||
row.setGamePayout(add(row.getGamePayout(), metric.getGamePayout()));
|
||||
row.setD1RetentionUser(row.getD1RetentionUser() + safeLong(metric.getD1RetentionUser()));
|
||||
row.setD1RetentionBaseUser(row.getD1RetentionBaseUser() + safeLong(metric.getD1RetentionBaseUser()));
|
||||
row.setD7RetentionUser(row.getD7RetentionUser() + safeLong(metric.getD7RetentionUser()));
|
||||
row.setD7RetentionBaseUser(row.getD7RetentionBaseUser() + safeLong(metric.getD7RetentionBaseUser()));
|
||||
row.setD30RetentionUser(row.getD30RetentionUser() + safeLong(metric.getD30RetentionUser()));
|
||||
row.setD30RetentionBaseUser(row.getD30RetentionBaseUser() + safeLong(metric.getD30RetentionBaseUser()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -787,14 +747,11 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
for (CountryDashboardMetricCO row : records) {
|
||||
total.setCountryNewUser(total.getCountryNewUser() + safeLong(row.getCountryNewUser()));
|
||||
total.setDailyActiveUser(total.getDailyActiveUser() + safeLong(row.getDailyActiveUser()));
|
||||
total.setDailyRechargeUser(total.getDailyRechargeUser() + safeLong(row.getDailyRechargeUser()));
|
||||
total.setNewUserRecharge(add(total.getNewUserRecharge(), row.getNewUserRecharge()));
|
||||
total.setOfficialRecharge(add(total.getOfficialRecharge(), row.getOfficialRecharge()));
|
||||
total.setMifapayRecharge(add(total.getMifapayRecharge(), row.getMifapayRecharge()));
|
||||
total.setGoogleRecharge(add(total.getGoogleRecharge(), row.getGoogleRecharge()));
|
||||
total.setDealerRecharge(add(total.getDealerRecharge(), row.getDealerRecharge()));
|
||||
total.setUserRecharge(add(total.getUserRecharge(), row.getUserRecharge()));
|
||||
total.setNewDealerUserRecharge(add(total.getNewDealerUserRecharge(), row.getNewDealerUserRecharge()));
|
||||
total.setSalaryExchange(add(total.getSalaryExchange(), row.getSalaryExchange()));
|
||||
total.setSalaryTransfer(add(total.getSalaryTransfer(), row.getSalaryTransfer()));
|
||||
total.setGiftConsume(add(total.getGiftConsume(), row.getGiftConsume()));
|
||||
@ -993,34 +950,6 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||
);
|
||||
}
|
||||
|
||||
private QueryCondition recentDays(int dayCount) {
|
||||
LocalDate end = LocalDate.now(statZoneId);
|
||||
LocalDate start = end.minusDays(Math.max(1, dayCount) - 1L);
|
||||
Instant recentStartInstant = start.atStartOfDay(statZoneId).toInstant();
|
||||
Instant recentEndInstant = end.plusDays(1).atStartOfDay(statZoneId).toInstant();
|
||||
LocalDateTime recentStorageStartTime =
|
||||
LocalDateTime.ofInstant(recentStartInstant, STORAGE_ZONE_ID);
|
||||
LocalDateTime recentStorageEndTime =
|
||||
LocalDateTime.ofInstant(recentEndInstant, STORAGE_ZONE_ID);
|
||||
return new QueryCondition(
|
||||
"DAY",
|
||||
DATE_FORMATTER.format(start),
|
||||
DATE_FORMATTER.format(end),
|
||||
statTimezone,
|
||||
statZoneId,
|
||||
storageTimezoneOffset,
|
||||
statTimezoneOffset,
|
||||
start,
|
||||
end,
|
||||
recentStorageStartTime,
|
||||
recentStorageEndTime,
|
||||
recentStartInstant,
|
||||
recentEndInstant,
|
||||
null,
|
||||
sysOrigin
|
||||
);
|
||||
}
|
||||
|
||||
private PeriodBucket toPeriodBucket(String day) {
|
||||
if (PERIOD_ALL.equals(periodType)) {
|
||||
return new PeriodBucket(PERIOD_ALL, "全部");
|
||||
|
||||
@ -1,25 +1,16 @@
|
||||
package com.red.circle.console.app.service.app.order;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.red.circle.console.app.dto.clienobject.order.InAppPurchaseDetailsCO;
|
||||
import com.red.circle.console.app.excel.EasyExcelUtils;
|
||||
import com.red.circle.console.app.excel.model.order.InAppPurchaseDetailsExportModel;
|
||||
import com.red.circle.console.infra.database.rds.entity.admin.User;
|
||||
import com.red.circle.console.infra.database.rds.service.admin.UserService;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.order.inner.asserts.OrderErrorCode;
|
||||
import com.red.circle.order.inner.endpoint.InAppPurchaseDetailsClient;
|
||||
import com.red.circle.order.inner.model.cmd.inapp.InAppPurchaseDetailsQryCmd;
|
||||
import com.red.circle.order.inner.model.dto.inapp.InAppPurchaseFactoryDTO;
|
||||
import com.red.circle.order.inner.model.dto.inapp.InAppPurchaseDetailsDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -142,85 +133,4 @@ public class InAppPurchaseBackServiceImpl implements InAppPurchaseBackService {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportDetails(HttpServletResponse response, InAppPurchaseDetailsQryCmd query)
|
||||
throws IOException {
|
||||
query.setLastId(null);
|
||||
query.setLimit(null);
|
||||
query.setExportAll(Boolean.TRUE);
|
||||
|
||||
List<InAppPurchaseDetailsExportModel> exportRows = listDetails(query).stream()
|
||||
.map(this::toExportModel)
|
||||
.collect(Collectors.toList());
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
String fileName = URLEncoder.encode("InAppPurchaseOrders", StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
EasyExcel.write(response.getOutputStream(), InAppPurchaseDetailsExportModel.class)
|
||||
.registerWriteHandler(EasyExcelUtils.getStyleStrategy())
|
||||
.sheet("Orders")
|
||||
.doWrite(exportRows);
|
||||
}
|
||||
|
||||
private InAppPurchaseDetailsExportModel toExportModel(InAppPurchaseDetailsCO co) {
|
||||
InAppPurchaseDetailsDTO details = co.getDetails();
|
||||
InAppPurchaseFactoryDTO factory = Objects.isNull(details) ? null : details.getFactory();
|
||||
UserProfileDTO buyer = pickBuyerProfile(co, details);
|
||||
UserProfileDTO accept = co.getAcceptUserProfile();
|
||||
|
||||
return new InAppPurchaseDetailsExportModel()
|
||||
.setEnv(text(Objects.isNull(details) ? null : details.getEnv()))
|
||||
.setSysOrigin(text(Objects.isNull(details) ? null : details.getSysOrigin()))
|
||||
.setPlatform(text(Objects.isNull(factory) ? null : factory.getPlatform()))
|
||||
.setFactoryCode(text(Objects.isNull(factory) ? null : factory.getFactoryCode()))
|
||||
.setChannel(text(Objects.isNull(factory) ? null : factory.getFactoryChannelCode()))
|
||||
.setBuyerUserId(userId(buyer, Objects.isNull(details) ? null : details.getCreateUser()))
|
||||
.setBuyerAccount(text(Objects.isNull(buyer) ? null : buyer.getAccount()))
|
||||
.setBuyerNickname(buyerNickname(co, buyer))
|
||||
.setAcceptUserId(userId(accept, Objects.isNull(details) ? null : details.getAcceptUserId()))
|
||||
.setAcceptAccount(text(Objects.isNull(accept) ? null : accept.getAccount()))
|
||||
.setAcceptNickname(text(Objects.isNull(accept) ? null : accept.getUserNickname()))
|
||||
.setAmountUsd(Objects.isNull(details) ? null : details.getAmountUsd())
|
||||
.setAmount(Objects.isNull(details) ? null : details.getAmount())
|
||||
.setCurrency(text(Objects.isNull(details) ? null : details.getCurrency()))
|
||||
.setProductDescriptor(text(Objects.isNull(details) ? null : details.getProductDescriptor()))
|
||||
.setStatus(text(Objects.isNull(details) ? null : details.getStatus()))
|
||||
.setReceiptType(text(Objects.isNull(details) ? null : details.getReceiptType()))
|
||||
.setOrderId(text(Objects.isNull(details) ? null : details.getOrderId()))
|
||||
.setAppOrderId(text(Objects.isNull(details) ? null : details.getId()))
|
||||
.setTrackId(text(Objects.isNull(details) ? null : details.getTrackId()))
|
||||
.setCountryCode(text(Objects.isNull(details) ? null : details.getCountryCode()))
|
||||
.setCreateTime(text(Objects.isNull(details) ? null : details.getCreateTime()))
|
||||
.setUpdateTime(text(Objects.isNull(details) ? null : details.getUpdateTime()));
|
||||
}
|
||||
|
||||
private UserProfileDTO pickBuyerProfile(InAppPurchaseDetailsCO co, InAppPurchaseDetailsDTO details) {
|
||||
if (Objects.nonNull(co.getCreateUserProfile())) {
|
||||
return co.getCreateUserProfile();
|
||||
}
|
||||
if (Objects.nonNull(details) && Objects.equals(details.getCreateUser(), details.getAcceptUserId())) {
|
||||
return co.getAcceptUserProfile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String buyerNickname(InAppPurchaseDetailsCO co, UserProfileDTO buyer) {
|
||||
if (StringUtils.isNotBlank(co.getCreateNickname())) {
|
||||
return co.getCreateNickname();
|
||||
}
|
||||
return text(Objects.isNull(buyer) ? null : buyer.getUserNickname());
|
||||
}
|
||||
|
||||
private String userId(UserProfileDTO profile, Long fallback) {
|
||||
if (Objects.nonNull(profile) && Objects.nonNull(profile.getId())) {
|
||||
return Objects.toString(profile.getId());
|
||||
}
|
||||
return text(fallback);
|
||||
}
|
||||
|
||||
private String text(Object value) {
|
||||
return Objects.isNull(value) ? "" : Objects.toString(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -37,11 +37,6 @@ public class SysEnumConfigServiceImpl implements SysEnumConfigService {
|
||||
return ResponseAssert.requiredSuccess(enumConfigClient.getViolationAvatar(violationPicture));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumConfigDTO getByCode(String name, String sysOrigin) {
|
||||
return ResponseAssert.requiredSuccess(enumConfigClient.getByCode(name, sysOrigin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSysEnumConfig(EnumConfigDTO sysEnumConfig) {
|
||||
ResponseAssert.requiredSuccess(enumConfigClient.saveSysEnumConfig(sysEnumConfig));
|
||||
|
||||
@ -4,9 +4,7 @@ import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.order.inner.endpoint.PayCountryClient;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
@ -70,11 +68,6 @@ public class SysPayCountryServiceImpl implements SysPayCountryService {
|
||||
ResponseAssert.requiredSuccess(payCountryClient.shelf(id, shelf));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd) {
|
||||
return ResponseAssert.requiredSuccess(payCountryClient.syncCurrencyRates(cmd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, PayCountryDTO> mapCountryByIds(Set<Long> ids) {
|
||||
return ResponseAssert.requiredSuccess(payCountryClient.mapCountryByIds(ids));
|
||||
|
||||
@ -2,19 +2,13 @@ package com.red.circle.console.app.service.app.team;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
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.dto.clienobject.team.TeamSalaryCO;
|
||||
import com.red.circle.console.app.dto.clienobject.team.TeamSalaryDetailsCO;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
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.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.TeamSalaryPaymentDetailsDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
@ -22,9 +16,7 @@ import com.red.circle.tool.core.text.StringUtils;
|
||||
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.user.UserProfileClient;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -121,39 +113,6 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
|
||||
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) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
regionConfigClient.mapRegionNameBySysOrigin(query.getSysOrigin()));
|
||||
@ -188,67 +147,4 @@ public class TeamSalaryBackServiceImpl implements TeamSalaryBackService {
|
||||
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())
|
||||
.setAgentMember(dto.getAgentMember())
|
||||
.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())
|
||||
.setAgentMember(dto.getAgentMember())
|
||||
.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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -92,11 +92,6 @@ public class TeamManagerServiceImpl implements TeamManagerService {
|
||||
.orElse(""))
|
||||
.setRegionId(info.getRegionId())
|
||||
.setRegionName(regionNameMap.get(info.getRegionId()))
|
||||
.setCanAddBdLeader(defaultEnabled(info.getCanAddBdLeader()))
|
||||
.setCanGiftProps(defaultEnabled(info.getCanGiftProps()))
|
||||
.setCanBanUser(defaultEnabled(info.getCanBanUser()))
|
||||
.setCanUnbanUser(defaultEnabled(info.getCanUnbanUser()))
|
||||
.setCanChangeCountry(defaultEnabled(info.getCanChangeCountry()))
|
||||
.setCreateTime(info.getCreateTime())
|
||||
.setUpdateTime(info.getUpdateTime()));
|
||||
}
|
||||
@ -117,11 +112,6 @@ public class TeamManagerServiceImpl implements TeamManagerService {
|
||||
.setSysOrigin(cmd.getSysOrigin())
|
||||
.setContact(cmd.getContact())
|
||||
.setRegionId(cmd.getRegionId())
|
||||
.setCanAddBdLeader(defaultEnabled(cmd.getCanAddBdLeader()))
|
||||
.setCanGiftProps(defaultEnabled(cmd.getCanGiftProps()))
|
||||
.setCanBanUser(defaultEnabled(cmd.getCanBanUser()))
|
||||
.setCanUnbanUser(defaultEnabled(cmd.getCanUnbanUser()))
|
||||
.setCanChangeCountry(defaultEnabled(cmd.getCanChangeCountry()))
|
||||
.setCreateUser(cmd.getCreateUser());
|
||||
|
||||
ResponseAssert.requiredSuccess(teamManagerClient.add(addCmd));
|
||||
@ -134,11 +124,6 @@ public class TeamManagerServiceImpl implements TeamManagerService {
|
||||
.setId(cmd.getId())
|
||||
.setContact(cmd.getContact())
|
||||
.setRegionId(cmd.getRegionId())
|
||||
.setCanAddBdLeader(cmd.getCanAddBdLeader())
|
||||
.setCanGiftProps(cmd.getCanGiftProps())
|
||||
.setCanBanUser(cmd.getCanBanUser())
|
||||
.setCanUnbanUser(cmd.getCanUnbanUser())
|
||||
.setCanChangeCountry(cmd.getCanChangeCountry())
|
||||
.setUpdateUser(cmd.getUpdateUser())));
|
||||
}
|
||||
|
||||
@ -159,8 +144,4 @@ public class TeamManagerServiceImpl implements TeamManagerService {
|
||||
ResponseAssert.requiredSuccess(administratorClient.deleteSysAdministrator(administrator.getId()));
|
||||
}
|
||||
|
||||
private Boolean defaultEnabled(Boolean value) {
|
||||
return !Boolean.FALSE.equals(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -456,11 +456,6 @@ public class UserFreightBalanceServiceImpl implements
|
||||
Map<Long, RegionConfigDTO> regionConfigMap = ResponseAssert.requiredSuccess(
|
||||
userRegionClient.mapUserRegionConfig(
|
||||
new UserRegionConfigMapCmd().setUserIds(userIds)));
|
||||
Map<Long, String> opsUserNicknameMap = userService.mapBackUserNickname(
|
||||
waters.stream()
|
||||
.map(UserFreightBalanceRunningWaterDTO::getCreateUser)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
return waters.stream().map(water -> {
|
||||
|
||||
@ -477,9 +472,6 @@ public class UserFreightBalanceServiceImpl implements
|
||||
.map(RegionConfigDTO::getRegionCode)
|
||||
.orElse("未知"));
|
||||
exportModel.setOriginName(FreightBalanceOrigin.getDesc(water.getOrigin()));
|
||||
exportModel.setOperationUserNickname(Optional.ofNullable(water.getCreateUser())
|
||||
.map(opsUserNicknameMap::get)
|
||||
.orElse(""));
|
||||
exportModel.setAccount(getAccountDescribe(sendUser));
|
||||
exportModel.setNickname(Optional.ofNullable(sendUser)
|
||||
.map(UserProfileDTO::getUserNickname).orElse("?"));
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
package com.red.circle.console.app.service.app.user;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
|
||||
import com.red.circle.common.business.core.enums.ReceiptType;
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
@ -11,8 +8,6 @@ import com.red.circle.console.app.convertor.WalletAppConvertor;
|
||||
import com.red.circle.console.app.dto.clienobject.user.UserGoldBalanceCO;
|
||||
import com.red.circle.console.app.dto.clienobject.user.UserGoldRunningWaterCO;
|
||||
import com.red.circle.console.app.dto.clienobject.user.UserGoldRunningWaterClsTmpCO;
|
||||
import com.red.circle.console.app.excel.EasyExcelUtils;
|
||||
import com.red.circle.console.app.excel.model.wallet.UserGoldRunningWaterExportModel;
|
||||
import com.red.circle.console.infra.database.rds.service.admin.UserService;
|
||||
import com.red.circle.console.inner.error.ConsoleErrorCode;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
@ -42,17 +37,9 @@ import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.SendGoldCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.UserGoldRunningWaterBackQryCmd;
|
||||
import com.red.circle.wallet.inner.model.dto.UserGoldRunningWaterClsHistoryDTO;
|
||||
import com.red.circle.wallet.inner.model.dto.UserGoldRunningWaterDTO;
|
||||
import com.red.circle.wallet.inner.model.dto.UserGoldRunningWaterHistoryDTO;
|
||||
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -70,10 +57,6 @@ import org.springframework.stereotype.Service;
|
||||
@RequiredArgsConstructor
|
||||
public class UserWalletServiceImpl implements UserWalletService {
|
||||
|
||||
private static final int EXPORT_BATCH_SIZE = 1000;
|
||||
private static final DateTimeFormatter EXPORT_TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final UserService userService;
|
||||
private final UserSvipClient userSvipClient;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
@ -195,133 +178,6 @@ public class UserWalletServiceImpl implements UserWalletService {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportGoldRunningWater(HttpServletResponse response, UserGoldRunningWaterBackQryCmd query)
|
||||
throws IOException {
|
||||
UserGoldRunningWaterBackQryCmd exportQuery = copyGoldRunningWaterQuery(query)
|
||||
.setContext(null)
|
||||
.setLastId(null)
|
||||
.setLimit(EXPORT_BATCH_SIZE);
|
||||
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
String fileName = URLEncoder.encode("GmGoldRunningWater", StandardCharsets.UTF_8)
|
||||
.replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
|
||||
ExcelWriter excelWriter = null;
|
||||
try {
|
||||
excelWriter = EasyExcel.write(response.getOutputStream(), UserGoldRunningWaterExportModel.class)
|
||||
.registerWriteHandler(EasyExcelUtils.getStyleStrategy())
|
||||
.build();
|
||||
WriteSheet sheet = EasyExcel.writerSheet("GmGoldRunningWater").build();
|
||||
boolean wroteAnyData = false;
|
||||
while (true) {
|
||||
List<UserGoldRunningWaterCO> waters = listGoldRunningWater(exportQuery);
|
||||
if (CollectionUtils.isEmpty(waters)) {
|
||||
break;
|
||||
}
|
||||
|
||||
excelWriter.write(toGoldRunningWaterExportModels(waters), sheet);
|
||||
wroteAnyData = true;
|
||||
Long nextLastId = getLastRunningWaterId(waters);
|
||||
if (Objects.isNull(nextLastId) || Objects.equals(nextLastId, exportQuery.getLastId())) {
|
||||
break;
|
||||
}
|
||||
exportQuery.setLastId(nextLastId);
|
||||
}
|
||||
if (!wroteAnyData) {
|
||||
excelWriter.write(Lists.newArrayList(), sheet);
|
||||
}
|
||||
} finally {
|
||||
if (Objects.nonNull(excelWriter)) {
|
||||
excelWriter.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private UserGoldRunningWaterBackQryCmd copyGoldRunningWaterQuery(
|
||||
UserGoldRunningWaterBackQryCmd source) {
|
||||
if (Objects.isNull(source)) {
|
||||
return new UserGoldRunningWaterBackQryCmd();
|
||||
}
|
||||
return new UserGoldRunningWaterBackQryCmd()
|
||||
.setId(source.getId())
|
||||
.setSysOrigin(source.getSysOrigin())
|
||||
.setUserId(source.getUserId())
|
||||
.setQueryBackOperationUser(source.getQueryBackOperationUser())
|
||||
.setBackOperationUser(source.getBackOperationUser())
|
||||
.setBackOperationUsers(source.getBackOperationUsers())
|
||||
.setTrackId(source.getTrackId())
|
||||
.setType(source.getType())
|
||||
.setOrigin(source.getOrigin())
|
||||
.setStartTime(source.getStartTime())
|
||||
.setEndTime(source.getEndTime());
|
||||
}
|
||||
|
||||
private List<UserGoldRunningWaterExportModel> toGoldRunningWaterExportModels(
|
||||
List<UserGoldRunningWaterCO> waters) {
|
||||
return waters.stream()
|
||||
.map(water -> {
|
||||
UserGoldRunningWaterDTO runningWater = water.getRunningWater();
|
||||
UserProfileDTO userProfile = water.getUserProfile();
|
||||
return new UserGoldRunningWaterExportModel()
|
||||
.setRecordId(Objects.toString(runningWater.getId(), ""))
|
||||
.setSysOrigin(runningWater.getSysOrigin())
|
||||
.setUserId(Objects.toString(runningWater.getUserId(), ""))
|
||||
.setUserAccount(getAccountDescribe(userProfile))
|
||||
.setUserNickname(Optional.ofNullable(userProfile)
|
||||
.map(UserProfileDTO::getUserNickname).orElse(""))
|
||||
.setType(getGoldRunningWaterTypeName(runningWater.getType()))
|
||||
.setQuantity(runningWater.getQuantity())
|
||||
.setBalance(runningWater.getBalance())
|
||||
.setOriginName(runningWater.getOriginName())
|
||||
.setRemark(runningWater.getRemark())
|
||||
.setBackOperationName(StringUtils.isBlank(water.getBackOperationName())
|
||||
? "-" : water.getBackOperationName())
|
||||
.setCreateTime(formatExportTime(runningWater.getCreateTime()));
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Long getLastRunningWaterId(List<UserGoldRunningWaterCO> waters) {
|
||||
if (CollectionUtils.isEmpty(waters)) {
|
||||
return null;
|
||||
}
|
||||
return Optional.ofNullable(waters.get(waters.size() - 1))
|
||||
.map(UserGoldRunningWaterCO::getRunningWater)
|
||||
.map(UserGoldRunningWaterDTO::getId)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private String getGoldRunningWaterTypeName(Integer type) {
|
||||
if (Objects.equals(type, 0)) {
|
||||
return "增加";
|
||||
}
|
||||
if (Objects.equals(type, 1)) {
|
||||
return "减少";
|
||||
}
|
||||
return Objects.toString(type, "");
|
||||
}
|
||||
|
||||
private String formatExportTime(Long time) {
|
||||
if (Objects.isNull(time)) {
|
||||
return "";
|
||||
}
|
||||
return EXPORT_TIME_FORMATTER.format(
|
||||
Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
private String getAccountDescribe(UserProfileDTO userProfile) {
|
||||
if (Objects.isNull(userProfile)) {
|
||||
return "";
|
||||
}
|
||||
if (Objects.nonNull(userProfile.getOwnSpecialId())) {
|
||||
return userProfile.getOwnSpecialId().getAccount() + "(" + userProfile.getAccount() + ")";
|
||||
}
|
||||
return userProfile.getAccount();
|
||||
}
|
||||
|
||||
private UserGoldRunningWaterClsTmpCO getGoldRunningWaterDbFallback(
|
||||
UserGoldRunningWaterBackQryCmd query) {
|
||||
if (StringUtils.isNotBlank(query.getContext()) && Objects.isNull(query.getLastId())) {
|
||||
|
||||
@ -30,7 +30,6 @@ import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -97,10 +96,7 @@ public class GmVipGiftServiceImpl implements GmVipGiftService {
|
||||
expireAt, now);
|
||||
vipOrderRecordService.save(order);
|
||||
|
||||
Set<Long> previousResourceIds = vipResourceIds(currentState);
|
||||
upsertUserVipState(currentState, userProfile, config, startAt, expireAt, now);
|
||||
expireSupersededVipResources(userProfile.getOriginSys(), userProfile.getId(), config,
|
||||
previousResourceIds, now);
|
||||
giftVipResources(userProfile.getId(), config, now, cmd.getReqUserId());
|
||||
|
||||
GmVipGiftRecord record = buildGiftRecord(cmd, applicant, userProfile, fromLevel, config,
|
||||
@ -276,63 +272,6 @@ public class GmVipGiftServiceImpl implements GmVipGiftService {
|
||||
now, opUser);
|
||||
}
|
||||
|
||||
private void expireSupersededVipResources(String sysOrigin, Long userId,
|
||||
VipLevelConfig targetConfig, Set<Long> previousResourceIds, Timestamp now) {
|
||||
Set<Long> targetResourceIds = vipResourceIds(targetConfig);
|
||||
Set<Long> allVipResourceIds = new HashSet<>();
|
||||
vipLevelConfigService.query()
|
||||
.eq(VipLevelConfig::getSysOrigin, sysOrigin)
|
||||
.list()
|
||||
.forEach(config -> allVipResourceIds.addAll(vipResourceIds(config)));
|
||||
allVipResourceIds.addAll(previousResourceIds);
|
||||
allVipResourceIds.removeAll(targetResourceIds);
|
||||
if (CollectionUtils.isEmpty(allVipResourceIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
userPropsBackpackService.update()
|
||||
.set(UserPropsBackpack::getExpireTime, now)
|
||||
.set(UserPropsBackpack::getUseProps, Boolean.FALSE)
|
||||
.set(UserPropsBackpack::getAllowGive, Boolean.FALSE)
|
||||
.set(UserPropsBackpack::getUpdateTime, now)
|
||||
.eq(UserPropsBackpack::getUserId, userId)
|
||||
.in(UserPropsBackpack::getPropsId, allVipResourceIds)
|
||||
.gt(UserPropsBackpack::getExpireTime, now)
|
||||
.execute();
|
||||
}
|
||||
|
||||
private Set<Long> vipResourceIds(VipLevelConfig config) {
|
||||
Set<Long> ids = new HashSet<>();
|
||||
if (Objects.isNull(config)) {
|
||||
return ids;
|
||||
}
|
||||
addPositive(ids, config.getBadgeResourceId());
|
||||
addPositive(ids, config.getAvatarFrameResourceId());
|
||||
addPositive(ids, config.getEntryEffectResourceId());
|
||||
addPositive(ids, config.getChatBubbleResourceId());
|
||||
addPositive(ids, config.getFloatPictureResourceId());
|
||||
return ids;
|
||||
}
|
||||
|
||||
private Set<Long> vipResourceIds(UserVipState state) {
|
||||
Set<Long> ids = new HashSet<>();
|
||||
if (Objects.isNull(state)) {
|
||||
return ids;
|
||||
}
|
||||
addPositive(ids, state.getBadgeResourceId());
|
||||
addPositive(ids, state.getAvatarFrameResourceId());
|
||||
addPositive(ids, state.getEntryEffectResourceId());
|
||||
addPositive(ids, state.getChatBubbleResourceId());
|
||||
addPositive(ids, state.getFloatPictureResourceId());
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void addPositive(Set<Long> ids, Long id) {
|
||||
if (Objects.nonNull(id) && id > 0) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void giveProp(Long userId, Long propsId, String type, Integer days, Timestamp now,
|
||||
Long opUser) {
|
||||
if (Objects.isNull(propsId) || propsId <= 0 || Objects.isNull(days) || days <= 0) {
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
package com.red.circle.console.app.service.app.activity;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpConfigCO;
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpLevelConfigCO;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpConfigSaveCmd;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpLevelConfigSaveCmd;
|
||||
import com.red.circle.console.app.service.app.sys.CpCabinConfigService;
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysCpCabinConfigQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.sys.SysCpCabinConfigDTO;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ResidentCpActivityServiceImplTest {
|
||||
|
||||
@Test
|
||||
void getConfigShouldReturnCpLevelsFromZeroToFive() {
|
||||
CpCabinConfigService cpCabinConfigService = mock(CpCabinConfigService.class);
|
||||
ResidentCpActivityServiceImpl service = new ResidentCpActivityServiceImpl(
|
||||
cpCabinConfigService);
|
||||
PageResult<SysCpCabinConfigDTO> page = new PageResult<>();
|
||||
page.setRecords(List.of(
|
||||
config(100L, 1L, 100L),
|
||||
config(101L, 2L, 200L),
|
||||
config(102L, 3L, 300L),
|
||||
config(103L, 4L, 400L),
|
||||
config(104L, 5L, 500L)
|
||||
));
|
||||
when(cpCabinConfigService.getCpCabin(any(SysCpCabinConfigQryCmd.class))).thenReturn(page);
|
||||
|
||||
ResidentCpConfigCO result = service.getConfig("likei");
|
||||
|
||||
assertEquals("LIKEI", result.getSysOrigin());
|
||||
assertEquals(List.of(0, 1, 2, 3, 4, 5),
|
||||
result.getLevels().stream().map(ResidentCpLevelConfigCO::getLevel).toList());
|
||||
assertEquals(100L, result.getLevels().get(0).getRequiredIntimacyValue());
|
||||
assertEquals(100L, result.getLevels().get(0).getCabinConfigId());
|
||||
assertEquals(0L, result.getLevels().get(5).getRequiredIntimacyValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveConfigShouldNotRequireExactLevelCount() {
|
||||
CpCabinConfigService cpCabinConfigService = mock(CpCabinConfigService.class);
|
||||
ResidentCpActivityServiceImpl service = new ResidentCpActivityServiceImpl(
|
||||
cpCabinConfigService);
|
||||
PageResult<SysCpCabinConfigDTO> page = new PageResult<>();
|
||||
page.setRecords(List.of());
|
||||
when(cpCabinConfigService.getCpCabin(any(SysCpCabinConfigQryCmd.class))).thenReturn(page);
|
||||
|
||||
ResidentCpConfigSaveCmd cmd = new ResidentCpConfigSaveCmd()
|
||||
.setSysOrigin("likei")
|
||||
.setLevels(List.of(
|
||||
level(0, 0L),
|
||||
level(1, 100L),
|
||||
level(2, 200L),
|
||||
level(3, 300L),
|
||||
level(4, 400L)
|
||||
));
|
||||
|
||||
assertDoesNotThrow(() -> service.saveConfig(cmd));
|
||||
verify(cpCabinConfigService, times(5)).addCpCabin(any(SysCpCabinConfigDTO.class));
|
||||
}
|
||||
|
||||
private static SysCpCabinConfigDTO config(Long id, Long sort, Long unlockValue) {
|
||||
return new SysCpCabinConfigDTO()
|
||||
.setId(id)
|
||||
.setName("CP " + sort + "级")
|
||||
.setCabinUnlockValue(unlockValue)
|
||||
.setSort(sort);
|
||||
}
|
||||
|
||||
private static ResidentCpLevelConfigSaveCmd level(Integer level, Long requiredValue) {
|
||||
return new ResidentCpLevelConfigSaveCmd()
|
||||
.setLevel(level)
|
||||
.setLevelName("CP " + level + "级")
|
||||
.setRequiredIntimacyValue(requiredValue);
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package com.red.circle.console.app.dto.clienobject.app.activity.cp;
|
||||
|
||||
import com.red.circle.framework.dto.ClientObject;
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 常驻 CP 配置.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ResidentCpConfigCO extends ClientObject {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Boolean configured = Boolean.FALSE;
|
||||
|
||||
private String sysOrigin;
|
||||
|
||||
private List<ResidentCpLevelConfigCO> levels = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Relation dismiss gold config. Key: CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private Map<String, Long> dismissConsumeGolds = new LinkedHashMap<>();
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.red.circle.console.app.dto.clienobject.app.activity.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 lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 常驻 CP 等级配置.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ResidentCpLevelConfigCO extends ClientObject {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer level;
|
||||
|
||||
private String levelName;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long requiredIntimacyValue;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long cabinConfigId;
|
||||
}
|
||||
@ -91,11 +91,6 @@ public class SysGiftConfigCO implements Serializable {
|
||||
*/
|
||||
private String giftTab;
|
||||
|
||||
/**
|
||||
* CP礼物关系类型:CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private String cpRelationType;
|
||||
|
||||
/**
|
||||
* 0.未删除 1.已删除.
|
||||
*/
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
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 Boolean agentMember;
|
||||
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;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -38,16 +38,6 @@ public class TeamManagerCO extends ClientObject {
|
||||
|
||||
private String regionName;
|
||||
|
||||
private Boolean canAddBdLeader;
|
||||
|
||||
private Boolean canGiftProps;
|
||||
|
||||
private Boolean canBanUser;
|
||||
|
||||
private Boolean canUnbanUser;
|
||||
|
||||
private Boolean canChangeCountry;
|
||||
|
||||
private Timestamp createTime;
|
||||
|
||||
private Timestamp updateTime;
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
package com.red.circle.console.app.dto.clientobject.datav;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 国家数据大屏 ARPU 汇总.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class CountryDashboardArpuSummaryCO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private CountryDashboardCO day;
|
||||
|
||||
private CountryDashboardCO threeDay;
|
||||
|
||||
private CountryDashboardCO week;
|
||||
}
|
||||
@ -29,6 +29,4 @@ public class CountryDashboardCO implements Serializable {
|
||||
private CountryDashboardMetricCO total;
|
||||
|
||||
private List<CountryDashboardMetricCO> records;
|
||||
|
||||
private CountryDashboardArpuSummaryCO arpuSummary;
|
||||
}
|
||||
|
||||
@ -28,16 +28,10 @@ public class CountryDashboardMetricCO implements Serializable {
|
||||
|
||||
private Long dailyActiveUser = 0L;
|
||||
|
||||
private Long dailyRechargeUser = 0L;
|
||||
|
||||
private BigDecimal newUserRecharge = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal dealerRecharge = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal userRecharge = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal newDealerUserRecharge = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal mifapayRecharge = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal googleRecharge = BigDecimal.ZERO;
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
package com.red.circle.console.app.dto.cmd.app.activity.cp;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 常驻 CP 配置保存命令.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ResidentCpConfigSaveCmd implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotBlank(message = "sysOrigin required.")
|
||||
private String sysOrigin;
|
||||
|
||||
@Valid
|
||||
@NotEmpty(message = "levels required.")
|
||||
private List<ResidentCpLevelConfigSaveCmd> levels;
|
||||
|
||||
/**
|
||||
* Relation dismiss gold config. Key: CP/BROTHER/SISTERS.
|
||||
*/
|
||||
private Map<String, Long> dismissConsumeGolds;
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package com.red.circle.console.app.dto.cmd.app.activity.cp;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 常驻 CP 等级配置保存命令.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ResidentCpLevelConfigSaveCmd implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NotNull(message = "level required.")
|
||||
private Integer level;
|
||||
|
||||
private String levelName;
|
||||
|
||||
@NotNull(message = "requiredIntimacyValue required.")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long requiredIntimacyValue;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long cabinConfigId;
|
||||
}
|
||||
@ -31,14 +31,4 @@ public class TeamManagerAddCmd extends Command {
|
||||
@NotBlank(message = "regionId required.")
|
||||
private String regionId;
|
||||
|
||||
private Boolean canAddBdLeader;
|
||||
|
||||
private Boolean canGiftProps;
|
||||
|
||||
private Boolean canBanUser;
|
||||
|
||||
private Boolean canUnbanUser;
|
||||
|
||||
private Boolean canChangeCountry;
|
||||
|
||||
}
|
||||
|
||||
@ -29,14 +29,4 @@ public class TeamManagerUpdateCmd extends Command {
|
||||
@NotBlank(message = "regionId required.")
|
||||
private String regionId;
|
||||
|
||||
private Boolean canAddBdLeader;
|
||||
|
||||
private Boolean canGiftProps;
|
||||
|
||||
private Boolean canBanUser;
|
||||
|
||||
private Boolean canUnbanUser;
|
||||
|
||||
private Boolean canChangeCountry;
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
package com.red.circle.console.app.service.app.activity;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.app.activity.cp.ResidentCpConfigCO;
|
||||
import com.red.circle.console.app.dto.cmd.app.activity.cp.ResidentCpConfigSaveCmd;
|
||||
|
||||
/**
|
||||
* 常驻 CP 配置管理.
|
||||
*/
|
||||
public interface ResidentCpActivityService {
|
||||
|
||||
ResidentCpConfigCO getConfig(String sysOrigin);
|
||||
|
||||
void saveConfig(ResidentCpConfigSaveCmd cmd);
|
||||
}
|
||||
@ -2,8 +2,6 @@ package com.red.circle.console.app.service.app.order;
|
||||
|
||||
import com.red.circle.console.app.dto.clienobject.order.InAppPurchaseDetailsCO;
|
||||
import com.red.circle.order.inner.model.cmd.inapp.InAppPurchaseDetailsQryCmd;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -29,10 +27,4 @@ public interface InAppPurchaseBackService extends IOrderService {
|
||||
*/
|
||||
Map<String, Object> statistics(InAppPurchaseDetailsQryCmd query);
|
||||
|
||||
/**
|
||||
* 按筛选条件导出订单详情,不使用分页条件.
|
||||
*/
|
||||
void exportDetails(HttpServletResponse response, InAppPurchaseDetailsQryCmd query)
|
||||
throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@ -20,8 +20,6 @@ public interface SysEnumConfigService {
|
||||
|
||||
String getViolationAvatar(EnumConfigKey violationPicture);
|
||||
|
||||
EnumConfigDTO getByCode(String name, String sysOrigin);
|
||||
|
||||
void saveSysEnumConfig(EnumConfigDTO sysEnumConfig);
|
||||
|
||||
void updateSysEnumConfig(EnumConfigDTO sysEnumConfig);
|
||||
|
||||
@ -2,9 +2,7 @@ package com.red.circle.console.app.service.app.sys.pay;
|
||||
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryAddCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryCurrencyRateSyncCmd;
|
||||
import com.red.circle.order.inner.model.cmd.SysPayCountryQryCmd;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryCurrencyRateSyncDTO;
|
||||
import com.red.circle.order.inner.model.dto.PayCountryDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
@ -61,11 +59,6 @@ public interface SysPayCountryService {
|
||||
*/
|
||||
void shelf(Long id, Boolean shelf);
|
||||
|
||||
/**
|
||||
* 同步支付国家默认货币和实时美元汇率.
|
||||
*/
|
||||
PayCountryCurrencyRateSyncDTO syncCurrencyRates(SysPayCountryCurrencyRateSyncCmd cmd);
|
||||
|
||||
/**
|
||||
* 根据id集合获得.
|
||||
*/
|
||||
|
||||
@ -2,13 +2,8 @@ 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.TeamManualSalaryCO;
|
||||
import com.red.circle.console.app.dto.clienobject.team.TeamManualSalaryResultCO;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* 团队自动发送工资凭证.
|
||||
@ -22,14 +17,4 @@ public interface TeamSalaryBackService {
|
||||
*/
|
||||
PageResult<TeamSalaryCO> listByCondition(TeamSalaryBackQryCmd query);
|
||||
|
||||
/**
|
||||
* 手动工资发放预览.
|
||||
*/
|
||||
PageResult<TeamManualSalaryCO> manualSalaryPage(TeamManualSalaryPaymentQryCmd query);
|
||||
|
||||
/**
|
||||
* 确认手动发放工资.
|
||||
*/
|
||||
List<TeamManualSalaryResultCO> manualSalaryPay(TeamManualSalaryPaymentCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@ import com.red.circle.wallet.inner.model.cmd.DeductGoldCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.DiamondCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.SendGoldCmd;
|
||||
import com.red.circle.wallet.inner.model.cmd.UserGoldRunningWaterBackQryCmd;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@ -36,12 +34,6 @@ public interface UserWalletService {
|
||||
*/
|
||||
UserGoldRunningWaterClsTmpCO getGoldRunningWaterCls(UserGoldRunningWaterBackQryCmd query);
|
||||
|
||||
/**
|
||||
* 导出用户金币流水.
|
||||
*/
|
||||
void exportGoldRunningWater(HttpServletResponse response, UserGoldRunningWaterBackQryCmd query)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* 发送金币.
|
||||
*/
|
||||
|
||||
@ -30,20 +30,8 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
|
||||
int createGamePeriodUserMetricTable();
|
||||
|
||||
int createRechargeDetailTable();
|
||||
|
||||
int ensurePeriodMetricTimezoneSchema();
|
||||
|
||||
int ensurePeriodMetricUserColumnsSchema();
|
||||
|
||||
int ensureMetricValueColumnsSchema();
|
||||
|
||||
Integer getDashboardBackfillLock(
|
||||
@Param("lockName") String lockName,
|
||||
@Param("timeoutSeconds") Integer timeoutSeconds);
|
||||
|
||||
Integer releaseDashboardBackfillLock(@Param("lockName") String lockName);
|
||||
|
||||
List<CountryDashboardMetricDTO> listNewUsers(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@ -191,9 +179,6 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
||||
|
||||
int upsertPeriodMetrics(@Param("metrics") List<CountryDashboardPeriodMetricDTO> metrics);
|
||||
|
||||
int upsertPeriodActiveRetentionMetrics(
|
||||
@Param("metrics") List<CountryDashboardPeriodMetricDTO> metrics);
|
||||
|
||||
int deletePeriodUserMetrics(
|
||||
@Param("periodType") String periodType,
|
||||
@Param("periodKey") String periodKey,
|
||||
|
||||
@ -24,8 +24,6 @@ public class CountryDashboardDailyMetricDTO {
|
||||
|
||||
private Long countryNewUser;
|
||||
|
||||
private Long dailyRechargeUser;
|
||||
|
||||
private BigDecimal newUserRecharge;
|
||||
|
||||
private BigDecimal officialRecharge;
|
||||
@ -36,24 +34,14 @@ public class CountryDashboardDailyMetricDTO {
|
||||
|
||||
private BigDecimal dealerRecharge;
|
||||
|
||||
private BigDecimal userRecharge;
|
||||
|
||||
private BigDecimal newDealerUserRecharge;
|
||||
|
||||
private BigDecimal salaryExchange;
|
||||
|
||||
private BigDecimal salaryTransfer;
|
||||
|
||||
private BigDecimal giftConsume;
|
||||
|
||||
private BigDecimal luckyGiftTotalFlow;
|
||||
|
||||
private Long luckyGiftUser;
|
||||
|
||||
private BigDecimal luckyGiftPayout;
|
||||
|
||||
private BigDecimal luckyGiftAnchorShare;
|
||||
|
||||
private BigDecimal gameTotalFlow;
|
||||
|
||||
private Long gameUser;
|
||||
|
||||
@ -21,10 +21,6 @@ public class CountryDashboardMetricDTO {
|
||||
|
||||
private Long countryNewUser;
|
||||
|
||||
private Long dailyActiveUser;
|
||||
|
||||
private Long dailyRechargeUser;
|
||||
|
||||
private BigDecimal newUserRecharge;
|
||||
|
||||
private BigDecimal officialRecharge;
|
||||
@ -35,39 +31,17 @@ public class CountryDashboardMetricDTO {
|
||||
|
||||
private BigDecimal dealerRecharge;
|
||||
|
||||
private BigDecimal userRecharge;
|
||||
|
||||
private BigDecimal newDealerUserRecharge;
|
||||
|
||||
private BigDecimal salaryExchange;
|
||||
|
||||
private BigDecimal salaryTransfer;
|
||||
|
||||
private BigDecimal giftConsume;
|
||||
|
||||
private BigDecimal luckyGiftTotalFlow;
|
||||
|
||||
private Long luckyGiftUser;
|
||||
|
||||
private BigDecimal luckyGiftPayout;
|
||||
|
||||
private BigDecimal luckyGiftAnchorShare;
|
||||
|
||||
private BigDecimal gameTotalFlow;
|
||||
|
||||
private Long gameUser;
|
||||
|
||||
private BigDecimal gamePayout;
|
||||
|
||||
private Long d1RetentionUser;
|
||||
|
||||
private Long d1RetentionBaseUser;
|
||||
|
||||
private Long d7RetentionUser;
|
||||
|
||||
private Long d7RetentionBaseUser;
|
||||
|
||||
private Long d30RetentionUser;
|
||||
|
||||
private Long d30RetentionBaseUser;
|
||||
}
|
||||
|
||||
@ -32,10 +32,6 @@ public class CountryDashboardPeriodMetricDTO {
|
||||
|
||||
private Long countryNewUser;
|
||||
|
||||
private Long dailyActiveUser;
|
||||
|
||||
private Long dailyRechargeUser;
|
||||
|
||||
private BigDecimal newUserRecharge;
|
||||
|
||||
private BigDecimal officialRecharge;
|
||||
@ -46,39 +42,17 @@ public class CountryDashboardPeriodMetricDTO {
|
||||
|
||||
private BigDecimal dealerRecharge;
|
||||
|
||||
private BigDecimal userRecharge;
|
||||
|
||||
private BigDecimal newDealerUserRecharge;
|
||||
|
||||
private BigDecimal salaryExchange;
|
||||
|
||||
private BigDecimal salaryTransfer;
|
||||
|
||||
private BigDecimal giftConsume;
|
||||
|
||||
private BigDecimal luckyGiftTotalFlow;
|
||||
|
||||
private Long luckyGiftUser;
|
||||
|
||||
private BigDecimal luckyGiftPayout;
|
||||
|
||||
private BigDecimal luckyGiftAnchorShare;
|
||||
|
||||
private BigDecimal gameTotalFlow;
|
||||
|
||||
private Long gameUser;
|
||||
|
||||
private BigDecimal gamePayout;
|
||||
|
||||
private Long d1RetentionUser;
|
||||
|
||||
private Long d1RetentionBaseUser;
|
||||
|
||||
private Long d7RetentionUser;
|
||||
|
||||
private Long d7RetentionBaseUser;
|
||||
|
||||
private Long d30RetentionUser;
|
||||
|
||||
private Long d30RetentionBaseUser;
|
||||
}
|
||||
|
||||
@ -3,14 +3,6 @@
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO">
|
||||
|
||||
<select id="getDashboardBackfillLock" resultType="java.lang.Integer">
|
||||
SELECT GET_LOCK(#{lockName}, #{timeoutSeconds})
|
||||
</select>
|
||||
|
||||
<select id="releaseDashboardBackfillLock" resultType="java.lang.Integer">
|
||||
SELECT RELEASE_LOCK(#{lockName})
|
||||
</select>
|
||||
|
||||
<update id="createDailyMetricTable">
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_daily_metric` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
@ -20,21 +12,15 @@
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`country_new_user` bigint NOT NULL DEFAULT 0 COMMENT '新增用户数',
|
||||
`daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT '当日充值用户数',
|
||||
`new_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增用户充值',
|
||||
`official_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '官方充值',
|
||||
`mifapay_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'MifaPay充值',
|
||||
`google_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'Google充值',
|
||||
`dealer_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '代理充值',
|
||||
`user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户充值',
|
||||
`new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增币商用户充值',
|
||||
`salary_exchange` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资兑换',
|
||||
`salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资转移',
|
||||
`gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '礼物消耗',
|
||||
`lucky_gift_total_flow` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物流水',
|
||||
`lucky_gift_user` bigint NOT NULL DEFAULT 0 COMMENT '幸运礼物用户数',
|
||||
`lucky_gift_payout` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物返奖',
|
||||
`lucky_gift_anchor_share` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物主播分成',
|
||||
`game_total_flow` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '游戏流水',
|
||||
`game_user` bigint NOT NULL DEFAULT 0 COMMENT '游戏用户数',
|
||||
`game_payout` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '游戏返奖',
|
||||
@ -61,31 +47,18 @@
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`country_new_user` bigint NOT NULL DEFAULT 0 COMMENT '新增用户数',
|
||||
`daily_active_user` bigint NOT NULL DEFAULT 0 COMMENT '当日活跃用户数',
|
||||
`daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT '当日充值用户数',
|
||||
`new_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增用户充值',
|
||||
`official_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '官方充值',
|
||||
`mifapay_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'MifaPay充值',
|
||||
`google_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'Google充值',
|
||||
`dealer_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '代理充值',
|
||||
`user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '用户充值',
|
||||
`new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '新增币商用户充值',
|
||||
`salary_exchange` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资兑换',
|
||||
`salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '工资转移',
|
||||
`gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '礼物消耗',
|
||||
`lucky_gift_total_flow` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物流水',
|
||||
`lucky_gift_user` bigint NOT NULL DEFAULT 0 COMMENT '幸运礼物用户数',
|
||||
`lucky_gift_payout` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物返奖',
|
||||
`lucky_gift_anchor_share` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '幸运礼物主播分成',
|
||||
`game_total_flow` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '游戏流水',
|
||||
`game_user` bigint NOT NULL DEFAULT 0 COMMENT '游戏用户数',
|
||||
`game_payout` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT '游戏返奖',
|
||||
`d1_retention_user` bigint NOT NULL DEFAULT 0 COMMENT '次日留存用户数',
|
||||
`d1_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT '次日留存基数',
|
||||
`d7_retention_user` bigint NOT NULL DEFAULT 0 COMMENT '7日留存用户数',
|
||||
`d7_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT '7日留存基数',
|
||||
`d30_retention_user` bigint NOT NULL DEFAULT 0 COMMENT '30日留存用户数',
|
||||
`d30_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT '30日留存基数',
|
||||
`refreshed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近刷新时间',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
@ -208,28 +181,6 @@
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏游戏维度周期用户去重明细'
|
||||
</update>
|
||||
|
||||
<update id="createRechargeDetailTable">
|
||||
CREATE TABLE IF NOT EXISTS `country_dashboard_recharge_detail` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`source_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '充值来源',
|
||||
`record_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源记录ID',
|
||||
`source_channel` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '来源渠道',
|
||||
`sys_origin` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '系统来源',
|
||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||
`country_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家码',
|
||||
`country_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNKNOWN' COMMENT '国家名称',
|
||||
`amount` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT 'USD金额',
|
||||
`coin_quantity` decimal(20,2) DEFAULT NULL COMMENT '金币数量',
|
||||
`remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
|
||||
`create_time` datetime NOT NULL COMMENT '业务创建时间',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_recharge_source_record` (`source_type`, `record_id`),
|
||||
KEY `idx_recharge_query` (`sys_origin`, `create_time`, `country_code`),
|
||||
KEY `idx_recharge_user` (`user_id`, `create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='国家数据大屏充值明细预聚合'
|
||||
</update>
|
||||
|
||||
<update id="ensurePeriodMetricTimezoneSchema">
|
||||
SET @country_dashboard_schema_lock = GET_LOCK('country_dashboard_period_timezone_schema', 60);
|
||||
SET @current_schema = DATABASE();
|
||||
@ -473,305 +424,6 @@
|
||||
DO IF(@country_dashboard_schema_lock = 1, RELEASE_LOCK('country_dashboard_period_timezone_schema'), 0);
|
||||
</update>
|
||||
|
||||
<update id="ensurePeriodMetricUserColumnsSchema">
|
||||
SET @country_dashboard_user_columns_schema_lock = GET_LOCK('country_dashboard_period_user_columns_schema', 60);
|
||||
SET @current_schema = DATABASE();
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'daily_active_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `daily_active_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日活跃用户数'' AFTER `country_new_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'daily_recharge_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `daily_active_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd1_retention_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d1_retention_user` bigint NOT NULL DEFAULT 0 COMMENT ''次日留存用户数'' AFTER `game_payout`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd1_retention_base_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d1_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT ''次日留存基数'' AFTER `d1_retention_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd7_retention_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d7_retention_user` bigint NOT NULL DEFAULT 0 COMMENT ''7日留存用户数'' AFTER `d1_retention_base_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd7_retention_base_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d7_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT ''7日留存基数'' AFTER `d7_retention_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd30_retention_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d30_retention_user` bigint NOT NULL DEFAULT 0 COMMENT ''30日留存用户数'' AFTER `d7_retention_base_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_user_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'd30_retention_base_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `d30_retention_base_user` bigint NOT NULL DEFAULT 0 COMMENT ''30日留存基数'' AFTER `d30_retention_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
DO IF(@country_dashboard_user_columns_schema_lock = 1, RELEASE_LOCK('country_dashboard_period_user_columns_schema'), 0);
|
||||
</update>
|
||||
|
||||
<update id="ensureMetricValueColumnsSchema">
|
||||
SET @country_dashboard_value_columns_schema_lock = GET_LOCK('country_dashboard_value_columns_schema', 60);
|
||||
SET @current_schema = DATABASE();
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'salary_transfer'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''工资转移'' AFTER `salary_exchange`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'daily_recharge_user'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `daily_recharge_user` bigint NOT NULL DEFAULT 0 COMMENT ''当日充值用户数'' AFTER `country_new_user`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'user_recharge'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'user_recharge'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''用户充值'' AFTER `dealer_recharge`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'new_dealer_user_recharge'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'new_dealer_user_recharge'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `new_dealer_user_recharge` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''新增币商用户充值'' AFTER `user_recharge`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'gift_consume'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''礼物消耗'' AFTER `salary_exchange`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'salary_transfer'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `salary_transfer` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''工资转移'' AFTER `salary_exchange`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_daily_metric'
|
||||
AND COLUMN_NAME = 'lucky_gift_anchor_share'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_daily_metric` ADD COLUMN `lucky_gift_anchor_share` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''幸运礼物主播分成'' AFTER `lucky_gift_payout`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'gift_consume'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `gift_consume` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''礼物消耗'' AFTER `salary_exchange`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = IF(
|
||||
@country_dashboard_value_columns_schema_lock = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @current_schema
|
||||
AND TABLE_NAME = 'country_dashboard_period_metric'
|
||||
AND COLUMN_NAME = 'lucky_gift_anchor_share'
|
||||
),
|
||||
'ALTER TABLE `country_dashboard_period_metric` ADD COLUMN `lucky_gift_anchor_share` decimal(20,2) NOT NULL DEFAULT 0.00 COMMENT ''幸运礼物主播分成'' AFTER `lucky_gift_payout`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
DO IF(@country_dashboard_value_columns_schema_lock = 1, RELEASE_LOCK('country_dashboard_value_columns_schema'), 0);
|
||||
</update>
|
||||
|
||||
<sql id="CountryColumns">
|
||||
NULLIF(u.country_code, '') AS countryCode,
|
||||
NULLIF(u.country_name, '') AS countryName
|
||||
@ -1180,21 +832,15 @@
|
||||
MAX(t.country_name) AS countryName,
|
||||
<include refid="PrecomputedPeriodColumns"/>,
|
||||
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
|
||||
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
|
||||
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
|
||||
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
|
||||
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
|
||||
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
|
||||
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
|
||||
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
|
||||
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
|
||||
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
|
||||
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
|
||||
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,
|
||||
IFNULL(SUM(t.lucky_gift_total_flow), 0) AS luckyGiftTotalFlow,
|
||||
IFNULL(SUM(t.lucky_gift_user), 0) AS luckyGiftUser,
|
||||
IFNULL(SUM(t.lucky_gift_payout), 0) AS luckyGiftPayout,
|
||||
IFNULL(SUM(t.lucky_gift_anchor_share), 0) AS luckyGiftAnchorShare,
|
||||
IFNULL(SUM(t.game_total_flow), 0) AS gameTotalFlow,
|
||||
IFNULL(SUM(t.game_user), 0) AS gameUser,
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout
|
||||
@ -1241,31 +887,18 @@
|
||||
t.period_key AS periodKey,
|
||||
MAX(t.period_name) AS periodName,
|
||||
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
|
||||
IFNULL(SUM(t.daily_active_user), 0) AS dailyActiveUser,
|
||||
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
|
||||
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
|
||||
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
|
||||
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
|
||||
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
|
||||
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
|
||||
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
|
||||
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
|
||||
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
|
||||
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
|
||||
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,
|
||||
IFNULL(SUM(t.lucky_gift_total_flow), 0) AS luckyGiftTotalFlow,
|
||||
IFNULL(SUM(t.lucky_gift_user), 0) AS luckyGiftUser,
|
||||
IFNULL(SUM(t.lucky_gift_payout), 0) AS luckyGiftPayout,
|
||||
IFNULL(SUM(t.lucky_gift_anchor_share), 0) AS luckyGiftAnchorShare,
|
||||
IFNULL(SUM(t.game_total_flow), 0) AS gameTotalFlow,
|
||||
IFNULL(SUM(t.game_user), 0) AS gameUser,
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout,
|
||||
IFNULL(SUM(t.d1_retention_user), 0) AS d1RetentionUser,
|
||||
IFNULL(SUM(t.d1_retention_base_user), 0) AS d1RetentionBaseUser,
|
||||
IFNULL(SUM(t.d7_retention_user), 0) AS d7RetentionUser,
|
||||
IFNULL(SUM(t.d7_retention_base_user), 0) AS d7RetentionBaseUser,
|
||||
IFNULL(SUM(t.d30_retention_user), 0) AS d30RetentionUser,
|
||||
IFNULL(SUM(t.d30_retention_base_user), 0) AS d30RetentionBaseUser
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = #{periodType}
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
@ -1330,21 +963,15 @@
|
||||
country_code,
|
||||
country_name,
|
||||
country_new_user,
|
||||
daily_recharge_user,
|
||||
new_user_recharge,
|
||||
official_recharge,
|
||||
mifapay_recharge,
|
||||
google_recharge,
|
||||
dealer_recharge,
|
||||
user_recharge,
|
||||
new_dealer_user_recharge,
|
||||
salary_exchange,
|
||||
salary_transfer,
|
||||
gift_consume,
|
||||
lucky_gift_total_flow,
|
||||
lucky_gift_user,
|
||||
lucky_gift_payout,
|
||||
lucky_gift_anchor_share,
|
||||
game_total_flow,
|
||||
game_user,
|
||||
game_payout,
|
||||
@ -1359,21 +986,15 @@
|
||||
#{item.countryCode},
|
||||
#{item.countryName},
|
||||
IFNULL(#{item.countryNewUser}, 0),
|
||||
IFNULL(#{item.dailyRechargeUser}, 0),
|
||||
IFNULL(#{item.newUserRecharge}, 0),
|
||||
IFNULL(#{item.officialRecharge}, 0),
|
||||
IFNULL(#{item.mifapayRecharge}, 0),
|
||||
IFNULL(#{item.googleRecharge}, 0),
|
||||
IFNULL(#{item.dealerRecharge}, 0),
|
||||
IFNULL(#{item.userRecharge}, 0),
|
||||
IFNULL(#{item.newDealerUserRecharge}, 0),
|
||||
IFNULL(#{item.salaryExchange}, 0),
|
||||
IFNULL(#{item.salaryTransfer}, 0),
|
||||
IFNULL(#{item.giftConsume}, 0),
|
||||
IFNULL(#{item.luckyGiftTotalFlow}, 0),
|
||||
IFNULL(#{item.luckyGiftUser}, 0),
|
||||
IFNULL(#{item.luckyGiftPayout}, 0),
|
||||
IFNULL(#{item.luckyGiftAnchorShare}, 0),
|
||||
IFNULL(#{item.gameTotalFlow}, 0),
|
||||
IFNULL(#{item.gameUser}, 0),
|
||||
IFNULL(#{item.gamePayout}, 0),
|
||||
@ -1383,21 +1004,15 @@
|
||||
ON DUPLICATE KEY UPDATE
|
||||
country_name = VALUES(country_name),
|
||||
country_new_user = VALUES(country_new_user),
|
||||
daily_recharge_user = VALUES(daily_recharge_user),
|
||||
new_user_recharge = VALUES(new_user_recharge),
|
||||
official_recharge = VALUES(official_recharge),
|
||||
mifapay_recharge = VALUES(mifapay_recharge),
|
||||
google_recharge = VALUES(google_recharge),
|
||||
dealer_recharge = VALUES(dealer_recharge),
|
||||
user_recharge = VALUES(user_recharge),
|
||||
new_dealer_user_recharge = VALUES(new_dealer_user_recharge),
|
||||
salary_exchange = VALUES(salary_exchange),
|
||||
salary_transfer = VALUES(salary_transfer),
|
||||
gift_consume = VALUES(gift_consume),
|
||||
lucky_gift_total_flow = VALUES(lucky_gift_total_flow),
|
||||
lucky_gift_user = VALUES(lucky_gift_user),
|
||||
lucky_gift_payout = VALUES(lucky_gift_payout),
|
||||
lucky_gift_anchor_share = VALUES(lucky_gift_anchor_share),
|
||||
game_total_flow = VALUES(game_total_flow),
|
||||
game_user = VALUES(game_user),
|
||||
game_payout = VALUES(game_payout),
|
||||
@ -1426,31 +1041,18 @@
|
||||
country_code,
|
||||
country_name,
|
||||
country_new_user,
|
||||
daily_active_user,
|
||||
daily_recharge_user,
|
||||
new_user_recharge,
|
||||
official_recharge,
|
||||
mifapay_recharge,
|
||||
google_recharge,
|
||||
dealer_recharge,
|
||||
user_recharge,
|
||||
new_dealer_user_recharge,
|
||||
salary_exchange,
|
||||
salary_transfer,
|
||||
gift_consume,
|
||||
lucky_gift_total_flow,
|
||||
lucky_gift_user,
|
||||
lucky_gift_payout,
|
||||
lucky_gift_anchor_share,
|
||||
game_total_flow,
|
||||
game_user,
|
||||
game_payout,
|
||||
d1_retention_user,
|
||||
d1_retention_base_user,
|
||||
d7_retention_user,
|
||||
d7_retention_base_user,
|
||||
d30_retention_user,
|
||||
d30_retention_base_user,
|
||||
refreshed_at
|
||||
)
|
||||
VALUES
|
||||
@ -1466,31 +1068,18 @@
|
||||
#{item.countryCode},
|
||||
#{item.countryName},
|
||||
IFNULL(#{item.countryNewUser}, 0),
|
||||
IFNULL(#{item.dailyActiveUser}, 0),
|
||||
IFNULL(#{item.dailyRechargeUser}, 0),
|
||||
IFNULL(#{item.newUserRecharge}, 0),
|
||||
IFNULL(#{item.officialRecharge}, 0),
|
||||
IFNULL(#{item.mifapayRecharge}, 0),
|
||||
IFNULL(#{item.googleRecharge}, 0),
|
||||
IFNULL(#{item.dealerRecharge}, 0),
|
||||
IFNULL(#{item.userRecharge}, 0),
|
||||
IFNULL(#{item.newDealerUserRecharge}, 0),
|
||||
IFNULL(#{item.salaryExchange}, 0),
|
||||
IFNULL(#{item.salaryTransfer}, 0),
|
||||
IFNULL(#{item.giftConsume}, 0),
|
||||
IFNULL(#{item.luckyGiftTotalFlow}, 0),
|
||||
IFNULL(#{item.luckyGiftUser}, 0),
|
||||
IFNULL(#{item.luckyGiftPayout}, 0),
|
||||
IFNULL(#{item.luckyGiftAnchorShare}, 0),
|
||||
IFNULL(#{item.gameTotalFlow}, 0),
|
||||
IFNULL(#{item.gameUser}, 0),
|
||||
IFNULL(#{item.gamePayout}, 0),
|
||||
IFNULL(#{item.d1RetentionUser}, 0),
|
||||
IFNULL(#{item.d1RetentionBaseUser}, 0),
|
||||
IFNULL(#{item.d7RetentionUser}, 0),
|
||||
IFNULL(#{item.d7RetentionBaseUser}, 0),
|
||||
IFNULL(#{item.d30RetentionUser}, 0),
|
||||
IFNULL(#{item.d30RetentionBaseUser}, 0),
|
||||
NOW()
|
||||
)
|
||||
</foreach>
|
||||
@ -1500,31 +1089,18 @@
|
||||
period_end_date = VALUES(period_end_date),
|
||||
country_name = VALUES(country_name),
|
||||
country_new_user = VALUES(country_new_user),
|
||||
daily_active_user = VALUES(daily_active_user),
|
||||
daily_recharge_user = VALUES(daily_recharge_user),
|
||||
new_user_recharge = VALUES(new_user_recharge),
|
||||
official_recharge = VALUES(official_recharge),
|
||||
mifapay_recharge = VALUES(mifapay_recharge),
|
||||
google_recharge = VALUES(google_recharge),
|
||||
dealer_recharge = VALUES(dealer_recharge),
|
||||
user_recharge = VALUES(user_recharge),
|
||||
new_dealer_user_recharge = VALUES(new_dealer_user_recharge),
|
||||
salary_exchange = VALUES(salary_exchange),
|
||||
salary_transfer = VALUES(salary_transfer),
|
||||
gift_consume = VALUES(gift_consume),
|
||||
lucky_gift_total_flow = VALUES(lucky_gift_total_flow),
|
||||
lucky_gift_user = VALUES(lucky_gift_user),
|
||||
lucky_gift_payout = VALUES(lucky_gift_payout),
|
||||
lucky_gift_anchor_share = VALUES(lucky_gift_anchor_share),
|
||||
game_total_flow = VALUES(game_total_flow),
|
||||
game_user = VALUES(game_user),
|
||||
game_payout = VALUES(game_payout),
|
||||
d1_retention_user = VALUES(d1_retention_user),
|
||||
d1_retention_base_user = VALUES(d1_retention_base_user),
|
||||
d7_retention_user = VALUES(d7_retention_user),
|
||||
d7_retention_base_user = VALUES(d7_retention_base_user),
|
||||
d30_retention_user = VALUES(d30_retention_user),
|
||||
d30_retention_base_user = VALUES(d30_retention_base_user),
|
||||
refreshed_at = NOW()
|
||||
</insert>
|
||||
|
||||
@ -1538,63 +1114,6 @@
|
||||
</if>
|
||||
</delete>
|
||||
|
||||
<insert id="upsertPeriodActiveRetentionMetrics">
|
||||
INSERT INTO country_dashboard_period_metric (
|
||||
period_type,
|
||||
period_key,
|
||||
stat_timezone,
|
||||
period_name,
|
||||
period_start_date,
|
||||
period_end_date,
|
||||
sys_origin,
|
||||
country_code,
|
||||
country_name,
|
||||
daily_active_user,
|
||||
d1_retention_user,
|
||||
d1_retention_base_user,
|
||||
d7_retention_user,
|
||||
d7_retention_base_user,
|
||||
d30_retention_user,
|
||||
d30_retention_base_user,
|
||||
refreshed_at
|
||||
)
|
||||
VALUES
|
||||
<foreach collection="metrics" item="item" separator=",">
|
||||
(
|
||||
#{item.periodType},
|
||||
#{item.periodKey},
|
||||
#{item.statTimezone},
|
||||
#{item.periodName},
|
||||
#{item.periodStartDate},
|
||||
#{item.periodEndDate},
|
||||
#{item.sysOrigin},
|
||||
#{item.countryCode},
|
||||
#{item.countryName},
|
||||
IFNULL(#{item.dailyActiveUser}, 0),
|
||||
IFNULL(#{item.d1RetentionUser}, 0),
|
||||
IFNULL(#{item.d1RetentionBaseUser}, 0),
|
||||
IFNULL(#{item.d7RetentionUser}, 0),
|
||||
IFNULL(#{item.d7RetentionBaseUser}, 0),
|
||||
IFNULL(#{item.d30RetentionUser}, 0),
|
||||
IFNULL(#{item.d30RetentionBaseUser}, 0),
|
||||
NOW()
|
||||
)
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
period_name = VALUES(period_name),
|
||||
period_start_date = VALUES(period_start_date),
|
||||
period_end_date = VALUES(period_end_date),
|
||||
country_name = VALUES(country_name),
|
||||
daily_active_user = VALUES(daily_active_user),
|
||||
d1_retention_user = VALUES(d1_retention_user),
|
||||
d1_retention_base_user = VALUES(d1_retention_base_user),
|
||||
d7_retention_user = VALUES(d7_retention_user),
|
||||
d7_retention_base_user = VALUES(d7_retention_base_user),
|
||||
d30_retention_user = VALUES(d30_retention_user),
|
||||
d30_retention_base_user = VALUES(d30_retention_base_user),
|
||||
refreshed_at = NOW()
|
||||
</insert>
|
||||
|
||||
<insert id="insertPeriodNewUserMetrics">
|
||||
INSERT IGNORE INTO country_dashboard_period_user_metric (
|
||||
period_type,
|
||||
@ -1756,7 +1275,6 @@
|
||||
t.period_key AS periodKey,
|
||||
MAX(t.period_name) AS periodName,
|
||||
SUM(CASE WHEN t.metric_type = 'COUNTRY_NEW_USER' THEN 1 ELSE 0 END) AS countryNewUser,
|
||||
SUM(CASE WHEN t.metric_type = 'RECHARGE_USER' THEN 1 ELSE 0 END) AS dailyRechargeUser,
|
||||
SUM(CASE WHEN t.metric_type = 'LUCKY_GIFT_USER' THEN 1 ELSE 0 END) AS luckyGiftUser,
|
||||
SUM(CASE WHEN t.metric_type = 'GAME_USER' THEN 1 ELSE 0 END) AS gameUser
|
||||
FROM country_dashboard_period_user_metric t
|
||||
@ -1776,30 +1294,16 @@
|
||||
MAX(t.country_name) AS countryName,
|
||||
'ALL' AS periodKey,
|
||||
'全部' AS periodName,
|
||||
IFNULL(SUM(t.country_new_user), 0) AS countryNewUser,
|
||||
IFNULL(SUM(t.daily_active_user), 0) AS dailyActiveUser,
|
||||
IFNULL(SUM(t.daily_recharge_user), 0) AS dailyRechargeUser,
|
||||
IFNULL(SUM(t.new_user_recharge), 0) AS newUserRecharge,
|
||||
IFNULL(SUM(t.official_recharge), 0) AS officialRecharge,
|
||||
IFNULL(SUM(t.mifapay_recharge), 0) AS mifapayRecharge,
|
||||
IFNULL(SUM(t.google_recharge), 0) AS googleRecharge,
|
||||
IFNULL(SUM(t.dealer_recharge), 0) AS dealerRecharge,
|
||||
IFNULL(SUM(t.user_recharge), 0) AS userRecharge,
|
||||
IFNULL(SUM(t.new_dealer_user_recharge), 0) AS newDealerUserRecharge,
|
||||
IFNULL(SUM(t.salary_exchange), 0) AS salaryExchange,
|
||||
IFNULL(SUM(t.salary_transfer), 0) AS salaryTransfer,
|
||||
IFNULL(SUM(t.gift_consume), 0) AS giftConsume,
|
||||
IFNULL(SUM(t.lucky_gift_total_flow), 0) AS luckyGiftTotalFlow,
|
||||
IFNULL(SUM(t.lucky_gift_payout), 0) AS luckyGiftPayout,
|
||||
IFNULL(SUM(t.lucky_gift_anchor_share), 0) AS luckyGiftAnchorShare,
|
||||
IFNULL(SUM(t.game_total_flow), 0) AS gameTotalFlow,
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout,
|
||||
IFNULL(SUM(t.d1_retention_user), 0) AS d1RetentionUser,
|
||||
IFNULL(SUM(t.d1_retention_base_user), 0) AS d1RetentionBaseUser,
|
||||
IFNULL(SUM(t.d7_retention_user), 0) AS d7RetentionUser,
|
||||
IFNULL(SUM(t.d7_retention_base_user), 0) AS d7RetentionBaseUser,
|
||||
IFNULL(SUM(t.d30_retention_user), 0) AS d30RetentionUser,
|
||||
IFNULL(SUM(t.d30_retention_base_user), 0) AS d30RetentionBaseUser
|
||||
IFNULL(SUM(t.game_payout), 0) AS gamePayout
|
||||
FROM country_dashboard_period_metric t
|
||||
WHERE t.period_type = 'DAY'
|
||||
AND t.stat_timezone = #{statTimezone}
|
||||
@ -1919,68 +1423,41 @@
|
||||
|
||||
<select id="countRechargeDetails" resultType="java.lang.Long">
|
||||
SELECT COUNT(1)
|
||||
FROM country_dashboard_recharge_detail t
|
||||
FROM (
|
||||
<include refid="RechargeDetailSource"/>
|
||||
) t
|
||||
INNER JOIN user_base_info u ON u.id = t.userId
|
||||
WHERE 1 = 1
|
||||
<if test="startTime != null">
|
||||
AND t.create_time >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND t.create_time < #{endTime}
|
||||
</if>
|
||||
<if test="countryKeyword != null and countryKeyword != ''">
|
||||
AND (
|
||||
t.country_code = #{countryKeyword}
|
||||
OR t.country_name LIKE CONCAT('%', #{countryKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="countryCodes != null and countryCodes != ''">
|
||||
AND FIND_IN_SET(t.country_code, #{countryCodes})
|
||||
</if>
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
<include refid="UserWhere"/>
|
||||
<include refid="CountryCodesWhere"/>
|
||||
</select>
|
||||
|
||||
<select id="listRechargeDetails"
|
||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRechargeDetailDTO">
|
||||
SELECT
|
||||
t.record_id AS recordId,
|
||||
t.source_type AS sourceType,
|
||||
t.source_channel AS sourceChannel,
|
||||
t.user_id AS userId,
|
||||
t.recordId,
|
||||
t.sourceType,
|
||||
t.sourceChannel,
|
||||
t.userId,
|
||||
u.account AS userAccount,
|
||||
u.user_nickname AS userNickname,
|
||||
t.country_code AS countryCode,
|
||||
t.country_name AS countryName,
|
||||
NULLIF(u.country_code, '') AS countryCode,
|
||||
NULLIF(u.country_name, '') AS countryName,
|
||||
t.amount,
|
||||
t.coin_quantity AS coinQuantity,
|
||||
t.coinQuantity,
|
||||
t.remark,
|
||||
DATE_FORMAT(
|
||||
CONVERT_TZ(t.create_time, #{storageTimezoneOffset}, #{statTimezoneOffset}),
|
||||
CONVERT_TZ(t.createTime, #{storageTimezoneOffset}, #{statTimezoneOffset}),
|
||||
'%Y-%m-%d %H:%i:%s'
|
||||
) AS createTime
|
||||
FROM country_dashboard_recharge_detail t
|
||||
LEFT JOIN user_base_info u ON u.id = t.user_id
|
||||
FROM (
|
||||
<include refid="RechargeDetailSource"/>
|
||||
) t
|
||||
INNER JOIN user_base_info u ON u.id = t.userId
|
||||
WHERE 1 = 1
|
||||
<if test="startTime != null">
|
||||
AND t.create_time >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND t.create_time < #{endTime}
|
||||
</if>
|
||||
<if test="countryKeyword != null and countryKeyword != ''">
|
||||
AND (
|
||||
t.country_code = #{countryKeyword}
|
||||
OR t.country_name LIKE CONCAT('%', #{countryKeyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="countryCodes != null and countryCodes != ''">
|
||||
AND FIND_IN_SET(t.country_code, #{countryCodes})
|
||||
</if>
|
||||
<if test="sysOrigin != null and sysOrigin != ''">
|
||||
AND t.sys_origin = #{sysOrigin}
|
||||
</if>
|
||||
ORDER BY t.create_time DESC, t.record_id DESC
|
||||
<include refid="UserWhere"/>
|
||||
<include refid="CountryCodesWhere"/>
|
||||
ORDER BY t.createTime DESC, t.recordId DESC
|
||||
LIMIT #{limit} OFFSET #{offset}
|
||||
</select>
|
||||
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
# 国家数据大屏口径对账与修复计划
|
||||
|
||||
## 基准说明
|
||||
|
||||
- 对账日期:`2026-05-27`
|
||||
- 统计时区:`Asia/Riyadh`
|
||||
- 统计窗口:`2026-05-27 00:00:00` 到 `2026-05-28 00:00:00`
|
||||
- 系统来源:`LIKEI`
|
||||
- 对账方式:旧 Java 源表口径结果 vs 当前 `country_dashboard_*` 预聚合结果。
|
||||
- 注意:日内数据会继续增长,金额和人数会随时间变化。本文中的“之前数据”来自本轮对账快照;后续修复验证必须重新跑同一窗口源表对账。
|
||||
|
||||
## 总体结论
|
||||
|
||||
当前大屏切到预聚合后,差异主要来自三类问题:
|
||||
|
||||
1. 旧 Java 是直接按 Mongo/SQL 源表字段聚合,CDC worker 有些字段改成了用户国家表、wallet 事件或部分表事件,口径没有完全复刻。
|
||||
2. 部分 Mongo 源表没有进入 CDC:日活、礼物流水、幸运礼物、工资转移等需要单独的 reconcile/增量任务。
|
||||
3. 留存基数与留存命中当前使用 worker 自己的用户国家表和活跃集合,和旧 Java 的注册 cohort + active log 口径不一致。
|
||||
|
||||
## 字段对账表
|
||||
|
||||
| 字段 | 旧 Java 口径 | 旧口径源表结果 | 当前预聚合结果 | 差异 | 差异原因 | 修复方案 | 状态 |
|
||||
| --- | --- | ---: | ---: | ---: | --- | --- | --- |
|
||||
| 新增 | 按 admin 用户列表展示口径统计 `user_base_info` 创建时间,展示日等于 `create_time + 8h` 后的日期;按用户国家归组。 | 之前用户列表显示约 57,部署期间增长到 60 | 修复前明显少算 | 修复前不稳定 | worker 原先按数据库原始时间截日,和 admin 用户列表展示日不一致。 | 已改 worker 的新用户统计窗口为 `create_time + 8h` 展示日;回填目标日期并重建 `DAY/ALL`。 | 已修 |
|
||||
| 日活 | Mongo `user_daily_active_log`,按 `activeDate + sysOrigin` 过滤;先按 `registerCountryCode,userId` 去重,再按 `registerCountryCode` 计数。 | 984 | 844 | -140 | worker 原先读取 active log 后,又强依赖 `dashboard_cdc_user_country` 做国家归类,用户国家缺失或删除标记会把活跃用户过滤掉;旧 Java 不查用户国家表。 | 已改 worker:日活直接使用 active log 的 `sysOrigin/registerCountryCode`;只有 active log 国家为空才 fallback 用户国家表。 | 已修 |
|
||||
| 新增充值 | 旧 Java 订单口径中,新注册用户在窗口内产生的充值。 | 2 | 2 | 0 | 当前已对齐。 | 保持现状;后续只做回归验证。 | 正常 |
|
||||
| Google 充值 | Google 订单成功支付金额,按订单完成时间落入统计窗口。 | 63 | 63 | 0 | 当前已对齐。 | 保持现状;后续只做回归验证。 | 正常 |
|
||||
| MifaPay 充值 | MifaPay 订单成功支付金额,按订单完成时间落入统计窗口。 | 0 | 0 | 0 | 当前已对齐。 | 保持现状;后续只做回归验证。 | 正常 |
|
||||
| 币商充值 | 币商/代理充值流水,旧 Java 从币商充值相关 SQL 表聚合。 | 0 | 0 | 0 | 当前已对齐。 | 保持现状;后续只做回归验证。 | 正常 |
|
||||
| 总充值 | Google + MifaPay + 币商等充值汇总。 | 63 | 63 | 0 | 当前已对齐。 | 保持现状;后续只做回归验证。 | 正常 |
|
||||
| 工资兑换 | 旧 Java 包含 SQL 工资账户流水和 Mongo bank 流水中的工资兑换口径。 | 6.75 | 0 | -6.75 | worker 当前没有完整接入工资兑换相关 SQL/Mongo 流水。 | 增加工资兑换 reconcile:读取旧 Java 使用的 SQL 表和 Mongo `user_bank_running_water` 条件,按用户国家归组写 `salary_exchange`;补回填。 | 待修 |
|
||||
| 工资转移 | 旧 Java 来自 Mongo `user_bank_running_water` 的工资转移口径。 | 38 | 0 | -38 | worker 当前未接入 Mongo bank 转移流水。 | 增加 Mongo bank reconcile:复刻旧 Java 的转移类型、状态、时间字段、金额单位,写 `salary_transfer`;补回填。 | 待修 |
|
||||
| 礼物消耗 | Mongo `gift_give_running_water`;旧 Java 条件包含 `refunded != true`、`userId != null`、窗口内 `createTime`、指定 `sysOrigin`、`luckyGift != true`、`originId` 为数字、`dynamicContentId` 为空或不存在;金额取 `giftValue.actualAmount`。 | 7,276,368 | 3,327,288 | -3,949,080 | worker 当前没有完整复刻 Mongo `gift_give_running_water` 条件,可能只覆盖部分礼物事件或国家映射。 | 新增/修正 gift reconcile,直接按旧 Java Mongo 条件聚合普通礼物消耗,写 `gift_consume`;不要从 wallet `LUCKY_GIFT` 反推普通礼物。 | 待修 |
|
||||
| 幸运礼物流水 | 旧 Java 从 `game_lucky_gift_count` 聚合幸运礼物总流水。 | 2,279,050 | 0 | -2,279,050 | `game_lucky_gift_count` 有源数据,但 worker/预聚合没有写入 `lucky_gift_total_flow`。 | 新增 lucky gift reconcile:按旧 Java 查询条件读取 `game_lucky_gift_count`,写 `lucky_gift_total_flow`,同时写用户去重表 `LUCKY_GIFT_USER`。 | 待修 |
|
||||
| 幸运礼物人数 | 旧 Java 从 `game_lucky_gift_count` 按用户去重统计参与人数。 | 101 | 0 | -101 | 同幸运礼物流水,用户去重表未写。 | lucky gift reconcile 写 `country_dashboard_period_user_metric.metric_type='LUCKY_GIFT_USER'`,再重建汇总人数。 | 待修 |
|
||||
| 幸运礼物返奖 | 旧 Java 从 `game_lucky_gift_count` 聚合返奖金额。 | 1,892,900 | 0 | -1,892,900 | 同幸运礼物流水,返奖字段未写。 | lucky gift reconcile 写 `lucky_gift_payout`。 | 待修 |
|
||||
| 幸运礼物利润 | `幸运礼物流水 - 幸运礼物返奖`。 | 386,150 | 0 | -386,150 | 流水和返奖都缺失导致利润为 0。 | 修复幸运礼物流水和返奖后自动恢复;利润不应单独写源数据。 | 待修 |
|
||||
| 游戏流水 | 旧 Java 从 `wallet_gold_asset_record_*` 和游戏统计表按游戏下注/消耗事件聚合。 | 109,688,935 | 74,509,547 | -35,179,388 | worker 当前游戏聚合少覆盖部分 `event_type` 或表来源,且用户去重也随之少算。 | 按旧 Java 的 game event_type 列表逐项核对,补齐 consume 事件映射;对每类事件做源表 vs 预聚合差异拆分。 | 待修 |
|
||||
| 游戏人数 | 旧 Java 按游戏流水参与用户去重。 | 176 | 113 | -63 | 游戏流水事件少覆盖,导致用户去重同步少算。 | 游戏流水修复时同步写 `GAME_USER` 去重表;重建 `DAY/ALL`。 | 待修 |
|
||||
| 游戏返奖 | 旧 Java 从游戏返奖/中奖事件聚合。 | 107,776,247 | 72,064,472 | -35,711,775 | worker 当前 payout 事件覆盖不完整,返奖少算幅度大于流水少算。 | 按旧 Java payout event_type 和 `game_lucky_box_count` 等来源补齐映射;验证 consume/payout 各自差值。 | 待修 |
|
||||
| 游戏利润 | `游戏流水 - 游戏返奖`。 | 1,912,688 | 2,445,075 | +532,387 | 流水和返奖都少算,但返奖少算更多,所以利润虚高。 | 修复游戏流水和游戏返奖后重算;利润不单独写源数据。 | 待修 |
|
||||
| 次日留存 | 旧 Java:注册 cohort 来自用户注册表;命中来自 Mongo `user_daily_active_log` 的目标活跃日期。 | 0 / 0 | 84 / 127 | +84 / +127 | 当前留存基数和命中口径与旧 Java 不一致,使用了 worker 用户国家表和当前 active 集合。 | 重写 retention reconcile:注册 cohort 复刻旧 Java `listNewUserCohorts`,活跃命中直接查 active log;按国家分别写 base/user。 | 待修 |
|
||||
| 七日留存 | 同上,目标日期为注册日 + 7。 | 0 / 0 | 25 / 60 | +25 / +60 | 同次日留存。 | 同 retention reconcile,支持 D7。 | 待修 |
|
||||
| 30 日留存 | 同上,目标日期为注册日 + 30。 | 0 / 0 | 5 / 7 | +5 / +7 | 同次日留存。 | 同 retention reconcile,支持 D30。 | 待修 |
|
||||
|
||||
## 已完成修复记录
|
||||
|
||||
### 新增
|
||||
|
||||
- 修改点:`dashboard-cdc-worker` 新增用户统计使用 admin 用户列表展示日,即 `create_time + 8h` 后的日期。
|
||||
- 验证结果:`2026-05-27` 修复后源数据和预聚合从 57 增长到 60,增长来自部署期间新增的 3 个用户。
|
||||
- 结论:新增已与用户列表显示口径对齐。
|
||||
|
||||
### 日活
|
||||
|
||||
- 修改点:`dashboard-cdc-worker/internal/storage/active_reconcile.go`
|
||||
- 提交:`0b5e687 Align daily active metric with active log`
|
||||
- 部署镜像:`dashboard-cdc-worker:daily-active-log-country-20260527-0b5e687`
|
||||
- 验证结果:`2026-05-27`
|
||||
- Mongo 旧 Java 口径:`1054`
|
||||
- `country_dashboard_period_metric` 预聚合:`1054`
|
||||
- 差值:`0`
|
||||
- 结论:日活已与旧 Java active log 口径对齐。
|
||||
|
||||
## 后续修复顺序建议
|
||||
|
||||
1. 工资兑换、工资转移:影响字段少,数据源明确,先补 Mongo/SQL reconcile。
|
||||
2. 礼物消耗:复刻 `gift_give_running_water` 条件,避免继续从 wallet 侧错误推导。
|
||||
3. 幸运礼物:直接接 `game_lucky_gift_count`,同时写金额和人数去重。
|
||||
4. 游戏流水、游戏返奖、游戏人数:按 event_type 拆分差异后逐类补齐。
|
||||
5. 留存:最后统一重写,因为它依赖新增口径和日活口径都稳定。
|
||||
|
||||
## 每项修复后的验证模板
|
||||
|
||||
每修一个字段,都必须跑同一套对账:
|
||||
|
||||
```sql
|
||||
-- 预聚合结果
|
||||
SELECT
|
||||
SUM(<metric_column>) AS precomputed_value
|
||||
FROM country_dashboard_period_metric
|
||||
WHERE period_type = 'DAY'
|
||||
AND period_key = '2026-05-27'
|
||||
AND stat_timezone = 'Asia/Riyadh'
|
||||
AND sys_origin = 'LIKEI';
|
||||
```
|
||||
|
||||
源表侧按旧 Java 条件单独查一遍,并输出:
|
||||
|
||||
| 字段 | 旧 Java 源表结果 | 预聚合结果 | 差值 | 是否通过 |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| 待验证字段 | 0 | 0 | 0 | 是/否 |
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.red.circle.external.inner.endpoint.message;
|
||||
|
||||
import com.red.circle.external.inner.endpoint.message.api.ImMessageClientApi;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
import com.red.circle.external.inner.service.message.ImMessageClientService;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.tool.core.parse.DataTypeUtils;
|
||||
@ -37,10 +36,4 @@ public class ImMessageClientEndpoint implements ImMessageClientApi {
|
||||
return ResultResponse.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultResponse<Void> sendCustomMessage(CustomC2cMsgBodyCmd cmd) {
|
||||
imMessageClientService.sendCustomMessage(cmd);
|
||||
return ResultResponse.success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package com.red.circle.external.inner.service.message;
|
||||
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
|
||||
/**
|
||||
* im 消息服务.
|
||||
*
|
||||
@ -14,9 +12,4 @@ public interface ImMessageClientService {
|
||||
*/
|
||||
void sendMessageText(String fromAccount, String toAccount, String text);
|
||||
|
||||
/**
|
||||
* 发送 C2C 自定义消息.
|
||||
*/
|
||||
void sendCustomMessage(CustomC2cMsgBodyCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
package com.red.circle.external.inner.service.message.impl;
|
||||
|
||||
import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.account.ImMessageManagerService;
|
||||
import com.red.circle.component.instant.msg.tencet.service.param.ImMessageParam;
|
||||
import com.red.circle.component.instant.msg.tencet.service.param.MessageBody;
|
||||
import com.red.circle.component.instant.msg.tencet.service.param.OfflinePushInfo;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2cMsgBodyCmd;
|
||||
import com.red.circle.external.inner.service.message.ImMessageClientService;
|
||||
import com.red.circle.tool.core.num.NumUtils;
|
||||
import com.red.circle.tool.core.parse.DataTypeUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -29,21 +23,4 @@ public class ImMessageClientServiceImpl implements ImMessageClientService {
|
||||
imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomMessage(CustomC2cMsgBodyCmd cmd) {
|
||||
ImMessageParam param = ImMessageParam.builder()
|
||||
.syncOtherMachine(cmd.getSyncOtherMachine())
|
||||
.fromAccount(cmd.getFromAccount())
|
||||
.toAccount(cmd.getToAccount())
|
||||
.offlinePushInfo(OfflinePushInfo.builder()
|
||||
.pushFlag(0)
|
||||
.build())
|
||||
.msgRandom(DataTypeUtils.toInteger(NumUtils.getRandomNumberString(9)))
|
||||
.build()
|
||||
.addMsgBody(MessageBody.customBody(cmd.getDesc(), cmd.getData()));
|
||||
imMessageManagerService.sendMessage(param)
|
||||
.subscribe(res -> log.warn("sendCustomMessage response result:{}", res),
|
||||
res -> log.error("sendCustomMessage error cmd={}, result={}", cmd, res));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -109,7 +109,6 @@ public class AppInAppPurchaseCmdExe {
|
||||
.setSysOrigin(history.getSysOrigin())
|
||||
.setPlatform(history.getPlatform())
|
||||
.setPayPlatform(history.getPayPlatform())
|
||||
.setAppVersion(reqAppVersion(cmd))
|
||||
.setOrigin(appCommodity.getProductConfigProductId())
|
||||
.setOriginName(appCommodity.getOriginDescription())
|
||||
.setExpireDateMs(history.getExpiresDateMs())
|
||||
@ -169,11 +168,6 @@ public class AppInAppPurchaseCmdExe {
|
||||
return MonthlyRechargeType.UNDEFINED;
|
||||
}
|
||||
|
||||
private String reqAppVersion(AbstractPurchaseCmd cmd) {
|
||||
return Objects.nonNull(cmd.getReqAppIntel())
|
||||
? Objects.toString(cmd.getReqAppIntel().getVersion(), "")
|
||||
: "";
|
||||
}
|
||||
private void saveFriendshipRecord(AppCommodity appCommodity) {
|
||||
PurchaseHistory purchaseHistory = appCommodity.getPurchaseHistory();
|
||||
orderPurchaseFriendshipRecordService.save(
|
||||
|
||||
@ -49,14 +49,6 @@ public class PayCountryAliasResolver {
|
||||
return getMappedUsablePayCountryByCountryId(countryId);
|
||||
}
|
||||
|
||||
public PayCountry resolvePayCountryByCountryId(Long countryId) {
|
||||
PayCountry usablePayCountry = resolveUsablePayCountryByCountryId(countryId);
|
||||
if (Objects.nonNull(usablePayCountry)) {
|
||||
return usablePayCountry;
|
||||
}
|
||||
return getShelfPayCountryByCountryId(countryId);
|
||||
}
|
||||
|
||||
private PayCountry getMappedUsablePayCountryByCountryId(Long countryId) {
|
||||
SysCountryCodeDTO countryCode = getCountryCodeById(countryId);
|
||||
if (Objects.isNull(countryCode)) {
|
||||
@ -122,21 +114,6 @@ public class PayCountryAliasResolver {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private PayCountry getShelfPayCountryByCountryId(Long countryId) {
|
||||
if (Objects.isNull(countryId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return payCountryService.query()
|
||||
.eq(PayCountry::getShelf, Boolean.TRUE)
|
||||
.eq(PayCountry::getCountryId, countryId)
|
||||
.orderByDesc(PayCountry::getSort)
|
||||
.list()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private boolean isUsablePayCountry(PayCountry payCountry) {
|
||||
return Objects.nonNull(payCountry)
|
||||
&& StringUtils.isNotBlank(payCountry.getCurrency())
|
||||
|
||||
@ -5,7 +5,6 @@ import com.google.api.client.util.Maps;
|
||||
import com.google.api.client.util.Sets;
|
||||
import com.red.circle.common.business.core.amount.PayAmountArithmeticUtils;
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.common.business.core.util.CountryCodeAliasUtils;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.response.CommonErrorCode;
|
||||
import com.red.circle.order.app.convertor.PayAppConvertor;
|
||||
@ -24,8 +23,6 @@ import com.red.circle.order.infra.database.rds.service.pay.PayApplicationCommodi
|
||||
import com.red.circle.order.infra.database.rds.service.pay.PayApplicationService;
|
||||
import com.red.circle.order.infra.database.rds.service.pay.PayChannelService;
|
||||
import com.red.circle.order.infra.database.rds.service.pay.PayCountryChannelDetailsService;
|
||||
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
|
||||
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.num.ArithmeticUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
@ -57,7 +54,6 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
private final PayCountryAliasResolver payCountryAliasResolver;
|
||||
private final PayApplicationCommodityService payApplicationCommodityService;
|
||||
private final PayCountryChannelDetailsService payCountryChannelDetailsService;
|
||||
private final SysCountryCodeClient sysCountryCodeClient;
|
||||
|
||||
public ApplicationCommodityCardV2CO execute(PayWebApplicationCommodityCmd cmd) {
|
||||
|
||||
@ -76,20 +72,6 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
.setApplication(application);
|
||||
}
|
||||
|
||||
// LIKEI/Yumi 的 H5 支付方式由本地 JSON 控制,商品接口只负责按后台商品配置返回商品。
|
||||
// 如果这里继续依赖支付国家渠道详情,JSON 已配置但后端渠道未上架时会把商品误过滤为空。
|
||||
List<PayApplicationCommodityCO> commodity = toCommodityCO(payCountry,
|
||||
payApplicationCommodities);
|
||||
if (CollectionUtils.isEmpty(commodity)) {
|
||||
return new ApplicationCommodityCardV2CO()
|
||||
.setApplication(application);
|
||||
}
|
||||
if (isJsonControlledPayment(cmd)) {
|
||||
return new ApplicationCommodityCardV2CO()
|
||||
.setApplication(application)
|
||||
.setCommodity(commodity);
|
||||
}
|
||||
|
||||
// 所有支持的渠道
|
||||
List<PayCountryChannelDetails> countryChannelDetails = payCountryChannelDetailsService
|
||||
.listChannelCode(cmd.getPayCountryId());
|
||||
@ -99,6 +81,15 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
.setApplication(application);
|
||||
}
|
||||
|
||||
// 商品
|
||||
List<PayApplicationCommodityCO> commodity = toCommodityCO(payCountry,
|
||||
payApplicationCommodities);
|
||||
|
||||
if (CollectionUtils.isEmpty(commodity)) {
|
||||
return new ApplicationCommodityCardV2CO()
|
||||
.setApplication(application);
|
||||
}
|
||||
|
||||
// 过滤渠道限额范围
|
||||
countryChannelDetails = filterCountryChannelLimit(countryChannelDetails, commodity);
|
||||
|
||||
@ -125,10 +116,8 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
private List<PayApplicationCommodity> getPayApplicationCommodities(
|
||||
PayWebApplicationCommodityCmd cmd) {
|
||||
|
||||
// Yumi/LIKEI H5 已经先按用户资料拿到国家,商品应按国家查;regionId 只保留给旧的区域商品链路.
|
||||
if (Objects.equals(cmd.requireReqSysOriginChildEnum(), SysOriginPlatformEnum.YOLO)
|
||||
|| (StringUtils.isNotBlank(cmd.getRegionId())
|
||||
&& StringUtils.isBlank(cmd.getCountryCode()))) {
|
||||
|| StringUtils.isNotBlank(cmd.getRegionId())) {
|
||||
|
||||
ResponseAssert
|
||||
.isTrue(CommonErrorCode.NOT_SUPPORTED_REGION, StringUtils.isNotBlank(cmd.getRegionId()));
|
||||
@ -143,10 +132,6 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
cmd.getType(), null);
|
||||
}
|
||||
|
||||
private boolean isJsonControlledPayment(PayWebApplicationCommodityCmd cmd) {
|
||||
return Objects.equals(cmd.requireReqSysOriginChildEnum(), SysOriginPlatformEnum.LIKEI);
|
||||
}
|
||||
|
||||
private List<PayCountryChannelDetails> filterCountryChannelLimit(
|
||||
List<PayCountryChannelDetails> countryChannelDetails,
|
||||
List<PayApplicationCommodityCO> commodity) {
|
||||
@ -180,19 +165,9 @@ public class PayWebApplicationCommodityV2QueryExe {
|
||||
}
|
||||
|
||||
private PayCountry getPayCountry(PayWebApplicationCommodityCmd cmd) {
|
||||
if (Objects.nonNull(cmd.getPayCountryId()) || StringUtils.isBlank(cmd.getCountryCode())) {
|
||||
return payCountryAliasResolver.resolveUsablePayCountry(cmd.getPayCountryId());
|
||||
}
|
||||
|
||||
String normalizedCountryCode = CountryCodeAliasUtils.normalizePaymentCountryCode(
|
||||
cmd.getCountryCode());
|
||||
SysCountryCodeDTO countryCode = ResponseAssert.requiredSuccess(
|
||||
sysCountryCodeClient.getByCode(normalizedCountryCode));
|
||||
ResponseAssert.notNull(CommonErrorCode.NOT_SUPPORTED_REGION, countryCode);
|
||||
// H5 只传用户国家码;这里仅把真实国家转成商品表使用的支付国家ID,是否开放支付国家由 H5 JSON 控制.
|
||||
return payCountryAliasResolver.resolvePayCountryByCountryId(countryCode.getId());
|
||||
}
|
||||
|
||||
private List<PayChannelDetailsCO> getPayChannels(List<PayChannel> payChannels,
|
||||
List<PayCountryChannelDetails> details) {
|
||||
|
||||
|
||||
@ -26,10 +26,8 @@ public class PayWebOrderStatusQueryExe {
|
||||
public PayOrderStatusCO execute(PayOrderStatusCmd cmd) {
|
||||
OrderUserPurchasePay mysqlOrder = getMysqlOrder(cmd.getOrderId());
|
||||
ResponseAssert.notNull(OrderErrorCode.NO_PURCHASE_RECORD_FOUND, mysqlOrder);
|
||||
Long expectedUserId = Objects.nonNull(cmd.getReqUserId()) ? cmd.getReqUserId() : cmd.getUserId();
|
||||
// 登录态请求继续使用网关注入的 reqUserId;公开 H5 轮询没有登录态,只允许用下单用户ID校验同一笔订单.
|
||||
ResponseAssert.isTrue(OrderErrorCode.NO_PURCHASE_RECORD_FOUND,
|
||||
Objects.nonNull(expectedUserId) && Objects.equals(mysqlOrder.getUserId(), expectedUserId));
|
||||
Objects.equals(mysqlOrder.getUserId(), cmd.getReqUserId()));
|
||||
InAppPurchaseStatus status = MiFaPayMysqlOrderSupport
|
||||
.toInAppPurchaseStatus(mysqlOrder.getPayStatus());
|
||||
return new PayOrderStatusCO()
|
||||
|
||||
@ -121,7 +121,6 @@ public class PayWebPlaceAnOrderCmdExe {
|
||||
+ "&appType=" + commodity.getType());
|
||||
details.setCancelRedirectUrl(details.getRechargeUrl());
|
||||
details.setNewVersion(cmd.getNewVersion());
|
||||
details.setAppVersion(cmd.getAppVersion());
|
||||
details.setRequestIp(IpUtils.getIpAddr());
|
||||
|
||||
// 记录充值金额
|
||||
|
||||
@ -18,11 +18,13 @@ import com.red.circle.tool.core.regex.RegexConstant;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
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.reigon.RegionRelationDTO;
|
||||
import com.red.circle.other.inner.enums.user.RegionRelationGroupEnum;
|
||||
import com.red.circle.other.inner.endpoint.user.region.RegionRelationClient;
|
||||
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
|
||||
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
|
||||
import com.red.circle.wallet.inner.endpoint.freight.FreightGoldClient;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -45,6 +47,7 @@ public class UserProfileAndCountryQueryExe {
|
||||
private final PayCountryAliasResolver payCountryAliasResolver;
|
||||
private final UserProfileClient userProfileClient;
|
||||
private final SysCountryCodeClient sysCountryCodeClient;
|
||||
private final RegionRelationClient regionRelationClient;
|
||||
|
||||
public UserProfileAndCountryCO execute(PayWebUserCmd cmd) {
|
||||
|
||||
@ -61,15 +64,25 @@ public class UserProfileAndCountryQueryExe {
|
||||
.toWebSiteUseProfileCO(userProfile);
|
||||
webSiteUseProfile.setAccount(userProfile.actualAccount());
|
||||
|
||||
List<PayCountry> payCountryList;
|
||||
|
||||
String regionId = ResponseAssert.requiredSuccess(
|
||||
userRegionClient.getRegionId(userProfile.getId())
|
||||
);
|
||||
|
||||
if (Objects.equals(cmd.getSysOrigin(), SysOriginPlatformEnum.LIKEI.name())) {
|
||||
return toLikeiUserCountryResult(userProfile, webSiteUseProfile, regionId);
|
||||
ResponseAssert.isTrue(CommonErrorCode.NOT_SUPPORTED_REGION, StringUtils.isNotBlank(regionId));
|
||||
|
||||
List<RegionRelationDTO> regionRelationList = getRegionRelationList(userProfile, regionId);
|
||||
ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, regionRelationList);
|
||||
|
||||
payCountryList = getPayCountryList(regionRelationList);
|
||||
ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, payCountryList);
|
||||
|
||||
} else {
|
||||
payCountryList = payCountryService.listAllShelf();
|
||||
}
|
||||
|
||||
List<PayCountry> payCountryList = payCountryService.listAllShelf();
|
||||
Map<Long, SysCountryCodeDTO> mapCountryCode = getMapCountryCode(payCountryList);
|
||||
ResponseAssert.notEmpty(CommonErrorCode.NOT_SUPPORTED_REGION, mapCountryCode);
|
||||
Map<Long, PayCountry> compatiblePayCountryMap = getCompatiblePayCountryMap(payCountryList);
|
||||
@ -96,57 +109,6 @@ public class UserProfileAndCountryQueryExe {
|
||||
|
||||
}
|
||||
|
||||
private UserProfileAndCountryCO toLikeiUserCountryResult(UserProfileDTO userProfile,
|
||||
WebSiteUseProfileCO webSiteUseProfile, String regionId) {
|
||||
|
||||
SysCountryCodeDTO countryCode = getUserCountryCode(userProfile);
|
||||
ResponseAssert.notNull(CommonErrorCode.NOT_SUPPORTED_REGION, countryCode);
|
||||
|
||||
PayCountry compatiblePayCountry = payCountryAliasResolver
|
||||
.resolvePayCountryByCountryId(countryCode.getId());
|
||||
boolean freightUser = existsFreightBalance(userProfile.getId());
|
||||
webSiteUseProfile
|
||||
.setFreightUser(freightUser)
|
||||
// Yumi H5 先确认用户身份,再用这里返回的商品类型查询后台商品,避免币商误买普通金币商品后入普通金币账户.
|
||||
.setCommodityType(freightUser ? PayApplicationCommodityType.FREIGHT_GOLD.name()
|
||||
: PayApplicationCommodityType.GOLD.name());
|
||||
|
||||
// LIKEI/Yumi 的支付国家开放由 H5 JSON 控制;这里仅返回用户真实国家,商品接口再按国家码查该国商品.
|
||||
PayCountryCO payCountryCO = new PayCountryCO()
|
||||
.setId(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getId() : null)
|
||||
.setPayCountryId(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getId() : null)
|
||||
.setCountryId(countryCode.getId())
|
||||
.setCurrency(Objects.nonNull(compatiblePayCountry) ? compatiblePayCountry.getCurrency() : null)
|
||||
.setCountry(countryCode);
|
||||
|
||||
return new UserProfileAndCountryCO()
|
||||
.setCountryList(Collections.singletonList(payCountryCO))
|
||||
.setUserProfile(webSiteUseProfile)
|
||||
.setRegionId(regionId);
|
||||
}
|
||||
|
||||
private boolean existsFreightBalance(Long userId) {
|
||||
return Boolean.TRUE.equals(ResponseAssert.requiredSuccess(
|
||||
freightGoldClient.existsBalance(userId)
|
||||
));
|
||||
}
|
||||
|
||||
private SysCountryCodeDTO getUserCountryCode(UserProfileDTO userProfile) {
|
||||
if (Objects.nonNull(userProfile.getCountryId())) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
sysCountryCodeClient.getById(userProfile.getCountryId())
|
||||
);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(userProfile.getCountryCode())) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
sysCountryCodeClient.getByCode(userProfile.getCountryCode())
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private UserProfileDTO getUserProfile(PayWebUserCmd cmd) {
|
||||
if (StringUtils.isNotBlank(cmd.getAccount()) && StringUtils.isNotBlank(cmd.getSysOrigin())) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
@ -184,4 +146,18 @@ public class UserProfileAndCountryQueryExe {
|
||||
(left, right) -> left));
|
||||
}
|
||||
|
||||
private List<RegionRelationDTO> getRegionRelationList(UserProfileDTO userProfile,
|
||||
String regionId) {
|
||||
return ResponseAssert.requiredSuccess(
|
||||
regionRelationClient
|
||||
.listByRegionId(regionId, userProfile.getOriginSys(),
|
||||
RegionRelationGroupEnum.OPEN_PAY_COUNTRY)
|
||||
);
|
||||
}
|
||||
|
||||
private List<PayCountry> getPayCountryList(List<RegionRelationDTO> regionRelationList) {
|
||||
return payCountryService
|
||||
.listByIds(regionRelationList.stream().map(RegionRelationDTO::getRelationId).collect(
|
||||
Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,10 +115,6 @@ public class PayPlaceAnOrderDetailsV2 implements Serializable {
|
||||
*/
|
||||
private Boolean newVersion;
|
||||
|
||||
/**
|
||||
* 本次充值请求的App版本.
|
||||
*/
|
||||
private String appVersion;
|
||||
/**
|
||||
* 请求ip信息.
|
||||
*/
|
||||
@ -160,7 +156,6 @@ public class PayPlaceAnOrderDetailsV2 implements Serializable {
|
||||
.put("countryCode", this.getCountryCode())
|
||||
.put("amount", this.getAmount().toString())
|
||||
.put("currency", this.getCurrency())
|
||||
.put("appVersion", Objects.toString(this.getAppVersion(), ""))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.red.circle.order.app.command.pay.web.strategy;
|
||||
|
||||
import com.google.api.client.util.Maps;
|
||||
import com.red.circle.component.pay.paymax.PayMaxUtils;
|
||||
import com.red.circle.framework.core.request.RequestClientEnum;
|
||||
import com.red.circle.framework.web.props.EnvProperties;
|
||||
@ -20,13 +21,8 @@ import com.red.circle.tool.core.date.TimestampUtils;
|
||||
import com.red.circle.tool.core.http.RcHttpClient;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -108,7 +104,7 @@ public class PayPlaceAnOrderService {
|
||||
.setNoticeData(response.extra())
|
||||
.setCreateTime(TimestampUtils.now())
|
||||
))
|
||||
.setMetadata(param.createMetadata())
|
||||
.setMetadata(Maps.newHashMap())
|
||||
.setVersion(0)
|
||||
);
|
||||
|
||||
@ -126,27 +122,6 @@ public class PayPlaceAnOrderService {
|
||||
* @return 汇率值
|
||||
*/
|
||||
public BigDecimal getRealTimeExchangeRate(String baseCurrency, String targetCurrency) {
|
||||
return getRealTimeExchangeRates(baseCurrency, List.of(targetCurrency)).get(targetCurrency);
|
||||
}
|
||||
|
||||
public Map<String, BigDecimal> getRealTimeExchangeRates(String baseCurrency,
|
||||
Collection<String> targetCurrencies) {
|
||||
Map<String, BigDecimal> result = new HashMap<>();
|
||||
Optional.ofNullable(targetCurrencies).orElseGet(List::of).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(String::trim)
|
||||
.filter(currency -> !currency.isEmpty())
|
||||
.distinct()
|
||||
.forEach(targetCurrency -> {
|
||||
BigDecimal rate = queryRealTimeExchangeRate(baseCurrency, targetCurrency);
|
||||
if (Objects.nonNull(rate)) {
|
||||
result.put(targetCurrency, rate);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal queryRealTimeExchangeRate(String baseCurrency, String targetCurrency) {
|
||||
try {
|
||||
QuoteQueryParam param = new QuoteQueryParam();
|
||||
QuoteQueryParam.QuoteQueryData data = new QuoteQueryParam.QuoteQueryData();
|
||||
|
||||
@ -9,7 +9,6 @@ import com.red.circle.framework.core.response.CommonErrorCode;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.framework.web.props.EnvProperties;
|
||||
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
|
||||
import com.red.circle.order.app.common.task.RechargeSuccessEventPublisher;
|
||||
import com.red.circle.order.app.command.pay.web.strategy.PayEnv;
|
||||
import com.red.circle.order.domain.gateway.UserRechargeCountGateway;
|
||||
import com.red.circle.order.infra.database.mongo.order.InAppPurchaseCollectionReceiptService;
|
||||
@ -80,7 +79,6 @@ public class InAppPurchaseCommon {
|
||||
private final PropsActivityClient propsActivityClient;
|
||||
private final UserOneTimeTaskClient userOneTimeTaskClient;
|
||||
private final UserProfileClient userProfileClient;
|
||||
private final RechargeSuccessEventPublisher rechargeSuccessEventPublisher;
|
||||
|
||||
public void inAppPurchaseReceipt(InAppPurchaseDetails order) {
|
||||
inAppPurchaseReceipt(order,
|
||||
@ -198,8 +196,6 @@ public class InAppPurchaseCommon {
|
||||
inviteUserClient.processRechargeCommission(amount,
|
||||
order.getSysOrigin(), order.getAcceptUserId());
|
||||
|
||||
rechargeSuccessEventPublisher.reportPaymentRecharge(order);
|
||||
|
||||
// 发送首充奖励
|
||||
completedFirstCharge(order.getAcceptUserId(), order.getSysOrigin());
|
||||
}
|
||||
|
||||
@ -15,9 +15,7 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -63,7 +61,6 @@ public final class MiFaPayMysqlOrderSupport {
|
||||
.setFactoryCode(param.getFactoryCode())
|
||||
.setFactoryOrderId(Objects.toString(response.getTradeNo(), ""))
|
||||
.setReferenceId(Objects.toString(param.getApplicationId(), ""))
|
||||
.setAppVersion(Objects.toString(param.getAppVersion(), ""))
|
||||
.setReceiptType(Objects.toString(param.getReceiptType(),
|
||||
InAppPurchaseReceiptType.PAYMENT.name()))
|
||||
.setPaymentChannel(Objects.toString(param.getChannelCode(), ""))
|
||||
@ -124,8 +121,7 @@ public final class MiFaPayMysqlOrderSupport {
|
||||
.setReason(order.getReason())
|
||||
.setCreateTime(order.getCreateTime())
|
||||
.setUpdateTime(order.getUpdateTime())
|
||||
.setCreateUser(order.getCreateUser())
|
||||
.setMetadata(metadata(order));
|
||||
.setCreateUser(order.getCreateUser());
|
||||
}
|
||||
|
||||
public static InAppPurchaseStatus toInAppPurchaseStatus(String payStatus) {
|
||||
@ -166,14 +162,6 @@ public final class MiFaPayMysqlOrderSupport {
|
||||
return InAppPurchaseReceiptType.PAYMENT;
|
||||
}
|
||||
|
||||
private static Map<String, String> metadata(OrderUserPurchasePay order) {
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
if (StringUtils.isNotBlank(order.getAppVersion())) {
|
||||
metadata.put("appVersion", order.getAppVersion());
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static BigDecimal defaultDecimal(BigDecimal value) {
|
||||
return value != null ? value : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@ -1,242 +0,0 @@
|
||||
package com.red.circle.order.app.common.task;
|
||||
|
||||
import com.red.circle.component.mq.MessageEvent;
|
||||
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
|
||||
import com.red.circle.mq.business.model.event.pay.RechargeSuccessEvent;
|
||||
import com.red.circle.mq.rocket.business.service.MessageSenderService;
|
||||
import com.red.circle.mq.rocket.business.streams.RechargeSuccessSink;
|
||||
import com.red.circle.order.infra.database.mongo.order.entity.InAppPurchaseDetails;
|
||||
import com.red.circle.order.infra.database.mongo.order.entity.assist.InAppPurchaseProduct;
|
||||
import com.red.circle.order.inner.model.enums.PayApplicationCommodityType;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RechargeSuccessEventPublisher {
|
||||
|
||||
private static final String GOOGLE = "GOOGLE";
|
||||
private static final String APPLE = "APPLE";
|
||||
private static final String THIRD_PARTY = "THIRD_PARTY";
|
||||
private static final String UNKNOWN = "UNKNOWN";
|
||||
|
||||
private final ObjectProvider<MessageSenderService> messageSenderServiceProvider;
|
||||
|
||||
@Value("${recharge.success.mq.enabled:true}")
|
||||
private boolean mqEnabled;
|
||||
|
||||
@Value("${recharge.success.mq.topic:RC_DEFAULT_APP_ORDINARY}")
|
||||
private String mqTopic;
|
||||
|
||||
@Value("${recharge.success.mq.tag:" + RechargeSuccessSink.TAG + "}")
|
||||
private String mqTag;
|
||||
|
||||
public RechargeSuccessEventPublisher(ObjectProvider<MessageSenderService> messageSenderServiceProvider) {
|
||||
this.messageSenderServiceProvider = messageSenderServiceProvider;
|
||||
}
|
||||
|
||||
public void reportAppPurchaseRecharge(BuySuccessEvent event) {
|
||||
if (Objects.isNull(event)) {
|
||||
return;
|
||||
}
|
||||
Long userId = event.getAcceptUserId();
|
||||
Long purchaseHistoryId = event.getPurchaseHistoryId();
|
||||
BigDecimal amountUsd = Objects.nonNull(event.getProductConfig())
|
||||
? event.getProductConfig().getUnitPrice()
|
||||
: null;
|
||||
Long amountCents = toCents(amountUsd);
|
||||
if (Objects.isNull(userId) || Objects.isNull(purchaseHistoryId) || !positive(amountCents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String sourceOrderId = String.valueOf(purchaseHistoryId);
|
||||
String payPlatform = normalizePayPlatform(defaultIfBlank(event.getPayPlatform(), event.getPlatform()));
|
||||
String paymentMethod = classifyPaymentMethod(payPlatform);
|
||||
String appVersion = defaultIfBlank(event.getAppVersion(), "");
|
||||
String productId = Objects.nonNull(event.getProductConfig())
|
||||
? defaultIfBlank(event.getProductConfig().getProductId(), event.getOrigin())
|
||||
: defaultIfBlank(event.getOrigin(), "");
|
||||
String googleProductId = Objects.equals(GOOGLE, payPlatform) ? productId : "";
|
||||
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("payerUserId", event.getUserId());
|
||||
payload.put("acceptUserId", userId);
|
||||
payload.put("purchaseHistoryId", purchaseHistoryId);
|
||||
payload.put("candyQuantity", event.getCandyQuantity());
|
||||
payload.put("platform", event.getPlatform());
|
||||
payload.put("payPlatform", event.getPayPlatform());
|
||||
payload.put("appVersion", event.getAppVersion());
|
||||
payload.put("productId", productId);
|
||||
payload.put("googleProductId", googleProductId);
|
||||
|
||||
publish(new RechargeSuccessEvent()
|
||||
.setEventId("RECHARGE_SUCCESS:" + paymentMethod + ":" + sourceOrderId)
|
||||
.setSysOrigin(event.getSysOrigin())
|
||||
.setUserId(userId)
|
||||
.setAmountCents(amountCents)
|
||||
.setAmountUsd(formatAmount(amountUsd))
|
||||
.setCurrency("USD")
|
||||
.setPayPlatform(payPlatform)
|
||||
.setPaymentMethod(paymentMethod)
|
||||
.setProductId(productId)
|
||||
.setGoogleProductId(googleProductId)
|
||||
.setAppVersion(appVersion)
|
||||
.setSourceOrderId(sourceOrderId)
|
||||
.setOrderId(sourceOrderId)
|
||||
.setOccurredAt(toInstantString(event.getBuyDateTime()))
|
||||
.setPayload(payload));
|
||||
}
|
||||
|
||||
public void reportPaymentRecharge(InAppPurchaseDetails order) {
|
||||
if (Objects.isNull(order) || !order.receiptTypeEqPayment()) {
|
||||
return;
|
||||
}
|
||||
InAppPurchaseProduct product = order.firstProduct();
|
||||
if (Objects.isNull(product) || !PayApplicationCommodityType.GOLD.eq(product.getCode())) {
|
||||
return;
|
||||
}
|
||||
String payPlatform = normalizePayPlatform(Objects.nonNull(order.getFactory()) ? order.getFactory().getFactoryCode() : "");
|
||||
String paymentMethod = classifyPaymentMethod(payPlatform);
|
||||
|
||||
BigDecimal amountUsd = firstPositive(order.getAmountUsd(), product.getAmountUsd());
|
||||
Long amountCents = toCents(amountUsd);
|
||||
if (Objects.isNull(order.getAcceptUserId()) || isBlank(order.getId()) || !positive(amountCents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("trackId", order.getTrackId());
|
||||
payload.put("sourceOrderId", order.getId());
|
||||
payload.put("orderId", order.getOrderId());
|
||||
payload.put("factoryCode", payPlatform);
|
||||
payload.put("factoryChannelCode", Objects.nonNull(order.getFactory()) ? order.getFactory().getFactoryChannelCode() : null);
|
||||
payload.put("platform", Objects.nonNull(order.getFactory()) ? order.getFactory().getPlatform() : null);
|
||||
payload.put("productCode", product.getCode());
|
||||
payload.put("productContent", product.getContent());
|
||||
String appVersion = appVersionFromMetadata(order);
|
||||
payload.put("appVersion", appVersion);
|
||||
String productId = defaultIfBlank(order.getTrackId(), "");
|
||||
String googleProductId = Objects.equals(GOOGLE, payPlatform) ? productId : "";
|
||||
payload.put("productId", productId);
|
||||
payload.put("googleProductId", googleProductId);
|
||||
|
||||
publish(new RechargeSuccessEvent()
|
||||
.setEventId("RECHARGE_SUCCESS:" + paymentMethod + ":" + payPlatform + ":" + order.getId())
|
||||
.setSysOrigin(order.getSysOrigin())
|
||||
.setUserId(order.getAcceptUserId())
|
||||
.setAmountCents(amountCents)
|
||||
.setAmountUsd(formatAmount(amountUsd))
|
||||
.setCurrency(defaultIfBlank(order.getCurrency(), "USD"))
|
||||
.setPayPlatform(payPlatform)
|
||||
.setPaymentMethod(paymentMethod)
|
||||
.setProductId(productId)
|
||||
.setGoogleProductId(googleProductId)
|
||||
.setAppVersion(appVersion)
|
||||
.setSourceOrderId(order.getId())
|
||||
.setOrderId(defaultIfBlank(order.getOrderId(), order.getId()))
|
||||
.setOccurredAt(firstTime(order.getPurchaseDateMs(), order.getUpdateTime(), order.getCreateTime()))
|
||||
.setPayload(payload));
|
||||
}
|
||||
|
||||
private void publish(RechargeSuccessEvent event) {
|
||||
if (!mqEnabled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
MessageSenderService senderService = messageSenderServiceProvider.getIfAvailable();
|
||||
if (Objects.isNull(senderService)) {
|
||||
log.warn("Recharge success mq sender missing, skip event report. eventId={}", event.getEventId());
|
||||
return;
|
||||
}
|
||||
senderService.sendEventMessage(MessageEvent.builder()
|
||||
.consumeId(event.getEventId())
|
||||
.topicString(defaultIfBlank(mqTopic, "RC_DEFAULT_APP_ORDINARY"))
|
||||
.tag(defaultIfBlank(mqTag, RechargeSuccessSink.TAG))
|
||||
.body(event)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.warn("Recharge success mq report error. eventId={}", event.getEventId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizePayPlatform(String value) {
|
||||
String normalized = Objects.toString(value, "").trim().toUpperCase();
|
||||
return normalized.isEmpty() ? UNKNOWN : normalized;
|
||||
}
|
||||
|
||||
private String classifyPaymentMethod(String payPlatform) {
|
||||
if (Objects.equals(GOOGLE, payPlatform)) {
|
||||
return GOOGLE;
|
||||
}
|
||||
if (Objects.equals(APPLE, payPlatform)) {
|
||||
return APPLE;
|
||||
}
|
||||
if (Objects.equals(UNKNOWN, payPlatform)) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
return THIRD_PARTY;
|
||||
}
|
||||
|
||||
private String appVersionFromMetadata(InAppPurchaseDetails order) {
|
||||
if (Objects.isNull(order) || Objects.isNull(order.getMetadata())) {
|
||||
return "";
|
||||
}
|
||||
return defaultIfBlank(order.getMetadata().get("appVersion"), "");
|
||||
}
|
||||
|
||||
private Long toCents(BigDecimal amount) {
|
||||
if (Objects.isNull(amount) || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return null;
|
||||
}
|
||||
return amount.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue();
|
||||
}
|
||||
|
||||
private BigDecimal firstPositive(BigDecimal first, BigDecimal second) {
|
||||
if (Objects.nonNull(first) && first.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return first;
|
||||
}
|
||||
return second;
|
||||
}
|
||||
|
||||
private boolean positive(Long value) {
|
||||
return Objects.nonNull(value) && value > 0;
|
||||
}
|
||||
|
||||
private String toInstantString(Timestamp timestamp) {
|
||||
return Objects.nonNull(timestamp) ? timestamp.toInstant().toString() : Instant.now().toString();
|
||||
}
|
||||
|
||||
private String firstTime(Timestamp first, Timestamp second, Timestamp third) {
|
||||
if (Objects.nonNull(first)) {
|
||||
return first.toInstant().toString();
|
||||
}
|
||||
if (Objects.nonNull(second)) {
|
||||
return second.toInstant().toString();
|
||||
}
|
||||
if (Objects.nonNull(third)) {
|
||||
return third.toInstant().toString();
|
||||
}
|
||||
return Instant.now().toString();
|
||||
}
|
||||
|
||||
private String formatAmount(BigDecimal amount) {
|
||||
return Objects.isNull(amount) ? null : amount.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
private String defaultIfBlank(String value, String fallback) {
|
||||
return isBlank(value) ? fallback : value.trim();
|
||||
}
|
||||
|
||||
private boolean isBlank(String value) {
|
||||
return Objects.toString(value, "").trim().isEmpty();
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,6 @@ import com.red.circle.framework.dto.ResultResponse;
|
||||
import com.red.circle.mq.business.model.event.pay.BuySuccessEvent;
|
||||
import com.red.circle.mq.business.model.event.pay.PurchaseProductEvent;
|
||||
import com.red.circle.mq.rocket.business.streams.BuySuccessSink;
|
||||
import com.red.circle.order.app.common.task.RechargeSuccessEventPublisher;
|
||||
import com.red.circle.order.app.common.task.TaskCenterGoClient;
|
||||
import com.red.circle.other.inner.endpoint.activity.CumulativeRechargeClient;
|
||||
import com.red.circle.other.inner.endpoint.activity.PropsActivityClient;
|
||||
@ -72,7 +71,6 @@ public class BuySuccessListener implements MessageListener {
|
||||
private final CumulativeRechargeClient cumulativeRechargeClient;
|
||||
private final RedisService redisService;
|
||||
private final TaskCenterGoClient taskCenterGoClient;
|
||||
private final RechargeSuccessEventPublisher rechargeSuccessEventPublisher;
|
||||
|
||||
@Override
|
||||
public Action consume(ConsumerMessage message) {
|
||||
@ -135,7 +133,6 @@ public class BuySuccessListener implements MessageListener {
|
||||
eventBody.getPlatform(),
|
||||
eventBody.getPayPlatform(),
|
||||
Objects.nonNull(eventBody.getBuyDateTime()) ? eventBody.getBuyDateTime().toInstant() : null);
|
||||
rechargeSuccessEventPublisher.reportAppPurchaseRecharge(eventBody);
|
||||
|
||||
userExpandClient.updatePurchasingToTrue(eventBody.getUserId());
|
||||
|
||||
|
||||
@ -49,16 +49,6 @@ public class WebSiteUseProfileCO extends ClientObject {
|
||||
*/
|
||||
private String sysOrigin;
|
||||
|
||||
/**
|
||||
* 是否存在货运金币账户;Yumi H5 用这个身份决定展示普通金币还是货运金币商品.
|
||||
*/
|
||||
private Boolean freightUser;
|
||||
|
||||
/**
|
||||
* 本次 H5 充值应该请求的商品类型,普通用户是 GOLD,货运/币商用户是 FREIGHT_GOLD.
|
||||
*/
|
||||
private String commodityType;
|
||||
|
||||
public static WebSiteUseProfileCO toWebSiteUseProfileCO(UserProfileDTO userProfile) {
|
||||
if (userProfile == null) {
|
||||
return null;
|
||||
|
||||
@ -23,9 +23,4 @@ public class PayOrderStatusCmd extends AppExtCommand {
|
||||
*/
|
||||
@NotBlank(message = "orderId required.")
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* H5公开支付页没有登录态,轮询状态时必须用下单用户ID继续做订单归属校验.
|
||||
*/
|
||||
private Long userId;
|
||||
}
|
||||
|
||||
@ -64,10 +64,6 @@ public class PayPlaceAnOrderCmd implements Serializable {
|
||||
*/
|
||||
private Boolean newVersion;
|
||||
|
||||
/**
|
||||
* 本次充值请求的App版本.
|
||||
*/
|
||||
private String appVersion;
|
||||
/**
|
||||
* 请求ip信息.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user