Compare commits
10 Commits
541493692c
...
e46681d4b7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e46681d4b7 | ||
|
|
0901496c7f | ||
|
|
9b4972f8e5 | ||
|
|
2279f5d640 | ||
|
|
bb83f2026b | ||
|
|
4940e612b5 | ||
|
|
d200011c48 | ||
|
|
2f5615ad61 | ||
|
|
c3d5784b91 | ||
|
|
5580dcabb4 |
301
.deploy/aslan-deploy/deploy-likei-services.sh
Executable file
301
.deploy/aslan-deploy/deploy-likei-services.sh
Executable file
@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_DIR="${BASE_DIR:-/opt/aslan-deploy}"
|
||||
SRC_DIR="${SRC_DIR:-$BASE_DIR/source/likei-services}"
|
||||
KUBECONFIG="${KUBECONFIG:-$BASE_DIR/kubeconfig}"
|
||||
NAMESPACE="${NAMESPACE:-prod}"
|
||||
MODE="${MODE:-preload}"
|
||||
SKIP_BUILD="${SKIP_BUILD:-0}"
|
||||
USE_GIT_SOURCE="${USE_GIT_SOURCE:-0}"
|
||||
GIT_REMOTE_URL="${GIT_REMOTE_URL:-git@gitea.haiyihy.com:hy/aslan-server.git}"
|
||||
GIT_REF="${GIT_REF:-atyou_prod}"
|
||||
MAVEN_EXTRA_ARGS="${MAVEN_EXTRA_ARGS:-}"
|
||||
BUILD_TS="${BUILD_TS:-$(date +%Y%m%dv%H%M%S)}"
|
||||
BASE_IMAGE="tuokemi-con-registry.ap-southeast-1.cr.aliyuncs.com/public_mirror_images/eclipse-temurin:17-jdk-jammy"
|
||||
FALLBACK_BASE_IMAGE="${FALLBACK_BASE_IMAGE:-eclipse-temurin:17-jdk-jammy}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
deploy-likei-services.sh [--mode preload|push|build-only|status] [--skip-build] [other|external|console ...]
|
||||
|
||||
Examples:
|
||||
/opt/aslan-deploy/deploy-likei-services.sh status
|
||||
/opt/aslan-deploy/deploy-likei-services.sh --mode preload other external console
|
||||
USE_GIT_SOURCE=1 GIT_REF=atyou_prod /opt/aslan-deploy/deploy-likei-services.sh --mode preload other
|
||||
MODE=push ALIYUN_USER=xxx ALIYUN_PASS=xxx /opt/aslan-deploy/deploy-likei-services.sh other
|
||||
|
||||
Notes:
|
||||
- preload mode builds the image on this server, imports it into each TKE node's containerd, then updates the deployment.
|
||||
- push mode follows the original Jenkins flow and requires registry credentials in ALIYUN_USER/ALIYUN_PASS.
|
||||
- USE_GIT_SOURCE=1 clones or resets source from GIT_REMOTE_URL/GIT_REF before building.
|
||||
EOF
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%F %T')" "$*"
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || {
|
||||
echo "missing command: $1" >&2
|
||||
exit 2
|
||||
}
|
||||
}
|
||||
|
||||
ensure_base_image() {
|
||||
if docker image inspect "$BASE_IMAGE" >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
log "base image not cached, pulling $FALLBACK_BASE_IMAGE"
|
||||
docker pull "$FALLBACK_BASE_IMAGE"
|
||||
docker tag "$FALLBACK_BASE_IMAGE" "$BASE_IMAGE"
|
||||
}
|
||||
|
||||
update_source_from_git() {
|
||||
if [[ "$USE_GIT_SOURCE" != "1" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
need_cmd git
|
||||
mkdir -p "$(dirname "$SRC_DIR")"
|
||||
if [[ ! -d "$SRC_DIR/.git" ]]; then
|
||||
rm -rf "$SRC_DIR"
|
||||
log "git clone $GIT_REMOTE_URL ($GIT_REF) -> $SRC_DIR"
|
||||
git clone --branch "$GIT_REF" "$GIT_REMOTE_URL" "$SRC_DIR"
|
||||
return
|
||||
fi
|
||||
|
||||
log "git reset source to origin/$GIT_REF from $GIT_REMOTE_URL"
|
||||
git -C "$SRC_DIR" remote set-url origin "$GIT_REMOTE_URL"
|
||||
git -C "$SRC_DIR" fetch origin "$GIT_REF"
|
||||
git -C "$SRC_DIR" checkout "$GIT_REF"
|
||||
git -C "$SRC_DIR" reset --hard "origin/$GIT_REF"
|
||||
}
|
||||
|
||||
service_module() {
|
||||
case "$1" in
|
||||
other) echo "rc-service/rc-service-other/other-start" ;;
|
||||
external) echo "rc-service/rc-service-external/external-start" ;;
|
||||
console) echo "rc-service/rc-service-console/console-start" ;;
|
||||
*) echo "unsupported service: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
service_dir() {
|
||||
case "$1" in
|
||||
other) echo "rc-service/rc-service-other" ;;
|
||||
external) echo "rc-service/rc-service-external" ;;
|
||||
console) echo "rc-service/rc-service-console" ;;
|
||||
*) echo "unsupported service: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
image_repo() {
|
||||
case "$1" in
|
||||
other) echo "tuokemi-con-registry.ap-southeast-1.cr.aliyuncs.com/atyou-prod/other" ;;
|
||||
external) echo "tuokemi-con-registry.ap-southeast-1.cr.aliyuncs.com/atyou-prod/external" ;;
|
||||
console) echo "tuokemi-con-registry.ap-southeast-1.cr.aliyuncs.com/atyou-prod/console" ;;
|
||||
*) echo "unsupported service: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
build_service() {
|
||||
local svc="$1"
|
||||
local module
|
||||
local dir
|
||||
local repo
|
||||
local tag
|
||||
local image
|
||||
module="$(service_module "$svc")"
|
||||
dir="$(service_dir "$svc")"
|
||||
repo="$(image_repo "$svc")"
|
||||
tag="${svc}-${BUILD_TS}"
|
||||
image="${repo}:${tag}"
|
||||
|
||||
cd "$SRC_DIR"
|
||||
if [[ "$SKIP_BUILD" != "1" ]]; then
|
||||
log "maven package $svc ($module)"
|
||||
# shellcheck disable=SC2086
|
||||
mvn clean package -pl "$module" -am -P prod -Dmaven.test.skip=true $MAVEN_EXTRA_ARGS
|
||||
fi
|
||||
|
||||
ensure_base_image
|
||||
log "docker build $image"
|
||||
docker build \
|
||||
--build-arg "SERVICE_VERSION=atyou-prod:${tag}" \
|
||||
-f "$dir/Dockerfile" \
|
||||
-t "$image" \
|
||||
"$dir"
|
||||
docker tag "$image" "${repo}:latest"
|
||||
|
||||
case "$MODE" in
|
||||
build-only)
|
||||
log "build-only completed for $svc: $image"
|
||||
;;
|
||||
push)
|
||||
push_image "$svc" "$image" "$repo"
|
||||
rollout "$svc" "$image"
|
||||
;;
|
||||
preload)
|
||||
preload_image_to_nodes "$svc" "$image"
|
||||
rollout "$svc" "$image"
|
||||
;;
|
||||
*)
|
||||
echo "unsupported MODE: $MODE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
push_image() {
|
||||
local svc="$1"
|
||||
local image="$2"
|
||||
local repo="$3"
|
||||
if [[ -n "${ALIYUN_USER:-}" && -n "${ALIYUN_PASS:-}" ]]; then
|
||||
log "docker login registry for $svc"
|
||||
printf '%s' "$ALIYUN_PASS" | docker login --username "$ALIYUN_USER" --password-stdin tuokemi-con-registry.ap-southeast-1.cr.aliyuncs.com
|
||||
fi
|
||||
log "docker push $image"
|
||||
docker push "$image"
|
||||
docker push "${repo}:latest"
|
||||
}
|
||||
|
||||
preload_image_to_nodes() {
|
||||
local svc="$1"
|
||||
local image="$2"
|
||||
local safe_tag="${image//[^A-Za-z0-9_.-]/-}"
|
||||
local tar_path="$BASE_DIR/artifacts/${safe_tag}.tar"
|
||||
mkdir -p "$BASE_DIR/artifacts"
|
||||
log "docker save $image -> $tar_path"
|
||||
docker save "$image" -o "$tar_path"
|
||||
|
||||
mapfile -t nodes < <(kubectl --kubeconfig "$KUBECONFIG" get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
|
||||
if [[ "${#nodes[@]}" -eq 0 ]]; then
|
||||
echo "no kubernetes nodes found" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
for node in "${nodes[@]}"; do
|
||||
preload_image_to_node "$svc" "$image" "$tar_path" "$node"
|
||||
done
|
||||
}
|
||||
|
||||
preload_image_to_node() {
|
||||
local svc="$1"
|
||||
local image="$2"
|
||||
local tar_path="$3"
|
||||
local node="$4"
|
||||
local pod="image-preload-${svc}-${node//[^a-zA-Z0-9-]/-}-$(date +%s)"
|
||||
local manifest
|
||||
manifest="$(mktemp)"
|
||||
cat >"$manifest" <<EOF
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: ${pod}
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
nodeName: "${node}"
|
||||
restartPolicy: Never
|
||||
hostPID: true
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
containers:
|
||||
- name: preload
|
||||
image: docker:26.1.3-dind
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: containerd
|
||||
mountPath: /run/containerd/containerd.sock
|
||||
volumes:
|
||||
- name: containerd
|
||||
hostPath:
|
||||
path: /run/containerd/containerd.sock
|
||||
type: Socket
|
||||
EOF
|
||||
log "create preload pod $pod on node $node"
|
||||
kubectl --kubeconfig "$KUBECONFIG" apply -f "$manifest" >/dev/null
|
||||
rm -f "$manifest"
|
||||
if ! kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" wait --for=condition=Ready "pod/$pod" --timeout=180s >/dev/null; then
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" describe "pod/$pod" || true
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" delete pod "$pod" --ignore-not-found=true >/dev/null
|
||||
return 1
|
||||
fi
|
||||
log "copy image tar to $pod"
|
||||
if ! kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" cp "$tar_path" "$pod:/tmp/image.tar" -c preload >/dev/null; then
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" delete pod "$pod" --ignore-not-found=true >/dev/null
|
||||
return 1
|
||||
fi
|
||||
log "import $image into node $node"
|
||||
if ! kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" exec "$pod" -c preload -- \
|
||||
ctr --address /run/containerd/containerd.sock -n k8s.io images import /tmp/image.tar >/dev/null; then
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" delete pod "$pod" --ignore-not-found=true >/dev/null
|
||||
return 1
|
||||
fi
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" delete pod "$pod" --ignore-not-found=true >/dev/null
|
||||
}
|
||||
|
||||
rollout() {
|
||||
local svc="$1"
|
||||
local image="$2"
|
||||
log "set image deployment/$svc $svc=$image"
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" set image "deployment/$svc" "$svc=$image"
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" rollout status "deployment/$svc" --timeout=600s
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" get deploy "$svc" -o wide
|
||||
}
|
||||
|
||||
status() {
|
||||
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" get deploy other external console -o wide
|
||||
kubectl --kubeconfig "$KUBECONFIG" get nodes -o wide
|
||||
}
|
||||
|
||||
main() {
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--skip-build)
|
||||
SKIP_BUILD=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
need_cmd java
|
||||
need_cmd mvn
|
||||
need_cmd docker
|
||||
need_cmd kubectl
|
||||
[[ -f "$KUBECONFIG" ]] || { echo "missing kubeconfig: $KUBECONFIG" >&2; exit 2; }
|
||||
|
||||
if [[ "${1:-}" == "status" || "$MODE" == "status" ]]; then
|
||||
status
|
||||
exit 0
|
||||
fi
|
||||
|
||||
update_source_from_git
|
||||
|
||||
local services=("$@")
|
||||
if [[ "${#services[@]}" -eq 0 ]]; then
|
||||
services=(other external console)
|
||||
fi
|
||||
|
||||
for svc in "${services[@]}"; do
|
||||
build_service "$svc"
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
76
.deploy/aslan-deploy/sync-and-deploy.sh
Executable file
76
.deploy/aslan-deploy/sync-and-deploy.sh
Executable file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DEPLOY_HOST="${DEPLOY_HOST:-43.160.245.220}"
|
||||
DEPLOY_USER="${DEPLOY_USER:-ubuntu}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/aslan-deploy-sg-ed25519}"
|
||||
REMOTE_BASE="${REMOTE_BASE:-/opt/aslan-deploy}"
|
||||
REMOTE_SRC="$REMOTE_BASE/source/likei-services"
|
||||
LOCAL_M2_ROOT="${LOCAL_M2_ROOT:-$HOME/.m2/repository}"
|
||||
REMOTE_M2_ROOT="${REMOTE_M2_ROOT:-/home/$DEPLOY_USER/.m2/repository}"
|
||||
M2_CACHE_GROUPS="${M2_CACHE_GROUPS:-com/red/circle com/github/sud}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
.deploy/aslan-deploy/sync-and-deploy.sh [remote deploy args...]
|
||||
|
||||
Examples:
|
||||
.deploy/aslan-deploy/sync-and-deploy.sh status
|
||||
.deploy/aslan-deploy/sync-and-deploy.sh --mode preload other
|
||||
.deploy/aslan-deploy/sync-and-deploy.sh --mode preload other external console
|
||||
|
||||
Environment:
|
||||
DEPLOY_HOST default 43.160.245.220
|
||||
DEPLOY_USER default ubuntu
|
||||
SSH_KEY default ~/.ssh/aslan-deploy-sg-ed25519
|
||||
|
||||
Git source on deploy host:
|
||||
ssh -i ~/.ssh/aslan-deploy-sg-ed25519 ubuntu@43.160.245.220 \
|
||||
'USE_GIT_SOURCE=1 GIT_REF=atyou_prod /opt/aslan-deploy/deploy-likei-services.sh --mode preload other'
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ssh_base=(ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST")
|
||||
rsync_ssh="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new"
|
||||
|
||||
"${ssh_base[@]}" "mkdir -p '$REMOTE_SRC' '$REMOTE_BASE'"
|
||||
|
||||
rsync -az --delete \
|
||||
--exclude '.git/' \
|
||||
--exclude 'target/' \
|
||||
--exclude '**/target/' \
|
||||
--exclude '.idea/' \
|
||||
--exclude '.DS_Store' \
|
||||
--exclude 'build.md' \
|
||||
--exclude 'node_modules/' \
|
||||
-e "$rsync_ssh" \
|
||||
"$REPO_ROOT/" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_SRC/"
|
||||
|
||||
rsync -az \
|
||||
-e "$rsync_ssh" \
|
||||
"$SCRIPT_DIR/deploy-likei-services.sh" "$DEPLOY_USER@$DEPLOY_HOST:$REMOTE_BASE/deploy-likei-services.sh"
|
||||
|
||||
for cache_group in $M2_CACHE_GROUPS; do
|
||||
local_cache_path="$LOCAL_M2_ROOT/$cache_group"
|
||||
remote_cache_parent="$REMOTE_M2_ROOT/$(dirname "$cache_group")"
|
||||
if [[ ! -d "$local_cache_path" ]]; then
|
||||
continue
|
||||
fi
|
||||
"${ssh_base[@]}" "mkdir -p '$remote_cache_parent'"
|
||||
rsync -az --delete \
|
||||
--exclude '*.lastUpdated' \
|
||||
-e "$rsync_ssh" \
|
||||
"$local_cache_path" "$DEPLOY_USER@$DEPLOY_HOST:$remote_cache_parent/"
|
||||
done
|
||||
|
||||
printf -v remote_script_q '%q' "$REMOTE_BASE/deploy-likei-services.sh"
|
||||
printf -v remote_args_q '%q ' "$@"
|
||||
"${ssh_base[@]}" "chmod +x $remote_script_q && $remote_script_q $remote_args_q"
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -96,3 +96,5 @@ devops-monitor-id_rsa.key
|
||||
/.claude/
|
||||
|
||||
build.md
|
||||
|
||||
reports/
|
||||
@ -0,0 +1,9 @@
|
||||
package com.red.circle.mq.business.model.event.gift;
|
||||
|
||||
/**
|
||||
* 礼物发送场景.
|
||||
*/
|
||||
public enum GiftSendSceneEnum {
|
||||
ROOM,
|
||||
PRIVATE_CHAT
|
||||
}
|
||||
@ -83,6 +83,11 @@ public class GiveAwayGiftBatchEvent implements Serializable {
|
||||
*/
|
||||
private Long roomId;
|
||||
|
||||
/**
|
||||
* 发送场景.
|
||||
*/
|
||||
private GiftSendSceneEnum sendScene;
|
||||
|
||||
/**
|
||||
* 动态id
|
||||
*/
|
||||
@ -270,6 +275,14 @@ public class GiveAwayGiftBatchEvent implements Serializable {
|
||||
return Objects.nonNull(giftConfig) && Objects.equals(giftConfig.getGiftTab(), "MAGIC");
|
||||
}
|
||||
|
||||
public GiftSendSceneEnum sendSceneOrRoom() {
|
||||
return Objects.isNull(sendScene) ? GiftSendSceneEnum.ROOM : sendScene;
|
||||
}
|
||||
|
||||
public boolean privateChatScene() {
|
||||
return Objects.equals(sendScene, GiftSendSceneEnum.PRIVATE_CHAT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是专属礼物.
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
package com.red.circle.external.inner.endpoint.message.api;
|
||||
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd;
|
||||
import com.red.circle.framework.dto.ResultResponse;
|
||||
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.RequestParam;
|
||||
|
||||
/**
|
||||
@ -29,4 +33,10 @@ public interface ImMessageClientApi {
|
||||
@RequestParam("toAccount") Long toAccount,
|
||||
@RequestParam("text") String text);
|
||||
|
||||
/**
|
||||
* 发送 C2C 自定义消息.
|
||||
*/
|
||||
@PostMapping("/sendCustomMessage")
|
||||
ResultResponse<Void> sendCustomMessage(@RequestBody @Validated CustomC2CMsgBodyCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
package com.red.circle.external.inner.model.cmd.message;
|
||||
|
||||
import com.red.circle.framework.dto.Command;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.io.Serial;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* C2C 自定义消息.
|
||||
*/
|
||||
@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;
|
||||
|
||||
private Object data;
|
||||
|
||||
public static CustomC2CMsgBodyBuilder builder() {
|
||||
return new CustomC2CMsgBodyBuilder();
|
||||
}
|
||||
|
||||
public static class CustomC2CMsgBodyBuilder {
|
||||
|
||||
private String fromAccount;
|
||||
private String toAccount;
|
||||
private String desc;
|
||||
private Object data;
|
||||
|
||||
public CustomC2CMsgBodyBuilder fromAccount(String fromAccount) {
|
||||
this.fromAccount = fromAccount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2CMsgBodyBuilder toAccount(String toAccount) {
|
||||
this.toAccount = toAccount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2CMsgBodyBuilder desc(String desc) {
|
||||
this.desc = desc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2CMsgBodyBuilder data(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomC2CMsgBodyCmd build() {
|
||||
CustomC2CMsgBodyCmd cmd = new CustomC2CMsgBodyCmd();
|
||||
cmd.setFromAccount(fromAccount);
|
||||
cmd.setToAccount(toAccount);
|
||||
cmd.setDesc(desc);
|
||||
cmd.setData(data);
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
package com.red.circle.console.adapter.app.user;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.console.app.dto.clienobject.count.aslan.AslanRegionCountryStatisticsCO;
|
||||
import com.red.circle.console.app.service.app.count.AslanRegionCountryStatisticsService;
|
||||
import com.red.circle.console.app.service.app.user.DatavService;
|
||||
|
||||
@ -163,15 +163,21 @@ public class UserFreightBalanceServiceImpl implements
|
||||
|
||||
@Override
|
||||
public void deduction(UserFreightBalanceCmd param) {
|
||||
// 余额不足
|
||||
// 扣款金币允许为0,表示本次只登记USDT扣款;负数会导致余额反向增加,必须拦截。
|
||||
ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
|
||||
ArithmeticUtils.gte(param.getEarnPoints(), BigDecimal.ZERO));
|
||||
|
||||
// 操作失败
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
|
||||
decrCandyBalance(param.getSysOrigin(), param.getUserId(), param.getEarnPoints()));
|
||||
// USDT扣款复用进货的amount字段,必须大于0,否则会产生没有金额意义的扣款流水。
|
||||
ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
|
||||
Objects.nonNull(param.getAmount()) && param.getAmount().compareTo(BigDecimal.ZERO) > 0);
|
||||
|
||||
// 插入流水
|
||||
// 只有金币数量大于0时才真正扣减货运金币余额;金币为0时保留原余额,只记录USDT扣款流水。
|
||||
if (param.getEarnPoints().compareTo(BigDecimal.ZERO) > 0) {
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
|
||||
decrCandyBalance(param.getSysOrigin(), param.getUserId(), param.getEarnPoints()));
|
||||
}
|
||||
|
||||
// 扣款流水同时记录金币数量和USDT金额,amount用于后台列表、导出和后续对账。
|
||||
BigDecimal balance = getAvailableBalance(param.getUserId());
|
||||
ResponseAssert.requiredSuccess(
|
||||
freightGoldClient.addRunningWater(new AddFreightBalanceRunningWaterCmd()
|
||||
@ -182,13 +188,16 @@ public class UserFreightBalanceServiceImpl implements
|
||||
.setQuantity(param.getEarnPoints())
|
||||
.setBalance(balance)
|
||||
.setUsdQuantity(BigDecimal.ZERO)
|
||||
.setAmount(param.getAmount())
|
||||
.setOrigin(FreightBalanceOrigin.DEDUCTION.name())
|
||||
.setRemark(param.getRemark())
|
||||
.setCreateUser(param.getOperationUser())
|
||||
.setUpdateUser(param.getOperationUser())));
|
||||
|
||||
//余额如果少于1250则收回徽章
|
||||
removeFreightShipBadge(param.getUserId(), balance);
|
||||
// 实际扣减金币后才需要重新判断金币余额阈值;USDT-only扣款不改变余额。
|
||||
if (param.getEarnPoints().compareTo(BigDecimal.ZERO) > 0) {
|
||||
removeFreightShipBadge(param.getUserId(), balance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -65,6 +65,11 @@ public class UserFreightBalanceRunningWaterCO implements Serializable {
|
||||
*/
|
||||
private BigDecimal balance;
|
||||
|
||||
/**
|
||||
* USDT金额.
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 来源.
|
||||
*/
|
||||
|
||||
@ -14,11 +14,15 @@ public enum BannerDisplayPositionEnum {
|
||||
|
||||
ROOM("房间内"),
|
||||
|
||||
ROOM_LIST("房间列表页"),
|
||||
|
||||
EXPLORE_PAGE("发现页"),
|
||||
|
||||
WALLET("钱包"),
|
||||
|
||||
HOME_ALERT("首页弹出层");
|
||||
HOME_ALERT("首页弹出层"),
|
||||
|
||||
GAME("游戏");
|
||||
|
||||
private final String desc;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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;
|
||||
@ -36,4 +37,10 @@ public class ImMessageClientEndpoint implements ImMessageClientApi {
|
||||
return ResultResponse.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultResponse<Void> sendCustomMessage(CustomC2CMsgBodyCmd cmd) {
|
||||
imMessageClientService.sendCustomMessage(cmd);
|
||||
return ResultResponse.success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.red.circle.external.inner.service.message;
|
||||
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd;
|
||||
|
||||
/**
|
||||
* im 消息服务.
|
||||
*
|
||||
@ -12,4 +14,9 @@ public interface ImMessageClientService {
|
||||
*/
|
||||
void sendMessageText(String fromAccount, String toAccount, String text);
|
||||
|
||||
/**
|
||||
* 发送 C2C 自定义消息.
|
||||
*/
|
||||
void sendCustomMessage(CustomC2CMsgBodyCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
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 java.util.concurrent.ThreadLocalRandom;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -23,4 +28,20 @@ public class ImMessageClientServiceImpl implements ImMessageClientService {
|
||||
imMessageManagerService.sendMessageText(fromAccount, toAccount, text).subscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomMessage(CustomC2CMsgBodyCmd cmd) {
|
||||
ImMessageParam param = ImMessageParam.builder()
|
||||
.fromAccount(cmd.getFromAccount())
|
||||
.toAccount(cmd.getToAccount())
|
||||
.msgRandom(ThreadLocalRandom.current().nextInt(100_000_000, 1_000_000_000))
|
||||
.offlinePushInfo(OfflinePushInfo.builder().pushFlag(0).build())
|
||||
.build()
|
||||
.addMsgBody(MessageBody.customBody(cmd.getDesc(), cmd.getData()));
|
||||
imMessageManagerService.sendMessage(param)
|
||||
.subscribe(resp -> log.info("send C2C custom message success: fromAccount={}, toAccount={}, desc={}, resp={}",
|
||||
cmd.getFromAccount(), cmd.getToAccount(), cmd.getDesc(), resp),
|
||||
throwable -> log.warn("send C2C custom message failed: fromAccount={}, toAccount={}, desc={}",
|
||||
cmd.getFromAccount(), cmd.getToAccount(), cmd.getDesc(), throwable));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,8 +30,8 @@ management:
|
||||
show-details: always
|
||||
|
||||
rtc:
|
||||
appId: f424387a480e41088239416e10030034
|
||||
certificate: c96d8494f80542bb83c843ad4045df03
|
||||
appId: 4b5e5cea3b86476caf7f7a57d05b82d1
|
||||
certificate: a6feb209de6448148a926f59634807bb
|
||||
|
||||
red-circle:
|
||||
oss:
|
||||
|
||||
@ -56,12 +56,12 @@ public class LiveMicrophoneServiceImpl implements LiveMicrophoneService {
|
||||
private final RedisService redisService;
|
||||
private final RoomUserBlacklistClient roomUserBlacklistClient;
|
||||
|
||||
private final String authKey = "Basic N2Q0ZTRiZDYyODUyNDc3ZGI4Yzk3OGU1NTVmMDRiOGI6ODdkMjcxMTg2NzgxNGY4YjgzZTNkOWIyZmRjOTc3OWQ=";
|
||||
private final String authKey = "Basic ZjFjNWIzMDZiMzA0NGU5Y2E5Yzk5YjYzNDNlZGJlZDM6OGQ2Njc0OGQ1NjQyNDVlZmI5MTc3NDFlYTllYWUyNWU=";
|
||||
|
||||
private final String killRoom = "https://api.sd-rtn.com/dev/v1/kicking-rule";
|
||||
private final String queryUserInfo = "https://api.sd-rtn.com/dev/v1/channel/user/";
|
||||
|
||||
private final String appId = "535633ae846441b09547b68f8353aad3";
|
||||
private final String appId = "4b5e5cea3b86476caf7f7a57d05b82d1";
|
||||
|
||||
|
||||
private final UserProfileClient userProfileClient;
|
||||
|
||||
@ -97,12 +97,12 @@ public class LiveMicrophoneGatewayImpl implements LiveMicrophoneGateway {
|
||||
private final LiveRoomCacheService liveRoomCacheService;
|
||||
// private final RedissonClient redissonClient;
|
||||
//临时处理,后续房子啊Nacos配置处理
|
||||
private final String authKey = "Basic MmIwZGFkOWQ0ZjEzNGQ2NmI5MmI2ZmY2MzljNmEwMTc6ZWRlMTQ1NTRjNTU0NDY2ZmE4OWNiYWFhNGNmZTM3YjA=";
|
||||
private final String authKey = "Basic ZjFjNWIzMDZiMzA0NGU5Y2E5Yzk5YjYzNDNlZGJlZDM6OGQ2Njc0OGQ1NjQyNDVlZmI5MTc3NDFlYTllYWUyNWU=";
|
||||
|
||||
private final String killRoom = "https://api.sd-rtn.com/dev/v1/kicking-rule";
|
||||
private final String queryUserInfo = "https://api.sd-rtn.com/dev/v1/channel/user/";
|
||||
|
||||
private final String appId = "660b61b534d04d44815ee855913a6efe";
|
||||
private final String appId = "4b5e5cea3b86476caf7f7a57d05b82d1";
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@ -12,6 +12,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import com.red.circle.other.app.service.gift.GiftService;
|
||||
import com.red.circle.tool.core.json.JacksonUtils;
|
||||
import java.math.BigDecimal;
|
||||
@ -131,6 +132,20 @@ public class GiftRestController extends BaseController {
|
||||
return giftService.giveAwayGiftBatch(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私聊赠送-礼物.
|
||||
*
|
||||
* @eo.name 私聊赠送-礼物.
|
||||
* @eo.url /private
|
||||
* @eo.method post
|
||||
* @eo.request-type json
|
||||
*/
|
||||
@PostMapping("/private")
|
||||
public BigDecimal giveAwayPrivateGift(@RequestBody @Validated GiveAwayPrivateGiftCmd cmd) {
|
||||
log.warn("giveAwayPrivateGift:{}", JacksonUtils.toJson(cmd));
|
||||
return giftService.giveAwayPrivateGift(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 赠送-幸运礼物.
|
||||
*
|
||||
@ -181,4 +196,3 @@ public class GiftRestController extends BaseController {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import com.red.circle.common.business.core.enums.*;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts;
|
||||
import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveGiftConfig;
|
||||
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
|
||||
import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
|
||||
@ -128,6 +129,7 @@ public class BlessingsGiftGiveCmdExe {
|
||||
giveAwayGiftBatchEvent.setQuantity(cmd.getQuantity());
|
||||
giveAwayGiftBatchEvent.setGiftConfig(toGiveGiftConfig(giftConfig));
|
||||
giveAwayGiftBatchEvent.setRoomId(cmd.getRoomId());
|
||||
giveAwayGiftBatchEvent.setSendScene(GiftSendSceneEnum.ROOM);
|
||||
giveAwayGiftBatchEvent.setDynamicContentId(cmd.getDynamicContentId());
|
||||
giveAwayGiftBatchEvent.setTrackId(trackId);
|
||||
giveAwayGiftBatchEvent.setSongId(cmd.getSongId());
|
||||
|
||||
@ -10,11 +10,14 @@ import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
|
||||
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
|
||||
import com.red.circle.other.app.convertor.material.GiftAppConvertor;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import com.red.circle.other.app.service.task.TaskService;
|
||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.domain.model.user.UserProfile;
|
||||
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
|
||||
import com.red.circle.other.inner.asserts.GiftErrorCode;
|
||||
import com.red.circle.other.inner.asserts.user.UserErrorCode;
|
||||
import com.red.circle.other.inner.enums.material.GiftCurrencyType;
|
||||
import com.red.circle.other.inner.enums.material.GiftTabEnum;
|
||||
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
|
||||
@ -100,6 +103,49 @@ public class GiftGiveAwayBatchCmdExe {
|
||||
return receipt.getBalance();
|
||||
}
|
||||
|
||||
public BigDecimal executePrivate(GiveAwayPrivateGiftCmd cmd) {
|
||||
GiftConfigDTO giftConfig = giftCacheService.getById(cmd.getGiftId());
|
||||
log.warn("GiftGiveAwayPrivateCmdExe{}", giftConfig);
|
||||
|
||||
ResponseAssert.notNull(GiftErrorCode.GIFT_NOT_FOUND, giftConfig);
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
Objects.equals(cmd.requireReqSysOrigin(), giftConfig.getSysOrigin()));
|
||||
ResponseAssert.isFalse(GiftErrorCode.NOT_SUPPORTED_GIFT_TYPE,
|
||||
Objects.equals(giftConfig.getGiftTab(), GiftTabEnum.LUCKY_GIFT.name()));
|
||||
checkPrivateAcceptUser(cmd);
|
||||
|
||||
if (isZeroGiftCandy(giftConfig)) {
|
||||
BigDecimal dollarAmount = walletGoldClient.getBalance(cmd.requiredReqUserId()).getBody().getDollarAmount();
|
||||
log.info("私聊赠送普通礼物金额为0,余额为 {}", dollarAmount);
|
||||
return dollarAmount;
|
||||
}
|
||||
|
||||
WalletReceiptDTO receipt = consumePrivateCandy(cmd, giftConfig);
|
||||
log.warn("GiftGiveAwayPrivateCmdExe{}", receipt);
|
||||
|
||||
giftMqMessage.sendGift(receipt.getConsumeId().toString(),
|
||||
giftAppConvertor.toGiveAwayPrivateGiftEvent(cmd, giftConfig, receipt.getConsumeId()));
|
||||
userProfileGateway.removeCache(cmd.requiredReqUserId());
|
||||
|
||||
Boolean taskStatus = taskService.checkTaskStatus(cmd.getReqUserId(), 2L,
|
||||
LocalDateTimeUtils.nowFormat("yyyy-MM-dd"));
|
||||
if (taskStatus) {
|
||||
taskMqMessage.sendTask(TaskApprovalEvent.builder()
|
||||
.taskId(2)
|
||||
.userId(cmd.getReqUserId())
|
||||
.day(LocalDateTimeUtils.nowFormat("yyyy-MM-dd"))
|
||||
.build());
|
||||
}
|
||||
return receipt.getBalance();
|
||||
}
|
||||
|
||||
private void checkPrivateAcceptUser(GiveAwayPrivateGiftCmd cmd) {
|
||||
UserProfile acceptUser = userProfileGateway.getByUserId(cmd.getAcceptUserId());
|
||||
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, acceptUser);
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
Objects.equals(cmd.requireReqSysOrigin(), acceptUser.getOriginSys()));
|
||||
}
|
||||
|
||||
private void checkDynamicRegion(GiveAwayGiftBatchCmd cmd) {
|
||||
if (cmd.getDynamicContentId() == null) {
|
||||
return;
|
||||
@ -163,6 +209,60 @@ public class GiftGiveAwayBatchCmdExe {
|
||||
throw new IllegalArgumentException("param error.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私聊付钱.
|
||||
*/
|
||||
private WalletReceiptDTO consumePrivateCandy(GiveAwayPrivateGiftCmd cmd, GiftConfigDTO giftConfig) {
|
||||
|
||||
if (isGiftTypeEqFree(giftConfig)) {
|
||||
return new WalletReceiptDTO()
|
||||
.setConsumeId(IdWorkerUtils.getId())
|
||||
.setProcessAmount(BigDecimal.ZERO)
|
||||
.setBalance(
|
||||
ResponseAssert.requiredSuccess(walletGoldClient.getBalance(cmd.requiredReqUserId()))
|
||||
.getDollarAmount());
|
||||
|
||||
}
|
||||
|
||||
if (isGiftTypeEqGold(giftConfig)) {
|
||||
BigDecimal amount = cmd.calculateConsumeGolds(giftConfig.getGiftCandy());
|
||||
WalletReceiptResDTO receiptRes = walletGoldClient.changeBalance(GoldReceiptCmd.builder()
|
||||
.appExpenditure()
|
||||
.userId(cmd.requiredReqUserId())
|
||||
.eventId(giftConfig.getId())
|
||||
.sysOrigin(cmd.requireReqSysOrigin())
|
||||
.origin(GoldOrigin.GIVE_GIFT + "_" + giftConfig.getGiftTab())
|
||||
.amount(amount)
|
||||
.closeDelayAsset()
|
||||
.build()).getBody();
|
||||
return new WalletReceiptDTO()
|
||||
.setConsumeId(receiptRes.getAssetRecordId())
|
||||
.setBalance(receiptRes.getBalance().getDollarAmount())
|
||||
.setProcessAmount(amount);
|
||||
}
|
||||
|
||||
if (isGiftTypeEqDiamond(giftConfig)) {
|
||||
return walletDiamondClient.changeBalance(new DiamondReceiptCmd()
|
||||
.setConsumeId(Objects.toString(giftConfig.getId()))
|
||||
.setType(ReceiptType.EXPENDITURE)
|
||||
.setTrackId(giftConfig.getId())
|
||||
.setUserId(cmd.requiredReqUserId())
|
||||
.setSysOrigin(cmd.requireReqSysOriginEnum())
|
||||
.setOrigin(DiamondOrigin.GIVE_GIFT)
|
||||
.setAmount(cmd.calculateConsumeGolds(giftConfig.getGiftCandy()))
|
||||
.setCreateTime(TimestampUtils.now())
|
||||
).getBody();
|
||||
}
|
||||
|
||||
ResponseAssert.failure(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE);
|
||||
throw new IllegalArgumentException("param error.");
|
||||
}
|
||||
|
||||
private boolean isZeroGiftCandy(GiftConfigDTO giftConfig) {
|
||||
return Objects.nonNull(giftConfig.getGiftCandy())
|
||||
&& BigDecimal.ZERO.compareTo(giftConfig.getGiftCandy()) == 0;
|
||||
}
|
||||
|
||||
private boolean isGiftTypeEqDiamond(GiftConfigDTO giftConfigInfo) {
|
||||
return Objects.equals(giftConfigInfo.getType(), GiftCurrencyType.DIAMOND.name());
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
|
||||
import com.red.circle.other.app.dto.clientobject.RoomRedPacketCO;
|
||||
import com.red.circle.other.app.dto.cmd.SendRoomRedPacketCmd;
|
||||
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
|
||||
import com.red.circle.other.app.enums.RoomRewardLevelEnum;
|
||||
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
|
||||
import com.red.circle.other.app.util.OfficialNoticeUtils;
|
||||
import com.red.circle.other.domain.gateway.RoomRedPacketGateway;
|
||||
@ -23,15 +22,12 @@ import com.red.circle.other.domain.redpacket.RoomRedPacketSourceType;
|
||||
import com.red.circle.other.domain.redpacket.RoomRedPacketStatus;
|
||||
import com.red.circle.other.domain.redpacket.RoomRedPacketType;
|
||||
import com.red.circle.other.infra.database.cache.service.other.RoomRedPacketCacheService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
|
||||
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
|
||||
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.other.inner.asserts.OtherErrorCode;
|
||||
import com.red.circle.other.inner.asserts.RoomErrorCode;
|
||||
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||
import com.red.circle.tool.core.tuple.PennyAmount;
|
||||
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
|
||||
@ -42,10 +38,11 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@ -159,26 +156,7 @@ public class SendRoomRedPacketCmdExe {
|
||||
RoomRedPacket redPacket = buildRedPacket(cmd, systemUserId, packetId, expireTime, RoomRedPacketSourceType.PLATFORM);
|
||||
roomRedPacketGateway.save(redPacket);
|
||||
|
||||
// 查询上周数据
|
||||
deductRoomContribution(cmd.getRoomId(), cmd.getTotalAmount());
|
||||
|
||||
// 预生成随机金额列表(拼手气红包)
|
||||
List<Long> amounts = generateRandomAmounts(cmd.getTotalAmount(), cmd.getTotalCount());
|
||||
long expireSeconds = cmd.getExpireMinutes() * 60;
|
||||
roomRedPacketCacheService.setRandomAmounts(packetId, amounts, expireSeconds);
|
||||
|
||||
// 写入Redis(红包主数据)
|
||||
cacheRedPacketData(redPacket);
|
||||
|
||||
// 添加到房间红包列表
|
||||
roomRedPacketCacheService.addToRoomList(
|
||||
cmd.getRoomId(),
|
||||
packetId,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
|
||||
// 发送飘窗通知(平台红包)
|
||||
sendMessage(cmd, packetId);
|
||||
publishPlatformRedPacket(cmd, redPacket, packetId);
|
||||
|
||||
log.info("发平台红包成功 packetId={} roomId={} amount={} count={} ",
|
||||
packetId, cmd.getRoomId(), cmd.getTotalAmount(), cmd.getTotalCount());
|
||||
@ -186,27 +164,101 @@ public class SendRoomRedPacketCmdExe {
|
||||
return RoomRedPacketAppConvertor.toCO(redPacket);
|
||||
}
|
||||
|
||||
private void deductRoomContribution(Long roomId, Long totalAmount) {
|
||||
String lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getLastWeekMonday().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
RoomContributionActivityCount lastWeek = findByRoomIdAndDateNumber(roomId, Integer.parseInt(lastWeekDateNumber));
|
||||
ResponseAssert.notNull(RoomErrorCode.ROOM_CONTRIBUTION_NOT_FOUND, lastWeek);
|
||||
/**
|
||||
* 执行房间周流水奖励平台红包.
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public RoomRedPacketCO executeRoomContributionReward(SendRoomRedPacketCmd cmd,
|
||||
String activityCountId) {
|
||||
cmd.setPacketType(2);
|
||||
validateParams(cmd);
|
||||
|
||||
Long amount = lastWeek.getContributionValue().longValue();
|
||||
RoomRewardLevelEnum level = RoomRewardLevelEnum.getLevelByAmount(amount);
|
||||
ResponseAssert.notNull(RoomErrorCode.ROOM_CONTRIBUTION_NOT_COMPLETED, level);
|
||||
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, Objects.equals(totalAmount, level.getRewardCoins()));
|
||||
String packetId = buildRoomContributionRewardPacketId(activityCountId);
|
||||
boolean locked = roomContributionActivityCountService
|
||||
.markRewardCoinsSending(activityCountId, packetId);
|
||||
if (!locked) {
|
||||
log.warn("房间周流水奖励已被处理 activityCountId={} roomId={}", activityCountId,
|
||||
cmd.getRoomId());
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean updated = roomContributionActivityCountService.updateRewardCoinsSent(lastWeek.getId());
|
||||
ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE, updated);
|
||||
try {
|
||||
RoomRedPacket redPacket = roomRedPacketGateway.findByPacketId(packetId);
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(cmd.getExpireMinutes());
|
||||
if (Objects.isNull(redPacket)) {
|
||||
Long systemUserId = 0L;
|
||||
redPacket = buildRedPacket(cmd, systemUserId, packetId, expireTime,
|
||||
RoomRedPacketSourceType.PLATFORM);
|
||||
roomRedPacketGateway.save(redPacket);
|
||||
} else if (isExpired(redPacket)) {
|
||||
roomRedPacketGateway.resetPlatformPacket(packetId, cmd.getTotalAmount(),
|
||||
cmd.getTotalCount(), cmd.getExpireMinutes(), expireTime);
|
||||
redPacket = buildRedPacket(cmd, 0L, packetId, expireTime,
|
||||
RoomRedPacketSourceType.PLATFORM);
|
||||
}
|
||||
|
||||
RoomRedPacket finalRedPacket = redPacket;
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
publishRoomContributionRewardAfterCommit(cmd, finalRedPacket, activityCountId,
|
||||
packetId);
|
||||
}
|
||||
});
|
||||
|
||||
log.info("房间周流水奖励红包已提交 packetId={} roomId={} activityCountId={} amount={} count={}",
|
||||
packetId, cmd.getRoomId(), activityCountId, cmd.getTotalAmount(),
|
||||
cmd.getTotalCount());
|
||||
return RoomRedPacketAppConvertor.toCO(redPacket);
|
||||
} catch (Exception e) {
|
||||
roomContributionActivityCountService
|
||||
.resetRewardCoinsSending(activityCountId, packetId, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据房间ID和日期查询数据
|
||||
*/
|
||||
private RoomContributionActivityCount findByRoomIdAndDateNumber(Long roomId, Integer dateNumber) {
|
||||
return roomContributionActivityCountService
|
||||
.findByRoomIdAndDateNumber(roomId, dateNumber)
|
||||
.orElse(null);
|
||||
private void publishRoomContributionRewardAfterCommit(SendRoomRedPacketCmd cmd,
|
||||
RoomRedPacket redPacket,
|
||||
String activityCountId,
|
||||
String packetId) {
|
||||
try {
|
||||
if (roomRedPacketCacheService.existsRedPacket(packetId)) {
|
||||
roomRedPacketCacheService.addToRoomList(
|
||||
cmd.getRoomId(),
|
||||
packetId,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
boolean sent = roomContributionActivityCountService
|
||||
.markRewardCoinsSent(activityCountId, packetId);
|
||||
if (!sent) {
|
||||
log.warn("房间周流水奖励红包已存在但状态标记失败 packetId={} activityCountId={}",
|
||||
packetId, activityCountId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
publishPlatformRedPacket(cmd, redPacket, packetId);
|
||||
boolean sent = roomContributionActivityCountService
|
||||
.markRewardCoinsSent(activityCountId, packetId);
|
||||
if (!sent) {
|
||||
log.warn("房间周流水奖励红包发布后状态标记失败 packetId={} activityCountId={}",
|
||||
packetId, activityCountId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
roomContributionActivityCountService
|
||||
.resetRewardCoinsSending(activityCountId, packetId, e.getMessage());
|
||||
log.error("房间周流水奖励红包发布失败 packetId={} activityCountId={} roomId={}",
|
||||
packetId, activityCountId, cmd.getRoomId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildRoomContributionRewardPacketId(String activityCountId) {
|
||||
return "RW" + activityCountId.replace("_", "");
|
||||
}
|
||||
|
||||
private boolean isExpired(RoomRedPacket redPacket) {
|
||||
return Objects.nonNull(redPacket.getExpireTime())
|
||||
&& !redPacket.getExpireTime().isAfter(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,9 +274,14 @@ public class SendRoomRedPacketCmdExe {
|
||||
build.put("packetId", packetId);
|
||||
|
||||
if (cmd.getTotalAmount() >= 30000) {
|
||||
SysOriginPlatformEnum sysOrigin = SysOriginPlatformEnum
|
||||
.toEnum(cmd.getReqSysOrigin().getOrigin());
|
||||
if (Objects.isNull(sysOrigin)) {
|
||||
return;
|
||||
}
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.ATYOU)
|
||||
.toPlatform(sysOrigin)
|
||||
.type(GroupMessageTypeEnum.ROOM_RED_PACKET)
|
||||
.data(build)
|
||||
.build()
|
||||
@ -271,6 +328,23 @@ public class SendRoomRedPacketCmdExe {
|
||||
}
|
||||
}
|
||||
|
||||
private void publishPlatformRedPacket(SendRoomRedPacketCmd cmd, RoomRedPacket redPacket,
|
||||
String packetId) {
|
||||
List<Long> amounts = generateRandomAmounts(cmd.getTotalAmount(), cmd.getTotalCount());
|
||||
long expireSeconds = cmd.getExpireMinutes() * 60;
|
||||
roomRedPacketCacheService.setRandomAmounts(packetId, amounts, expireSeconds);
|
||||
|
||||
cacheRedPacketData(redPacket);
|
||||
|
||||
roomRedPacketCacheService.addToRoomList(
|
||||
cmd.getRoomId(),
|
||||
packetId,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
|
||||
sendMessage(cmd, packetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣除用户金币
|
||||
*/
|
||||
|
||||
@ -23,6 +23,7 @@ import com.red.circle.mq.business.model.event.gift.GameLuckyGiftBusinessEvent;
|
||||
import com.red.circle.mq.business.model.event.gift.GameLuckyGiftUser;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts;
|
||||
import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum;
|
||||
import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
|
||||
import com.red.circle.mq.rocket.business.service.MessageSenderService;
|
||||
import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink;
|
||||
@ -318,6 +319,7 @@ public class GameLuckyGiftCommon {
|
||||
.setQuantity(cmd.getQuantity())
|
||||
.setGiftConfig(giftAppConvertor.toGiveGiftConfig(giftConfigDTO))
|
||||
.setRoomId(cmd.getRoomId())
|
||||
.setSendScene(GiftSendSceneEnum.ROOM)
|
||||
.setTrackId(Long.parseLong(timeId))
|
||||
.setBagGift(Boolean.FALSE)
|
||||
.setCreateTime(TimestampUtils.now());
|
||||
|
||||
@ -4,11 +4,13 @@ import com.google.common.collect.Lists;
|
||||
import com.red.circle.framework.core.convertor.ConvertorModel;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts;
|
||||
import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum;
|
||||
import com.red.circle.mq.business.model.event.gift.GiveGiftConfig;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.GiftBackpackLimitCO;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.GiftConfigCO;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import com.red.circle.other.infra.database.rds.entity.gift.GiftBackpackLimit;
|
||||
import com.red.circle.other.infra.database.rds.entity.gift.GiftConfig;
|
||||
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
|
||||
@ -49,6 +51,7 @@ public interface GiftAppConvertor {
|
||||
.setQuantity(cmd.getQuantity())
|
||||
.setGiftConfig(toGiveGiftConfig(giftConfig))
|
||||
.setRoomId(cmd.getRoomId())
|
||||
.setSendScene(GiftSendSceneEnum.ROOM)
|
||||
.setDynamicContentId(cmd.getDynamicContentId())
|
||||
.setTrackId(trackId)
|
||||
.setSongId(cmd.getSongId())
|
||||
@ -103,10 +106,32 @@ public interface GiftAppConvertor {
|
||||
.setQuantity(cmd.getQuantity())
|
||||
.setGiftConfig(toGiveGiftConfig(giftConfig))
|
||||
.setRoomId(cmd.getRoomId())
|
||||
.setSendScene(GiftSendSceneEnum.ROOM)
|
||||
.setTrackId(trackId)
|
||||
.setBagGift(Boolean.TRUE)
|
||||
.setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now()
|
||||
: new Timestamp(cmd.getReqTime().getTime()));
|
||||
}
|
||||
|
||||
default GiveAwayGiftBatchEvent toGiveAwayPrivateGiftEvent(GiveAwayPrivateGiftCmd cmd,
|
||||
GiftConfigDTO giftConfig, Long trackId) {
|
||||
return new GiveAwayGiftBatchEvent()
|
||||
.setSysOrigin(cmd.requireReqSysOriginEnum())
|
||||
.setRequestPlatform(Objects.toString(cmd.getReqClient()))
|
||||
.setSendUserId(cmd.requiredReqUserId())
|
||||
.setAccepts(List.of(
|
||||
new GiveAwayGiftRoomAccepts()
|
||||
.setSeatIndex(null)
|
||||
.setAcceptUserId(cmd.getAcceptUserId())
|
||||
))
|
||||
.setQuantity(cmd.getQuantity())
|
||||
.setGiftConfig(toGiveGiftConfig(giftConfig))
|
||||
.setRoomId(null)
|
||||
.setSendScene(GiftSendSceneEnum.PRIVATE_CHAT)
|
||||
.setTrackId(trackId)
|
||||
.setBagGift(Boolean.FALSE)
|
||||
.setCreateTime(Objects.isNull(cmd.getReqTime()) ? TimestampUtils.now()
|
||||
: new Timestamp(cmd.getReqTime().getTime()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ import com.red.circle.component.mq.service.Action;
|
||||
import com.red.circle.component.mq.service.ConsumerMessage;
|
||||
import com.red.circle.component.mq.service.MessageListener;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.external.inner.endpoint.message.ImMessageClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomC2CMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
@ -33,12 +35,14 @@ import com.red.circle.other.app.command.game.roompk.GameRoomPkUserCmdExe;
|
||||
import com.red.circle.other.app.command.game.teampk.GameTeamPkBaseInfoCmdExe;
|
||||
import com.red.circle.other.app.common.gift.GameLuckyGiftCommon;
|
||||
import com.red.circle.other.app.dto.clientobject.game.*;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.PrivateGiftImPayloadCO;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.SendGiftNotifyCO;
|
||||
import com.red.circle.other.app.dto.cmd.game.barrage.GameUserEffectsCmd;
|
||||
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
|
||||
import com.red.circle.other.domain.live.LiveHeartbeatCache;
|
||||
import com.red.circle.other.domain.model.user.ability.RegionConfig;
|
||||
import com.red.circle.other.domain.ranking.RankingActivityType;
|
||||
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
|
||||
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
|
||||
import com.red.circle.other.infra.database.cache.service.other.LiveHeartbeatCacheService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser;
|
||||
@ -59,6 +63,7 @@ import com.red.circle.other.inner.enums.config.EnumConfigKey;
|
||||
import com.red.circle.other.inner.enums.material.GiftSpecialEnum;
|
||||
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
|
||||
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardPropsDTO;
|
||||
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
|
||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import com.red.circle.tool.core.date.TimestampUtils;
|
||||
@ -86,6 +91,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class GiveGiftsListener implements MessageListener {
|
||||
|
||||
private static final String TAG = "礼物消息";
|
||||
private static final String PRIVATE_GIFT_IM_DESC = "PRIVATE_GIFT";
|
||||
private final GameMqMessage gameMqMessage;
|
||||
private final GiftMqMessage giftMqMessage;
|
||||
private final TeamMemberService teamMemberService;
|
||||
@ -98,6 +104,8 @@ public class GiveGiftsListener implements MessageListener {
|
||||
private final LiveHeartbeatCacheService liveHeartbeatCacheService;
|
||||
private final GiftGiveRunningWaterService giftGiveRunningWaterService;
|
||||
private final ImGroupClient imGroupClient;
|
||||
private final ImMessageClient imMessageClient;
|
||||
private final GiftCacheService giftCacheService;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
private final GameTeamPkBaseInfoCmdExe gameTeamPkBaseInfoCmdExe;
|
||||
private final GameRoomPkUserCmdExe gameRoomPkUserCmdExe;
|
||||
@ -370,6 +378,10 @@ public class GiveGiftsListener implements MessageListener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.privateChatScene()) {
|
||||
sendPrivateGiftIM(event);
|
||||
}
|
||||
|
||||
if (!SysOriginPlatformEnum.isVoiceSystem(runningWater.getSysOrigin())) {
|
||||
return;
|
||||
}
|
||||
@ -393,8 +405,47 @@ public class GiveGiftsListener implements MessageListener {
|
||||
// 排行榜相关
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.RANK_COUNT, runningWater);
|
||||
|
||||
// 火箭统计相关
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater);
|
||||
if (!event.privateChatScene()) {
|
||||
// 火箭统计相关
|
||||
offlineProcessGiftEvent(OfflineStatisticsGiftEventType.ROCKET_COUNT, runningWater);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPrivateGiftIM(GiveAwayGiftBatchEvent event) {
|
||||
if (CollectionUtils.isEmpty(event.getAccepts()) || event.getAccepts().get(0).getAcceptUserId() == null) {
|
||||
log.warn("私聊礼物IM发送失败,接收人为空:{}", JacksonUtils.toJson(event));
|
||||
return;
|
||||
}
|
||||
Long acceptUserId = event.getAccepts().get(0).getAcceptUserId();
|
||||
try {
|
||||
GiftConfigDTO giftConfig = giftCacheService.getById(event.giftId());
|
||||
PrivateGiftImPayloadCO payload = new PrivateGiftImPayloadCO()
|
||||
.setType(PRIVATE_GIFT_IM_DESC)
|
||||
.setScene(event.sendSceneOrRoom().name())
|
||||
.setTrackId(event.getTrackId())
|
||||
.setSendUserId(event.getSendUserId())
|
||||
.setAcceptUserId(acceptUserId)
|
||||
.setGiftId(event.giftId())
|
||||
.setGiftName(Objects.nonNull(giftConfig) ? giftConfig.getGiftName() : null)
|
||||
.setGiftPhoto(Objects.nonNull(giftConfig) ? giftConfig.getGiftPhoto() : null)
|
||||
.setGiftSourceUrl(Objects.nonNull(giftConfig) ? giftConfig.getGiftSourceUrl() : null)
|
||||
.setGiftType(event.giftType())
|
||||
.setGiftTab(event.getGiftConfig().getGiftTab())
|
||||
.setGiftCandy(event.getGiftConfig().getGiftCandy())
|
||||
.setQuantity(event.getQuantity())
|
||||
.setCreateTime(getCreateTime(event).getTime());
|
||||
imMessageClient.sendCustomMessage(CustomC2CMsgBodyCmd.builder()
|
||||
.fromAccount(Objects.toString(event.getSendUserId()))
|
||||
.toAccount(Objects.toString(acceptUserId))
|
||||
.desc(PRIVATE_GIFT_IM_DESC)
|
||||
.data(payload)
|
||||
.build());
|
||||
log.info("私聊礼物IM发送成功,trackId={},sendUserId={},acceptUserId={}",
|
||||
event.getTrackId(), event.getSendUserId(), acceptUserId);
|
||||
} catch (Exception e) {
|
||||
log.warn("私聊礼物IM发送失败,trackId={},sendUserId={},acceptUserId={}",
|
||||
event.getTrackId(), event.getSendUserId(), acceptUserId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<GiftGiveRunningWater,Boolean> saveRunningWater(GiveAwayGiftBatchEvent event) {
|
||||
@ -444,6 +495,7 @@ public class GiveGiftsListener implements MessageListener {
|
||||
.processed(Boolean.FALSE)
|
||||
.trackId(event.getTrackId().toString())
|
||||
.originId(Objects.nonNull(event.getRoomId()) ? event.getRoomId().toString() : "OTHER")
|
||||
.sendScene(event.sendSceneOrRoom().name())
|
||||
.dynamicContentId(Objects.nonNull(event.getDynamicContentId()) ? event.getDynamicContentId().toString() : null)
|
||||
.sysOrigin(event.getSysOrigin().name())
|
||||
.requestPlatform(event.getRequestPlatform())
|
||||
|
||||
@ -42,6 +42,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||
@RequiredArgsConstructor
|
||||
public class OnlineStatusUploadListener implements MessageListener {
|
||||
|
||||
private static final long ACTIVE_BITMAP_MAX_OFFSET = 50_000_000L;
|
||||
|
||||
private final ExpandService expandService;
|
||||
private final UserSVipGateway userSVipGateway;
|
||||
private final WalletGoldClient walletGoldClient;
|
||||
@ -109,7 +111,7 @@ public class OnlineStatusUploadListener implements MessageListener {
|
||||
}
|
||||
|
||||
private void countActiveUser(UserProfile userProfile) {
|
||||
long account = Objects.hash(userProfile.getAccount());
|
||||
long account = activeBitmapOffset(userProfile.getAccount());
|
||||
if (userProfileGateway.checkTeamMember(userProfile.getId())) {
|
||||
countUserActiveIndexService.countAnchorActiveUser(account);
|
||||
countUserActiveIndexService.countAnchorActiveUser(userProfile.getOriginSys(), account);
|
||||
@ -120,6 +122,20 @@ public class OnlineStatusUploadListener implements MessageListener {
|
||||
countUserActiveIndexService.countOrdinaryActiveUser(userProfile.getOriginSys(), account);
|
||||
}
|
||||
|
||||
private long activeBitmapOffset(String account) {
|
||||
if (StringUtils.isNumeric(account)) {
|
||||
try {
|
||||
long offset = Long.parseLong(account);
|
||||
if (offset >= 0 && offset <= ACTIVE_BITMAP_MAX_OFFSET) {
|
||||
return offset;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Keep unusual account ids bounded instead of expanding Redis bitmaps.
|
||||
}
|
||||
}
|
||||
return Math.floorMod(Objects.hashCode(account), ACTIVE_BITMAP_MAX_OFFSET);
|
||||
}
|
||||
|
||||
private void addOnlineUser(UserOnlineStatusEvent event, UserProfile userProfile) {
|
||||
if (StringUtils.isBlank(event.getSessionId())) {
|
||||
return;
|
||||
|
||||
@ -0,0 +1,202 @@
|
||||
package com.red.circle.other.app.scheduler;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.component.redis.annotation.TaskCacheLock;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.other.app.command.redpacket.SendRoomRedPacketCmdExe;
|
||||
import com.red.circle.other.app.dto.cmd.SendRoomRedPacketCmd;
|
||||
import com.red.circle.other.app.enums.RoomRewardLevelEnum;
|
||||
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
|
||||
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
|
||||
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
|
||||
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
|
||||
import com.red.circle.tool.core.collection.CollectionUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
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
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(name = "scheduler.room-contribution-weekly-reward", havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public class RoomContributionWeeklyRewardTask {
|
||||
|
||||
private static final Integer DEFAULT_EXPIRE_MINUTES = 10;
|
||||
private static final Integer DEFAULT_TOTAL_COUNT = 10;
|
||||
private static final BigDecimal DEFAULT_MIN_CONTRIBUTION = BigDecimal.valueOf(100000);
|
||||
private static final BigDecimal TARAB_MIN_CONTRIBUTION = BigDecimal.valueOf(150000);
|
||||
|
||||
private final SendRoomRedPacketCmdExe sendRoomRedPacketCmdExe;
|
||||
private final RoomProfileManagerService roomProfileManagerService;
|
||||
private final RoomContributionActivityCountService roomContributionActivityCountService;
|
||||
|
||||
@Value("${activity.room-contribution.weekly-reward.expire-minutes:10}")
|
||||
private Integer expireMinutes;
|
||||
|
||||
@Value("${activity.room-contribution.weekly-reward.total-count:10}")
|
||||
private Integer totalCount;
|
||||
|
||||
@Value("${activity.room-contribution.weekly-reward.repair-enabled:true}")
|
||||
private Boolean repairEnabled;
|
||||
|
||||
@Value("${activity.room-contribution.weekly-reward.sys-origins:ATYOU}")
|
||||
private String enabledSysOrigins;
|
||||
|
||||
/**
|
||||
* 每周一沙特时间 00:08 发送上周房间周流水奖励.
|
||||
*/
|
||||
@Scheduled(cron = "0 8 0 ? * MON", zone = "Asia/Riyadh")
|
||||
@TaskCacheLock(key = "ROOM_CONTRIBUTION_WEEKLY_REWARD_TASK", expireSecond = 600)
|
||||
public void sendLastWeekRoomContributionRewards() {
|
||||
sendLastWeekRoomContributionRewards("weekly");
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动后和每分钟补偿上周未发奖励,防止周一任务异常后永久漏发.
|
||||
*/
|
||||
@Scheduled(
|
||||
initialDelayString = "${activity.room-contribution.weekly-reward.repair-initial-delay-ms:60000}",
|
||||
fixedDelayString = "${activity.room-contribution.weekly-reward.repair-fixed-delay-ms:60000}")
|
||||
@TaskCacheLock(key = "ROOM_CONTRIBUTION_WEEKLY_REWARD_TASK", expireSecond = 60)
|
||||
public void repairLastWeekRoomContributionRewards() {
|
||||
if (!Boolean.TRUE.equals(repairEnabled)) {
|
||||
return;
|
||||
}
|
||||
sendLastWeekRoomContributionRewards("repair");
|
||||
}
|
||||
|
||||
private void sendLastWeekRoomContributionRewards(String trigger) {
|
||||
log.warn("[RoomContributionWeeklyReward] start trigger={}", trigger);
|
||||
|
||||
List<RoomContributionActivityCount> list =
|
||||
roomContributionActivityCountService.listLastWeekRewardUnsentData();
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
log.warn("[RoomContributionWeeklyReward] no unsent reward data trigger={}", trigger);
|
||||
return;
|
||||
}
|
||||
|
||||
int success = 0;
|
||||
int fail = 0;
|
||||
for (RoomContributionActivityCount data : list) {
|
||||
try {
|
||||
if (sendReward(data)) {
|
||||
success++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail++;
|
||||
log.error("[RoomContributionWeeklyReward] send failed roomId={} dataId={} amount={}",
|
||||
data.getRoomId(), data.getId(), data.getContributionValue(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("[RoomContributionWeeklyReward] end trigger={} total={} success={} fail={}",
|
||||
trigger, list.size(), success, fail);
|
||||
}
|
||||
|
||||
private boolean sendReward(RoomContributionActivityCount data) {
|
||||
if (Objects.isNull(data) || Objects.isNull(data.getRoomId())
|
||||
|| Objects.isNull(data.getContributionValue())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomProfileManager room = roomProfileManagerService.getById(data.getRoomId());
|
||||
SysOriginPlatformEnum sysOrigin = getSysOrigin(room);
|
||||
if (Objects.isNull(room) || Objects.isNull(sysOrigin)) {
|
||||
log.warn("[RoomContributionWeeklyReward] skip invalid room roomId={} dataId={}",
|
||||
data.getRoomId(), data.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getEnabledSysOrigins().contains(sysOrigin)) {
|
||||
log.warn("[RoomContributionWeeklyReward] skip disabled sysOrigin roomId={} dataId={} sysOrigin={}",
|
||||
data.getRoomId(), data.getId(), sysOrigin);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!meetsPlatformThreshold(sysOrigin, data.getContributionValue())) {
|
||||
log.warn("[RoomContributionWeeklyReward] skip threshold roomId={} dataId={} sysOrigin={} amount={}",
|
||||
data.getRoomId(), data.getId(), sysOrigin, data.getContributionValue());
|
||||
return false;
|
||||
}
|
||||
|
||||
RoomRewardLevelEnum level = RoomRewardLevelEnum
|
||||
.getLevelByAmount(data.getContributionValue().longValue());
|
||||
if (Objects.isNull(level)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SendRoomRedPacketCmd cmd = new SendRoomRedPacketCmd();
|
||||
cmd.setRoomId(data.getRoomId());
|
||||
cmd.setTotalAmount(level.getRewardCoins());
|
||||
cmd.setTotalCount(getTotalCount(level.getRewardCoins()));
|
||||
cmd.setExpireMinutes(getExpireMinutes());
|
||||
cmd.setReqUserId(room.getUserId());
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of(sysOrigin.name()));
|
||||
|
||||
if (Objects.isNull(sendRoomRedPacketCmdExe.executeRoomContributionReward(cmd, data.getId()))) {
|
||||
return false;
|
||||
}
|
||||
log.info("[RoomContributionWeeklyReward] sent roomId={} dataId={} level={} rewardCoins={}",
|
||||
data.getRoomId(), data.getId(), level.getLevel(), level.getRewardCoins());
|
||||
return true;
|
||||
}
|
||||
|
||||
private SysOriginPlatformEnum getSysOrigin(RoomProfileManager room) {
|
||||
if (Objects.isNull(room) || Objects.isNull(room.getSysOrigin())) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return SysOriginPlatformEnum.valueOf(room.getSysOrigin());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean meetsPlatformThreshold(SysOriginPlatformEnum sysOrigin, BigDecimal contribution) {
|
||||
BigDecimal minContribution = Objects.equals(sysOrigin, SysOriginPlatformEnum.TARAB)
|
||||
? TARAB_MIN_CONTRIBUTION : DEFAULT_MIN_CONTRIBUTION;
|
||||
return contribution.compareTo(minContribution) >= 0;
|
||||
}
|
||||
|
||||
private Set<SysOriginPlatformEnum> getEnabledSysOrigins() {
|
||||
if (Objects.isNull(enabledSysOrigins) || enabledSysOrigins.isBlank()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return Arrays.stream(enabledSysOrigins.split(","))
|
||||
.map(String::trim)
|
||||
.filter(item -> !item.isBlank())
|
||||
.map(SysOriginPlatformEnum::toEnum)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Integer getExpireMinutes() {
|
||||
if (Objects.isNull(expireMinutes) || !List.of(1, 3, 5, 10).contains(expireMinutes)) {
|
||||
return DEFAULT_EXPIRE_MINUTES;
|
||||
}
|
||||
return expireMinutes;
|
||||
}
|
||||
|
||||
private Integer getTotalCount(Long rewardCoins) {
|
||||
int count = Objects.isNull(totalCount) ? DEFAULT_TOTAL_COUNT : totalCount;
|
||||
count = Math.max(1, Math.min(count, 100));
|
||||
if (Objects.nonNull(rewardCoins)) {
|
||||
count = Math.min(count, rewardCoins.intValue());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -60,6 +61,11 @@ public class GiftServiceImpl implements GiftService {
|
||||
return giftGiveAwayBatchCmdExe.execute(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd) {
|
||||
return giftGiveAwayBatchCmdExe.executePrivate(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd) {
|
||||
return luckyGiftGiveCmdExe.execute(cmd);
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
package com.red.circle.other.app.convertor.material;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.framework.core.request.RequestClientEnum;
|
||||
import com.red.circle.mq.business.model.event.gift.GiftSendSceneEnum;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
class GiftAppConvertorTest {
|
||||
|
||||
private final GiftAppConvertor convertor = Mappers.getMapper(GiftAppConvertor.class);
|
||||
|
||||
@Test
|
||||
void shouldBuildPrivateGiftEventWithOneAcceptAndNoRoom() {
|
||||
GiveAwayPrivateGiftCmd cmd = new GiveAwayPrivateGiftCmd();
|
||||
cmd.setReqUserId(1L);
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
cmd.setReqClient(RequestClientEnum.Android);
|
||||
cmd.setAcceptUserId(2L);
|
||||
cmd.setGiftId(3L);
|
||||
cmd.setQuantity(4);
|
||||
|
||||
var event = convertor.toGiveAwayPrivateGiftEvent(cmd, giftConfig(), 99L);
|
||||
|
||||
assertEquals(GiftSendSceneEnum.PRIVATE_CHAT, event.getSendScene());
|
||||
assertTrue(event.privateChatScene());
|
||||
assertNull(event.getRoomId());
|
||||
assertEquals(1L, event.getSendUserId());
|
||||
assertEquals(1, event.getAccepts().size());
|
||||
assertEquals(2L, event.getAccepts().get(0).getAcceptUserId());
|
||||
assertEquals(4, event.getQuantity());
|
||||
assertEquals(99L, event.getTrackId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepBatchGiftEventAsRoomScene() {
|
||||
GiveAwayGiftBatchCmd cmd = new GiveAwayGiftBatchCmd();
|
||||
cmd.setReqUserId(1L);
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of(SysOriginPlatformEnum.ATYOU.name()));
|
||||
cmd.setReqClient(RequestClientEnum.Android);
|
||||
cmd.setAcceptUserIds(List.of(2L, 2L));
|
||||
cmd.setGiftId(3L);
|
||||
cmd.setQuantity(4);
|
||||
cmd.setRoomId(5L);
|
||||
|
||||
var event = convertor.toGiveAwayGiftBatchEvent(cmd, giftConfig(), 99L);
|
||||
|
||||
assertEquals(GiftSendSceneEnum.ROOM, event.getSendScene());
|
||||
assertFalse(event.privateChatScene());
|
||||
assertEquals(5L, event.getRoomId());
|
||||
assertEquals(1, event.getAccepts().size());
|
||||
assertEquals(2L, event.getAccepts().get(0).getAcceptUserId());
|
||||
}
|
||||
|
||||
private GiftConfigDTO giftConfig() {
|
||||
return new GiftConfigDTO()
|
||||
.setId(3L)
|
||||
.setGiftCandy(new BigDecimal("10"))
|
||||
.setGiftIntegral(BigDecimal.ZERO)
|
||||
.setType("GOLD")
|
||||
.setGiftTab("NORMAL");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.red.circle.other.app.dto.clientobject.gift;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 私聊礼物 IM 自定义消息内容.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class PrivateGiftImPayloadCO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String type;
|
||||
private String scene;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long trackId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long sendUserId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long acceptUserId;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long giftId;
|
||||
|
||||
private String giftName;
|
||||
private String giftPhoto;
|
||||
private String giftSourceUrl;
|
||||
private String giftType;
|
||||
private String giftTab;
|
||||
private BigDecimal giftCandy;
|
||||
private Integer quantity;
|
||||
private Long createTime;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.red.circle.other.app.dto.cmd.gift;
|
||||
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
/**
|
||||
* 私聊赠送礼物.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class GiveAwayPrivateGiftCmd extends AppExtCommand {
|
||||
|
||||
/**
|
||||
* 接收人.
|
||||
*/
|
||||
@NotNull(message = "acceptUserId required.")
|
||||
private Long acceptUserId;
|
||||
|
||||
/**
|
||||
* 礼物编号.
|
||||
*/
|
||||
@NotNull(message = "giftId required.")
|
||||
private Long giftId;
|
||||
|
||||
/**
|
||||
* 礼物数量.
|
||||
*/
|
||||
@Range(min = 1, max = 7777, message = "quantity range 1~7777")
|
||||
@NotNull(message = "quantity required.")
|
||||
private Integer quantity;
|
||||
|
||||
public BigDecimal calculateConsumeGolds(BigDecimal giftGolds) {
|
||||
return giftGolds.multiply(BigDecimal.valueOf(quantity));
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import com.red.circle.other.app.dto.clientobject.gift.PremiumGiftWallCO;
|
||||
import com.red.circle.other.app.dto.clientobject.gift.UserGiftBackpackCO;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBackpackCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
|
||||
import com.red.circle.other.app.dto.cmd.gift.GiveAwayPrivateGiftCmd;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@ -27,6 +28,8 @@ public interface GiftService {
|
||||
|
||||
BigDecimal giveAwayGiftBatch(GiveAwayGiftBatchCmd cmd);
|
||||
|
||||
BigDecimal giveAwayPrivateGift(GiveAwayPrivateGiftCmd cmd);
|
||||
|
||||
BigDecimal giveLuckyGift(GiveAwayGiftBatchCmd cmd);
|
||||
|
||||
List<GiftWallCO> listUserGiftWall(AppUserIdCmd cmd);
|
||||
|
||||
@ -36,6 +36,12 @@ public interface RoomRedPacketGateway {
|
||||
*/
|
||||
void updateRefundStatus(String packetId, Integer refundStatus);
|
||||
|
||||
/**
|
||||
* 重置平台红包发布参数.
|
||||
*/
|
||||
void resetPlatformPacket(String packetId, Long totalAmount, Integer totalCount,
|
||||
Integer expireMinutes, LocalDateTime expireTime);
|
||||
|
||||
/**
|
||||
* 查询过期且未退款的红包
|
||||
*/
|
||||
|
||||
@ -62,6 +62,31 @@ public class RoomContributionActivityCount extends Convert implements Serializab
|
||||
*/
|
||||
Boolean rewardCoinsSent;
|
||||
|
||||
/**
|
||||
* 奖励金币是否发送中.
|
||||
*/
|
||||
Boolean rewardCoinsSending;
|
||||
|
||||
/**
|
||||
* 奖励金币红包ID.
|
||||
*/
|
||||
String rewardCoinsPacketId;
|
||||
|
||||
/**
|
||||
* 奖励金币发送时间.
|
||||
*/
|
||||
Timestamp rewardCoinsSentTime;
|
||||
|
||||
/**
|
||||
* 奖励金币发送中时间.
|
||||
*/
|
||||
Timestamp rewardCoinsSendingTime;
|
||||
|
||||
/**
|
||||
* 奖励金币发送失败原因.
|
||||
*/
|
||||
String rewardCoinsSendError;
|
||||
|
||||
/**
|
||||
* 比例.
|
||||
*/
|
||||
|
||||
@ -47,6 +47,11 @@ public class GiftGiveRunningWater implements Serializable {
|
||||
*/
|
||||
private String originId;
|
||||
|
||||
/**
|
||||
* 发送场景.
|
||||
*/
|
||||
private String sendScene;
|
||||
|
||||
/**
|
||||
* 动态id
|
||||
*/
|
||||
|
||||
@ -92,11 +92,26 @@ public interface RoomContributionActivityCountService {
|
||||
*/
|
||||
List<RoomContributionActivityCount> listRoomContributionByPH(Long roomId,BigDecimal number);
|
||||
|
||||
/**
|
||||
* 更新奖励金币状态为已发放
|
||||
*/
|
||||
/**
|
||||
* 更新奖励金币状态为已发放
|
||||
*/
|
||||
boolean updateRewardCoinsSent(String id);
|
||||
|
||||
/**
|
||||
* 抢占奖励金币发送记录.
|
||||
*/
|
||||
boolean markRewardCoinsSending(String id, String packetId);
|
||||
|
||||
/**
|
||||
* 标记奖励金币已发送.
|
||||
*/
|
||||
boolean markRewardCoinsSent(String id, String packetId);
|
||||
|
||||
/**
|
||||
* 释放奖励金币发送中状态,等待重试.
|
||||
*/
|
||||
void resetRewardCoinsSending(String id, String packetId, String reason);
|
||||
|
||||
/**
|
||||
* 获取所有房间上周贡献数据(未领取状态)
|
||||
*
|
||||
@ -104,6 +119,13 @@ public interface RoomContributionActivityCountService {
|
||||
*/
|
||||
List<RoomContributionActivityCount> getAllRoomsLastWeekData();
|
||||
|
||||
/**
|
||||
* 获取所有房间上周贡献数据(平台奖励未发放状态)
|
||||
*
|
||||
* @return 所有房间上周贡献数据列表
|
||||
*/
|
||||
List<RoomContributionActivityCount> listLastWeekRewardUnsentData();
|
||||
|
||||
/**
|
||||
* 根据房间ID和日期查询数据
|
||||
*
|
||||
|
||||
@ -14,6 +14,7 @@ import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -45,6 +46,7 @@ public class RoomContributionActivityCountServiceImpl implements
|
||||
private final MongoPageHelper mongoPageHelper;
|
||||
private static final BigDecimal integralMin = BigDecimal.valueOf(100000);
|
||||
private static final BigDecimal otherIntegralMin = BigDecimal.valueOf(150000);
|
||||
private static final long REWARD_SENDING_EXPIRE_MILLIS = 2 * 60 * 1000L;
|
||||
|
||||
private Criteria getUniquenessCriteria(Long roomId, Integer dateNumber) {
|
||||
return Criteria.where("id").is(getUniqueId(roomId, dateNumber));
|
||||
@ -291,10 +293,53 @@ public class RoomContributionActivityCountServiceImpl implements
|
||||
return result.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markRewardCoinsSending(String id, String packetId) {
|
||||
Query query = Query.query(new Criteria().andOperator(
|
||||
Criteria.where("_id").is(id),
|
||||
unsentRewardCriteria(),
|
||||
rewardNotSendingOrExpiredCriteria()
|
||||
));
|
||||
Update update = new Update()
|
||||
.set("rewardCoinsSending", Boolean.TRUE)
|
||||
.set("rewardCoinsPacketId", packetId)
|
||||
.set("rewardCoinsSendingTime", TimestampUtils.now())
|
||||
.unset("rewardCoinsSendError");
|
||||
UpdateResult result = mongoTemplate.updateFirst(query, update,
|
||||
RoomContributionActivityCount.class);
|
||||
return result.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markRewardCoinsSent(String id, String packetId) {
|
||||
Query query = Query.query(Criteria.where("_id").is(id)
|
||||
.and("rewardCoinsPacketId").is(packetId));
|
||||
Update update = new Update()
|
||||
.set("rewardCoinsSent", Boolean.TRUE)
|
||||
.set("rewardCoinsSending", Boolean.FALSE)
|
||||
.set("rewardCoinsSentTime", TimestampUtils.now())
|
||||
.unset("rewardCoinsSendError");
|
||||
UpdateResult result = mongoTemplate.updateFirst(query, update,
|
||||
RoomContributionActivityCount.class);
|
||||
return result.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetRewardCoinsSending(String id, String packetId, String reason) {
|
||||
Query query = Query.query(new Criteria().andOperator(
|
||||
Criteria.where("_id").is(id),
|
||||
Criteria.where("rewardCoinsPacketId").is(packetId),
|
||||
unsentRewardCriteria()
|
||||
));
|
||||
Update update = new Update()
|
||||
.set("rewardCoinsSending", Boolean.FALSE)
|
||||
.set("rewardCoinsSendError", limitErrorReason(reason));
|
||||
mongoTemplate.updateFirst(query, update, RoomContributionActivityCount.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoomContributionActivityCount> getAllRoomsLastWeekData() {
|
||||
// 获取上周的 dateNumber
|
||||
Integer lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getThisMonday() - 7;
|
||||
Integer lastWeekDateNumber = getLastWeekDateNumber();
|
||||
|
||||
Criteria criteria = Criteria.where("dateNumber").is(lastWeekDateNumber)
|
||||
.and("received").is(Boolean.FALSE);
|
||||
@ -302,6 +347,18 @@ public class RoomContributionActivityCountServiceImpl implements
|
||||
return mongoTemplate.find(Query.query(criteria), RoomContributionActivityCount.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoomContributionActivityCount> listLastWeekRewardUnsentData() {
|
||||
Criteria criteria = new Criteria().andOperator(
|
||||
Criteria.where("dateNumber").is(getLastWeekDateNumber()),
|
||||
Criteria.where("contributionValue").gte(integralMin),
|
||||
unsentRewardCriteria(),
|
||||
rewardNotSendingOrExpiredCriteria()
|
||||
);
|
||||
|
||||
return mongoTemplate.find(Query.query(criteria), RoomContributionActivityCount.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoomContributionActivityCount> findByRoomIdAndDateNumber(Long roomId, Integer dateNumber) {
|
||||
Criteria criteria = Criteria.where("roomId").is(roomId)
|
||||
@ -315,4 +372,32 @@ public class RoomContributionActivityCountServiceImpl implements
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
private Integer getLastWeekDateNumber() {
|
||||
return Integer.valueOf(ZonedDateTimeAsiaRiyadhUtils.getLastWeekMonday()
|
||||
.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
||||
}
|
||||
|
||||
private Criteria unsentRewardCriteria() {
|
||||
return new Criteria().orOperator(
|
||||
Criteria.where("rewardCoinsSent").exists(false),
|
||||
Criteria.where("rewardCoinsSent").ne(Boolean.TRUE)
|
||||
);
|
||||
}
|
||||
|
||||
private Criteria rewardNotSendingOrExpiredCriteria() {
|
||||
return new Criteria().orOperator(
|
||||
Criteria.where("rewardCoinsSending").exists(false),
|
||||
Criteria.where("rewardCoinsSending").ne(Boolean.TRUE),
|
||||
Criteria.where("rewardCoinsSendingTime")
|
||||
.lt(new java.sql.Timestamp(System.currentTimeMillis() - REWARD_SENDING_EXPIRE_MILLIS))
|
||||
);
|
||||
}
|
||||
|
||||
private String limitErrorReason(String reason) {
|
||||
if (StringUtils.isBlank(reason) || reason.length() <= 500) {
|
||||
return reason;
|
||||
}
|
||||
return reason.substring(0, 500);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ public class GiftGiveRunningWaterServiceImpl implements GiftGiveRunningWaterServ
|
||||
.songId(document.getLong("songId"))
|
||||
.trackId(document.getString("trackId"))
|
||||
.originId(document.getString("originId"))
|
||||
.sendScene(document.getString("sendScene"))
|
||||
.dynamicContentId(document.getString("dynamicContentId"))
|
||||
.sysOrigin(document.getString("sysOrigin"))
|
||||
.requestPlatform(document.getString("requestPlatform"))
|
||||
|
||||
@ -70,7 +70,7 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
.find(Query.query(Criteria.where("region").is(region)
|
||||
.and("release").is(Boolean.TRUE)
|
||||
.and("sysOrigin").is(sysOrigin))
|
||||
.with(Sort.by(Sort.Order.desc("createTime"))),
|
||||
.with(newestFirst()),
|
||||
TeamPolicyManager.class);
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
Criteria.where("region").is(region)
|
||||
.and("release").is(Boolean.TRUE)
|
||||
.and("sysOrigin").is(sysOrigin)
|
||||
), TeamPolicyManager.class);
|
||||
).with(newestFirst()), TeamPolicyManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -98,7 +98,7 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
}
|
||||
criteria.and("countryCode").is(countryCode);
|
||||
|
||||
return mongoTemplate.findOne(Query.query(criteria), TeamPolicyManager.class);
|
||||
return mongoTemplate.findOne(Query.query(criteria).with(newestFirst()), TeamPolicyManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,7 +109,8 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
}
|
||||
|
||||
List<TeamPolicyManager> teamPolicyManagers = mongoTemplate.find(
|
||||
Query.query(Criteria.where("region").in(regions).and("release").is(Boolean.TRUE)),
|
||||
Query.query(Criteria.where("region").in(regions).and("release").is(Boolean.TRUE))
|
||||
.with(newestFirst()),
|
||||
TeamPolicyManager.class);
|
||||
|
||||
if (CollectionUtils.isEmpty(teamPolicyManagers)) {
|
||||
@ -117,7 +118,7 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
}
|
||||
|
||||
return teamPolicyManagers.stream()
|
||||
.collect(Collectors.toMap(TeamPolicyManager::getRegion, v -> v));
|
||||
.collect(Collectors.toMap(TeamPolicyManager::getRegion, v -> v, (existing, replacement) -> existing));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,7 +131,8 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
// 查询所有 region 的已发布政策(所有 countryCode)
|
||||
List<TeamPolicyManager> policies = mongoTemplate.find(
|
||||
Query.query(Criteria.where("region").in(regions)
|
||||
.and("release").is(Boolean.TRUE)),
|
||||
.and("release").is(Boolean.TRUE))
|
||||
.with(newestFirst()),
|
||||
TeamPolicyManager.class);
|
||||
|
||||
if (CollectionUtils.isEmpty(policies)) {
|
||||
@ -170,6 +172,7 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
&& StringUtils.isNotBlank(policyManager.getSysOrigin())) {
|
||||
|
||||
policyManager.setHistoryRelease(Boolean.TRUE);
|
||||
disableOtherReleasedPolicies(policyManager);
|
||||
}
|
||||
|
||||
policyManager.setPolicy(policyManager.getPolicy().stream()
|
||||
@ -178,6 +181,33 @@ public class TeamPolicyManagerServiceImpl implements TeamPolicyManagerService {
|
||||
mongoTemplate.save(policyManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布政策时保持同一作用域只有一个正在使用的政策,避免端内按国家或默认政策匹配到旧版本。
|
||||
*/
|
||||
private void disableOtherReleasedPolicies(TeamPolicyManager policyManager) {
|
||||
Criteria criteria = Criteria.where("sysOrigin").is(policyManager.getSysOrigin())
|
||||
.and("region").is(policyManager.getRegion())
|
||||
.and("release").is(Boolean.TRUE)
|
||||
.and("id").ne(policyManager.getId());
|
||||
|
||||
if (Objects.equals(policyManager.getPolicyType(), TeamPolicyTypeEnum.SALARY_DIAMOND.name())) {
|
||||
criteria.and("policyType").is(TeamPolicyTypeEnum.SALARY_DIAMOND.name());
|
||||
} else {
|
||||
criteria.and("policyType").ne(TeamPolicyTypeEnum.SALARY_DIAMOND.name());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(policyManager.getCountryCode())) {
|
||||
criteria.and("countryCode").is(policyManager.getCountryCode());
|
||||
}
|
||||
|
||||
mongoTemplate.updateMulti(Query.query(criteria),
|
||||
Update.update("release", Boolean.FALSE), TeamPolicyManager.class);
|
||||
}
|
||||
|
||||
private Sort newestFirst() {
|
||||
return Sort.by(Sort.Order.desc("createTime"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Long id) {
|
||||
|
||||
|
||||
@ -36,6 +36,15 @@ public interface RoomRedPacketDAO {
|
||||
*/
|
||||
int updateRefundStatus(@Param("packetId") String packetId, @Param("refundStatus") Integer refundStatus);
|
||||
|
||||
/**
|
||||
* 重置平台红包发布参数
|
||||
*/
|
||||
int resetPlatformPacket(@Param("packetId") String packetId,
|
||||
@Param("totalAmount") Long totalAmount,
|
||||
@Param("totalCount") Integer totalCount,
|
||||
@Param("expireMinutes") Integer expireMinutes,
|
||||
@Param("expireTime") LocalDateTime expireTime);
|
||||
|
||||
/**
|
||||
* 查询过期且未退款的红包
|
||||
*/
|
||||
|
||||
@ -53,6 +53,13 @@ public class RoomRedPacketGatewayImpl implements RoomRedPacketGateway {
|
||||
roomRedPacketDAO.updateRefundStatus(packetId, refundStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPlatformPacket(String packetId, Long totalAmount, Integer totalCount,
|
||||
Integer expireMinutes, LocalDateTime expireTime) {
|
||||
roomRedPacketDAO.resetPlatformPacket(packetId, totalAmount, totalCount, expireMinutes,
|
||||
expireTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoomRedPacket> findExpiredAndNotRefunded(LocalDateTime now) {
|
||||
List<RoomRedPacketEntity> entities = roomRedPacketDAO.selectExpiredAndNotRefunded(now);
|
||||
|
||||
@ -75,6 +75,21 @@
|
||||
WHERE packet_id = #{packetId}
|
||||
</update>
|
||||
|
||||
<update id="resetPlatformPacket">
|
||||
UPDATE room_red_packet
|
||||
SET total_amount = #{totalAmount},
|
||||
total_count = #{totalCount},
|
||||
remain_amount = #{totalAmount},
|
||||
remain_count = #{totalCount},
|
||||
expire_minutes = #{expireMinutes},
|
||||
expire_time = #{expireTime},
|
||||
status = 1,
|
||||
refund_status = 0,
|
||||
update_time = NOW()
|
||||
WHERE packet_id = #{packetId}
|
||||
AND source_type = 2
|
||||
</update>
|
||||
|
||||
<select id="selectExpiredAndNotRefunded" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user