feat: add super dealer sub coin sellers

This commit is contained in:
zhx 2026-07-09 13:36:36 +08:00
parent 305d511c19
commit b46c2ac951
23 changed files with 684 additions and 9 deletions

View File

@ -23,11 +23,13 @@ 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 ...]
deploy-likei-services.sh [--mode preload|push|build-only|status] [--skip-build] [--fast] [wallet|other|external|console ...]
Examples:
/opt/aslan-deploy/deploy-likei-services.sh status
/opt/aslan-deploy/deploy-likei-services.sh --mode preload wallet
/opt/aslan-deploy/deploy-likei-services.sh --mode preload other external console
/opt/aslan-deploy/deploy-likei-services.sh --fast wallet
USE_GIT_SOURCE=1 GIT_REF=aslan_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
@ -102,6 +104,7 @@ update_source_from_git() {
service_module() {
case "$1" in
wallet) echo "rc-service/rc-service-wallet/wallet-start" ;;
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" ;;
@ -111,6 +114,7 @@ service_module() {
service_dir() {
case "$1" in
wallet) echo "rc-service/rc-service-wallet" ;;
other) echo "rc-service/rc-service-other" ;;
external) echo "rc-service/rc-service-external" ;;
console) echo "rc-service/rc-service-console" ;;
@ -120,6 +124,7 @@ service_dir() {
image_repo() {
case "$1" in
wallet) echo "$IMAGE_REGISTRY/$IMAGE_PROJECT/wallet" ;;
other) echo "$IMAGE_REGISTRY/$IMAGE_PROJECT/other" ;;
external) echo "$IMAGE_REGISTRY/$IMAGE_PROJECT/external" ;;
console) echo "$IMAGE_REGISTRY/$IMAGE_PROJECT/console" ;;
@ -290,7 +295,7 @@ rollout() {
}
status() {
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" get deploy other external console -o wide
kubectl --kubeconfig "$KUBECONFIG" -n "$NAMESPACE" get deploy wallet other external console -o wide
kubectl --kubeconfig "$KUBECONFIG" get nodes -o wide
}
@ -306,6 +311,11 @@ main() {
SKIP_BUILD=1
shift
;;
--fast)
# Code-only wallet changes can keep Maven's target cache instead of forcing clean package.
MAVEN_GOALS=package
shift
;;
-h|--help)
usage
exit 0

View File

@ -37,6 +37,11 @@ ALLOWED_BRANCHES = {
}
SERVICES = {
"wallet": {
"label": "Wallet",
"port": "9000",
"health": "/actuator/health",
},
"other": {
"label": "Other",
"port": "5800",
@ -749,7 +754,7 @@ INDEX_HTML = """<!doctype html>
</section>
</main>
<script>
const serviceNames = ["other", "external", "console"];
const serviceNames = ["wallet", "other", "external", "console"];
let currentJob = "";
let configMode = "yaml";
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS `user_freight_sub_seller_relation` (
`id` bigint NOT NULL COMMENT '主键标识',
`sys_origin` varchar(32) NOT NULL COMMENT '来源系统',
`parent_freight_id` bigint NOT NULL COMMENT '超级经销商 freight balance id',
`parent_user_id` bigint NOT NULL COMMENT '超级经销商用户 id',
`child_user_id` bigint NOT NULL COMMENT '子 coin seller 用户 id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_user` bigint DEFAULT NULL COMMENT '创建人',
`update_user` bigint DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sys_origin_child_user_id` (`sys_origin`, `child_user_id`),
KEY `idx_sys_origin_parent_user_id` (`sys_origin`, `parent_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级经销商子 coin seller 关系';

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS `user_freight_sub_seller_relation` (
`id` bigint NOT NULL COMMENT '主键标识',
`sys_origin` varchar(32) NOT NULL COMMENT '来源系统',
`parent_freight_id` bigint NOT NULL COMMENT '超级经销商 freight balance id',
`parent_user_id` bigint NOT NULL COMMENT '超级经销商用户 id',
`child_user_id` bigint NOT NULL COMMENT '子 coin seller 用户 id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_user` bigint DEFAULT NULL COMMENT '创建人',
`update_user` bigint DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sys_origin_child_user_id` (`sys_origin`, `child_user_id`),
KEY `idx_sys_origin_parent_user_id` (`sys_origin`, `parent_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级经销商子 coin seller 关系';

View File

@ -79,6 +79,21 @@ public enum FreightErrorCode implements IResponseErrorCode {
* 不是超级货运代理
*/
NOT_SUPER_FREIGHT(5512, "Not a Super Freight forwarder"),
/**
* coin seller 已绑定.
*/
SUB_FREIGHT_SELLER_EXISTS(5513, "Sub coin seller already exists"),
/**
* 不是当前超级经销商的子 coin seller.
*/
NOT_SUB_FREIGHT_SELLER(5514, "Not a sub coin seller"),
/**
* 无效的子 coin seller.
*/
INVALID_SUB_FREIGHT_SELLER(5515, "Invalid sub coin seller"),
;

View File

@ -13,6 +13,7 @@ import com.red.circle.wallet.app.dto.clientobject.FreightDealerUserSearchResultC
import com.red.circle.wallet.app.dto.clientobject.FreightRunningWaterCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSearchUserResultCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSellerCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSubSellerCO;
import com.red.circle.wallet.app.dto.cmd.*;
import com.red.circle.wallet.app.service.UserFreightService;
import java.math.BigDecimal;
@ -269,6 +270,46 @@ public class FreightRestController extends BaseController {
return userFreightService.listFreightSeller(cmd);
}
/**
* 超级经销商子 coin seller 列表.
*
* @eo.name 超级经销商子 coin seller 列表
* @eo.url /super/sub-sellers
* @eo.method get
* @eo.request-type formdata
*/
@GetMapping("/super/sub-sellers")
public List<FreightSubSellerCO> listFreightSubSeller(AppExtCommand cmd) {
return userFreightService.listFreightSubSeller(cmd);
}
/**
* 超级经销商绑定已有 coin seller.
*
* @eo.name 超级经销商绑定已有 coin seller
* @eo.url /super/sub-sellers
* @eo.method post
* @eo.request-type json
*/
@PostMapping("/super/sub-sellers")
public void addFreightSubSeller(@RequestBody @Validated FreightSubSellerCmd cmd) {
userFreightService.addFreightSubSeller(cmd);
}
/**
* 超级经销商给子 coin seller 转账.
*
* @eo.name 超级经销商给子 coin seller 转账
* @eo.url /super/sub-sellers/transfer
* @eo.method post
* @eo.request-type json
*/
@PostMapping("/super/sub-sellers/transfer")
public BigDecimal superDealerTransferToSubSeller(
@RequestBody @Validated FreightSubSellerTransferCmd cmd) {
return userFreightService.superDealerTransferToSubSeller(cmd);
}
/**
* 是否是经销商卖家.
*

View File

@ -31,6 +31,7 @@ import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
* 经销商发送货运给代理.
@ -53,6 +54,7 @@ public class DealerToFreightShipCmdExe {
private final UserPayAuthCacheService userPayAuthCacheService;
private final FreightBalanceRunningWaterService freightBalanceRunningWaterService;
@Transactional(rollbackFor = Exception.class)
public BigDecimal execute(FreightDealerShipCmd cmd) {
controlSwitch.execute(cmd.requireReqSysOriginChildEnum());
log.warn("{}", JacksonUtils.toJson(cmd));
@ -79,7 +81,8 @@ public class DealerToFreightShipCmdExe {
// 接收用户是否为货运代理
ResponseAssert.isTrue(FreightErrorCode.NOT_FREIGHT,
freightBalanceService.existsBalance(cmd.getAcceptUserId()));
freightBalanceService.existsBalance(cmd.requireReqSysOriginChildEnum(),
cmd.getAcceptUserId()));
// 发送人与接受人必须要在相同区域下才能交易
ResponseAssert.isTrue(CommonErrorCode.REGION_NOT_SUPPORTED_ERROR,
@ -112,14 +115,16 @@ public class DealerToFreightShipCmdExe {
.decrCandyBalance(cmd.requireReqSysOriginEnum(), cmd.requiredReqUserId(),
cmd.getQuantity()));
BigDecimal balance = freightBalanceService.getAvailableBalance(cmd.requiredReqUserId());
BigDecimal balance = freightBalanceService.getAvailableBalance(
cmd.requireReqSysOriginChildEnum(), cmd.requiredReqUserId());
// 插入流水
saveRunningWater(cmd, balance);
// 发货
freightBalanceService.incrCandyBalance(cmd.requireReqSysOriginEnum(), cmd.getAcceptUserId(),
cmd.getQuantity());
ResponseAssert.isTrue(WalletErrorCode.DELIVERY_FAILED,
freightBalanceService.incrCandyBalance(cmd.requireReqSysOriginEnum(),
cmd.getAcceptUserId(), cmd.getQuantity()));
// 余额如果等于0则回收货运代理徽章
freightShipGateway.removeFreightShipBadge(cmd.requiredReqUserId(), balance);
@ -181,7 +186,8 @@ public class DealerToFreightShipCmdExe {
if (Objects.nonNull(userProfile)) {
freightBalanceRunningWater.setRemark("从[" + userProfile.getAccount() + "]经销商进货");
}
BigDecimal acceptBalance = freightBalanceService.getAvailableBalance(cmd.getAcceptUserId());
BigDecimal acceptBalance = freightBalanceService.getAvailableBalance(
cmd.requireReqSysOriginChildEnum(), cmd.getAcceptUserId());
freightBalanceRunningWater.setBalance(acceptBalance.add(cmd.getQuantity()));
freightBalanceRunningWater.setId(IdWorkerUtils.getId());
freightBalanceRunningWater.setUserId(cmd.getAcceptUserId());

View File

@ -0,0 +1,56 @@
package com.red.circle.wallet.app.command.freight;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.wallet.app.command.freight.support.SuperFreightDealerChecker;
import com.red.circle.wallet.app.dto.cmd.FreightDealerShipCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerTransferCmd;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalance;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightSubSellerRelationService;
import com.red.circle.wallet.inner.error.FreightErrorCode;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import java.math.BigDecimal;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 超级经销商向已绑定子 coin seller 转账.
*/
@Service
@RequiredArgsConstructor
public class SuperDealerSubSellerTransferCmdExe {
private final DealerToFreightShipCmdExe dealerToFreightShipCmdExe;
private final SuperFreightDealerChecker superFreightDealerChecker;
private final FreightSubSellerRelationService freightSubSellerRelationService;
@Transactional(rollbackFor = Exception.class)
public BigDecimal execute(FreightSubSellerTransferCmd cmd) {
FreightBalance parentFreightBalance = superFreightDealerChecker.requireSuperDealer(cmd);
ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
ArithmeticUtils.gte(cmd.getQuantity(), BigDecimal.ONE));
FreightSubSellerRelation relation =
freightSubSellerRelationService.getByParentAndChild(cmd.requireReqSysOrigin(),
cmd.requiredReqUserId(), parentFreightBalance.getId(), cmd.getAcceptUserId());
// 子级转账必须绑定关系命中避免绕过 H5 入口给任意 coin seller 出货.
ResponseAssert.notNull(FreightErrorCode.NOT_SUB_FREIGHT_SELLER, relation);
FreightDealerShipCmd dealerShipCmd = new FreightDealerShipCmd()
.setAcceptUserId(cmd.getAcceptUserId())
.setQuantity(cmd.getQuantity())
.setPassword(cmd.getPassword());
dealerShipCmd.setReqSysOrigin(ReqSysOrigin.of(cmd.requireReqSysOrigin()));
dealerShipCmd.setReqUserId(cmd.requiredReqUserId());
dealerShipCmd.setReqTraceId(cmd.getReqTraceId());
dealerShipCmd.setReqAppIntel(cmd.getReqAppIntel());
dealerShipCmd.setReqTime(cmd.getReqTime());
return dealerToFreightShipCmdExe.execute(dealerShipCmd);
}
}

View File

@ -0,0 +1,66 @@
package com.red.circle.wallet.app.command.freight;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.inner.endpoint.user.region.UserRegionClient;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.wallet.app.command.freight.support.SuperFreightDealerChecker;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerCmd;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalance;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightBalanceService;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightSubSellerRelationService;
import com.red.circle.wallet.infra.sync.ControlSwitch;
import com.red.circle.wallet.inner.error.FreightErrorCode;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 超级经销商绑定子 coin seller.
*/
@Service
@RequiredArgsConstructor
public class UserFreightSubSellerAddCmdExe {
private final ControlSwitch controlSwitch;
private final UserRegionClient userRegionClient;
private final FreightBalanceService freightBalanceService;
private final SuperFreightDealerChecker superFreightDealerChecker;
private final FreightSubSellerRelationService freightSubSellerRelationService;
public void execute(FreightSubSellerCmd cmd) {
controlSwitch.execute(cmd.requireReqSysOriginChildEnum());
FreightBalance parentFreight = superFreightDealerChecker.requireSuperDealer(cmd);
ResponseAssert.isFalse(FreightErrorCode.INVALID_SUB_FREIGHT_SELLER,
Objects.equals(cmd.requiredReqUserId(), cmd.getUserId()));
FreightBalance childFreight = freightBalanceService.getFreightBalance(
cmd.requireReqSysOriginChildEnum(), cmd.getUserId());
ResponseAssert.notNull(FreightErrorCode.NOT_FREIGHT, childFreight);
// coin seller 必须是已有且未关闭的普通币商不能绑定经销商或超级经销商.
boolean validChild = Objects.equals(childFreight.getSysOrigin(), cmd.requireReqSysOrigin())
&& Objects.equals(childFreight.getClose(), Boolean.FALSE)
&& !Objects.equals(childFreight.getDealer(), Boolean.TRUE)
&& !Objects.equals(childFreight.getSuperDealer(), Boolean.TRUE);
ResponseAssert.isTrue(FreightErrorCode.INVALID_SUB_FREIGHT_SELLER, validChild);
ResponseAssert.isTrue(CommonErrorCode.REGION_NOT_SUPPORTED_ERROR,
ResponseAssert.requiredSuccess(
userRegionClient.checkEqRegion(cmd.requiredReqUserId(), cmd.getUserId())));
ResponseAssert.isFalse(FreightErrorCode.SUB_FREIGHT_SELLER_EXISTS,
freightSubSellerRelationService.existsByChildUserId(cmd.requireReqSysOrigin(),
cmd.getUserId()));
freightSubSellerRelationService.save(new FreightSubSellerRelation()
.setId(IdWorkerUtils.getId())
.setSysOrigin(cmd.requireReqSysOrigin())
.setParentFreightId(parentFreight.getId())
.setParentUserId(cmd.requiredReqUserId())
.setChildUserId(cmd.getUserId()));
}
}

View File

@ -0,0 +1,72 @@
package com.red.circle.wallet.app.command.freight.query;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.wallet.app.command.freight.support.SuperFreightDealerChecker;
import com.red.circle.wallet.app.dto.clientobject.FreightSubSellerCO;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalance;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightBalanceService;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightSubSellerRelationService;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 查询当前超级经销商的子 coin seller 列表.
*/
@Service
@RequiredArgsConstructor
public class UserFreightSubSellerQryExe {
private final UserProfileClient userProfileClient;
private final FreightBalanceService freightBalanceService;
private final SuperFreightDealerChecker superFreightDealerChecker;
private final FreightSubSellerRelationService freightSubSellerRelationService;
public List<FreightSubSellerCO> execute(AppExtCommand cmd) {
FreightBalance parentFreightBalance = superFreightDealerChecker.requireSuperDealer(cmd);
List<FreightSubSellerRelation> relations =
freightSubSellerRelationService.listByParentUserId(cmd.requireReqSysOrigin(),
cmd.requiredReqUserId(), parentFreightBalance.getId());
if (CollectionUtils.isEmpty(relations)) {
return CollectionUtils.newArrayList();
}
Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
userProfileClient.mapByUserIds(relations.stream()
.map(FreightSubSellerRelation::getChildUserId)
.collect(Collectors.toSet())));
return relations.stream()
.map(relation -> toCO(relation, userProfileMap.get(relation.getChildUserId()), cmd))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private FreightSubSellerCO toCO(FreightSubSellerRelation relation, UserProfileDTO userProfile,
AppExtCommand cmd) {
if (Objects.isNull(userProfile)) {
return null;
}
if (Objects.nonNull(userProfile.getOwnSpecialId())) {
userProfile.setAccount(userProfile.getOwnSpecialId().getAccount());
}
return new FreightSubSellerCO()
.setRelationId(relation.getId())
.setChildUserId(relation.getChildUserId())
.setBalance(freightBalanceService.getAvailableBalance(cmd.requireReqSysOriginChildEnum(),
relation.getChildUserId()))
.setCreateTime(relation.getCreateTime())
.setUserProfile(userProfile);
}
}

View File

@ -0,0 +1,36 @@
package com.red.circle.wallet.app.command.freight.support;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightBalance;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightBalanceService;
import com.red.circle.wallet.inner.error.FreightErrorCode;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 超级经销商服务端授权检查.
*/
@Component
@RequiredArgsConstructor
public class SuperFreightDealerChecker {
private final FreightBalanceService freightBalanceService;
public FreightBalance requireSuperDealer(AppExtCommand cmd) {
FreightBalance freightBalance = freightBalanceService.getFreightBalance(
cmd.requireReqSysOriginChildEnum(), cmd.requiredReqUserId());
ResponseAssert.notNull(FreightErrorCode.NOT_FREIGHT, freightBalance);
// 超级经销商权限必须在服务端校验H5 入口展示不能作为授权依据.
boolean isSuperDealer = Objects.equals(freightBalance.getSysOrigin(),
cmd.requireReqSysOrigin())
&& Objects.equals(freightBalance.getClose(), Boolean.FALSE)
&& Objects.equals(freightBalance.getDealer(), Boolean.TRUE)
&& Objects.equals(freightBalance.getSuperDealer(), Boolean.TRUE);
ResponseAssert.isTrue(FreightErrorCode.NOT_SUPER_FREIGHT, isSuperDealer);
return freightBalance;
}
}

View File

@ -7,8 +7,10 @@ import com.red.circle.common.business.dto.cmd.app.AppUserIdCmd;
import com.red.circle.wallet.app.command.freight.DealerToFreightShipCmdExe;
import com.red.circle.wallet.app.command.freight.DealerToUserShipCmdExe;
import com.red.circle.wallet.app.command.freight.SendFreightShipCmdExe;
import com.red.circle.wallet.app.command.freight.SuperDealerSubSellerTransferCmdExe;
import com.red.circle.wallet.app.command.freight.UserFreightSellerAddCmdExe;
import com.red.circle.wallet.app.command.freight.UserFreightSellerUsableGoldCmdExe;
import com.red.circle.wallet.app.command.freight.UserFreightSubSellerAddCmdExe;
import com.red.circle.wallet.app.command.freight.query.CheckDealerQryExe;
import com.red.circle.wallet.app.command.freight.query.CheckFreightQryExe;
import com.red.circle.wallet.app.command.freight.query.CheckFreightSellerQryExe;
@ -21,11 +23,13 @@ import com.red.circle.wallet.app.command.freight.query.UserFreightAccountQryExe;
import com.red.circle.wallet.app.command.freight.query.UserFreightAvailableBalanceQryExe;
import com.red.circle.wallet.app.command.freight.query.UserFreightSellerInfoQryExe;
import com.red.circle.wallet.app.command.freight.query.UserFreightSellerQryExe;
import com.red.circle.wallet.app.command.freight.query.UserFreightSubSellerQryExe;
import com.red.circle.wallet.app.dto.clientobject.FreightAccountCO;
import com.red.circle.wallet.app.dto.clientobject.FreightDealerUserSearchResultCO;
import com.red.circle.wallet.app.dto.clientobject.FreightRunningWaterCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSearchUserResultCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSellerCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSubSellerCO;
import com.red.circle.wallet.app.dto.cmd.FreightDealerShipCmd;
import com.red.circle.wallet.app.dto.cmd.FreightDealerToUserShipCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerCmd;
@ -33,6 +37,8 @@ import com.red.circle.wallet.app.dto.cmd.FreightSellerQryCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerRunningWaterQryCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerUsableGoldCmd;
import com.red.circle.wallet.app.dto.cmd.FreightShipSendCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerTransferCmd;
import java.math.BigDecimal;
import java.util.List;
import lombok.RequiredArgsConstructor;
@ -52,10 +58,12 @@ public class UserFreightServiceImpl implements UserFreightService {
private final SendFreightShipCmdExe sendFreightShipCmdExe;
private final DealerToUserShipCmdExe dealerToUserShipCmdExe;
private final UserFreightSellerQryExe userFreightSellerQryExe;
private final UserFreightSubSellerQryExe userFreightSubSellerQryExe;
private final CheckFreightSellerQryExe checkFreightSellerQryExe;
private final UserFreightAccountQryExe userFreightAccountQryExe;
private final DealerToFreightShipCmdExe dealerToFreightShipCmdExe;
private final UserFreightSellerAddCmdExe userFreightSellerAddCmdExe;
private final UserFreightSubSellerAddCmdExe userFreightSubSellerAddCmdExe;
private final UserFreightSellerInfoQryExe userFreightSellerInfoQryExe;
private final DealerFreightUserSearchQryExe dealerFreightUserSearchQryExe;
private final FreightSearchUserResultQryExe freightSearchUserResultQryExe;
@ -64,6 +72,7 @@ public class UserFreightServiceImpl implements UserFreightService {
private final UserFreightAvailableBalanceQryExe userFreightAvailableBalanceQryExe;
private final UserFreightSellerUsableGoldCmdExe userFreightSellerUsableGoldCmdExe;
private final FreightSellerRunningWaterFlowQryExe freightSellerRunningWaterFlowQryExe;
private final SuperDealerSubSellerTransferCmdExe superDealerSubSellerTransferCmdExe;
@Override
public void addFreightSeller(FreightSellerCmd cmd) {
@ -151,4 +160,19 @@ public class UserFreightServiceImpl implements UserFreightService {
return freightFirstRechargeCheckCmdExe.execute(cmd);
}
@Override
public List<FreightSubSellerCO> listFreightSubSeller(AppExtCommand cmd) {
return userFreightSubSellerQryExe.execute(cmd);
}
@Override
public void addFreightSubSeller(FreightSubSellerCmd cmd) {
userFreightSubSellerAddCmdExe.execute(cmd);
}
@Override
public BigDecimal superDealerTransferToSubSeller(FreightSubSellerTransferCmd cmd) {
return superDealerSubSellerTransferCmdExe.execute(cmd);
}
}

View File

@ -0,0 +1,47 @@
package com.red.circle.wallet.app.dto.clientobject;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.framework.dto.ClientObject;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 超级经销商子 coin seller 信息.
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class FreightSubSellerCO extends ClientObject {
/**
* 绑定关系 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long relationId;
/**
* coin seller 用户 id.
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long childUserId;
/**
* coin seller 币商钱包余额.
*/
private BigDecimal balance;
/**
* 绑定时间.
*/
private Date createTime;
/**
* 用户资料.
*/
private UserProfileDTO userProfile;
}

View File

@ -0,0 +1,28 @@
package com.red.circle.wallet.app.dto.cmd;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 超级经销商绑定子 coin seller.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class FreightSubSellerCmd extends AppExtCommand {
@Serial
private static final long serialVersionUID = 1L;
/**
* coin seller 用户 id.
*
* @eo.required
*/
@NotNull(message = "userId required.")
private Long userId;
}

View File

@ -0,0 +1,42 @@
package com.red.circle.wallet.app.dto.cmd;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotNull;
import java.io.Serial;
import java.math.BigDecimal;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 超级经销商给子 coin seller 转币商钱包金币.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class FreightSubSellerTransferCmd extends AppExtCommand {
@Serial
private static final long serialVersionUID = 1L;
/**
* 接收用户.
*
* @eo.required
*/
@NotNull(message = "acceptUserId required.")
private Long acceptUserId;
/**
* 数量.
*
* @eo.required
*/
@NotNull(message = "quantity required.")
private BigDecimal quantity;
/**
* 支付密码.
*/
private String password;
}

View File

@ -9,6 +9,7 @@ import com.red.circle.wallet.app.dto.clientobject.FreightDealerUserSearchResultC
import com.red.circle.wallet.app.dto.clientobject.FreightRunningWaterCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSearchUserResultCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSellerCO;
import com.red.circle.wallet.app.dto.clientobject.FreightSubSellerCO;
import com.red.circle.wallet.app.dto.cmd.FreightDealerShipCmd;
import com.red.circle.wallet.app.dto.cmd.FreightDealerToUserShipCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerCmd;
@ -16,6 +17,8 @@ import com.red.circle.wallet.app.dto.cmd.FreightSellerQryCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerRunningWaterQryCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSellerUsableGoldCmd;
import com.red.circle.wallet.app.dto.cmd.FreightShipSendCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerCmd;
import com.red.circle.wallet.app.dto.cmd.FreightSubSellerTransferCmd;
import java.math.BigDecimal;
import java.util.List;
@ -60,4 +63,10 @@ public interface UserFreightService {
boolean checkFreightFirstRecharge(AppUserIdCmd cmd);
List<FreightSubSellerCO> listFreightSubSeller(AppExtCommand cmd);
void addFreightSubSeller(FreightSubSellerCmd cmd);
BigDecimal superDealerTransferToSubSeller(FreightSubSellerTransferCmd cmd);
}

View File

@ -0,0 +1,11 @@
package com.red.circle.wallet.infra.database.rds.dao;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
/**
* 超级经销商子 coin seller 关系 DAO.
*/
public interface FreightSubSellerRelationDAO extends BaseDAO<FreightSubSellerRelation> {
}

View File

@ -0,0 +1,54 @@
package com.red.circle.wallet.infra.database.rds.entity.freight;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.red.circle.framework.mybatis.entity.TimestampBaseEntity;
import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 超级经销商与子 coin seller 的绑定关系.
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("user_freight_sub_seller_relation")
public class FreightSubSellerRelation extends TimestampBaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键标识.
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
* 来源系统.
*/
@TableField("sys_origin")
private String sysOrigin;
/**
* 超级经销商 freight balance id.
*/
@TableField("parent_freight_id")
private Long parentFreightId;
/**
* 超级经销商用户 id.
*/
@TableField("parent_user_id")
private Long parentUserId;
/**
* coin seller 用户 id.
*/
@TableField("child_user_id")
private Long childUserId;
}

View File

@ -37,6 +37,8 @@ public interface FreightBalanceService extends BaseService<FreightBalance> {
*/
boolean existsBalance(Long userId);
boolean existsBalance(SysOriginPlatformEnum sysOrigin, Long userId);
/**
* 累计余额.
*
@ -65,6 +67,8 @@ public interface FreightBalanceService extends BaseService<FreightBalance> {
*/
BigDecimal getAvailableBalance(Long userId);
BigDecimal getAvailableBalance(SysOriginPlatformEnum sysOrigin, Long userId);
/**
* 获取平台货运代理人列表.
*
@ -119,6 +123,8 @@ public interface FreightBalanceService extends BaseService<FreightBalance> {
*/
FreightBalance getFreightBalance(Long userId);
FreightBalance getFreightBalance(SysOriginPlatformEnum sysOrigin, Long userId);
/**
* 根据id获取货运代理
*/

View File

@ -0,0 +1,19 @@
package com.red.circle.wallet.infra.database.rds.service.freight;
import com.red.circle.framework.mybatis.service.BaseService;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
import java.util.List;
/**
* 超级经销商子 coin seller 关系服务.
*/
public interface FreightSubSellerRelationService extends BaseService<FreightSubSellerRelation> {
boolean existsByChildUserId(String sysOrigin, Long childUserId);
FreightSubSellerRelation getByParentAndChild(String sysOrigin, Long parentUserId,
Long parentFreightId, Long childUserId);
List<FreightSubSellerRelation> listByParentUserId(String sysOrigin, Long parentUserId,
Long parentFreightId);
}

View File

@ -59,10 +59,21 @@ public class FreightBalanceServiceImpl extends
.orElse(Boolean.FALSE);
}
@Override
public boolean existsBalance(SysOriginPlatformEnum sysOrigin, Long userId) {
return Optional.ofNullable(
query().select(FreightBalance::getClose)
.eq(FreightBalance::getSysOrigin, sysOrigin)
.eq(FreightBalance::getUserId, userId)
.last(PageConstant.LIMIT_ONE).getOne())
.map(freightBalance -> Objects.equals(freightBalance.getClose(), Boolean.FALSE))
.orElse(Boolean.FALSE);
}
@Override
public boolean incrCandyBalance(SysOriginPlatformEnum sysOrigin, Long userId,
BigDecimal quantity) {
FreightBalance freightBalance = getFreightBalance(userId);
FreightBalance freightBalance = getFreightBalance(sysOrigin, userId);
if (Objects.isNull(freightBalance)) {
return save(new FreightBalance()
@ -104,6 +115,15 @@ public class FreightBalanceServiceImpl extends
.orElse(BigDecimal.ZERO);
}
@Override
public BigDecimal getAvailableBalance(SysOriginPlatformEnum sysOrigin, Long userId) {
return Optional.ofNullable(getFreightBalance(sysOrigin, userId))
.map(balance -> balance.getEarnPoints()
.subtract(balance.getConsumptionPoints())
.setScale(2, RoundingMode.DOWN))
.orElse(BigDecimal.ZERO);
}
@Override
public Set<Long> listUserIdBySysOrigin(SysOriginPlatformEnum sysOrigin) {
return Optional.ofNullable(
@ -149,6 +169,15 @@ public class FreightBalanceServiceImpl extends
return query().eq(FreightBalance::getUserId, userId).last(PageConstant.LIMIT_ONE).getOne();
}
@Override
public FreightBalance getFreightBalance(SysOriginPlatformEnum sysOrigin, Long userId) {
return query()
.eq(FreightBalance::getSysOrigin, sysOrigin)
.eq(FreightBalance::getUserId, userId)
.last(PageConstant.LIMIT_ONE)
.getOne();
}
@Override
public FreightBalance getFreightBalanceById(Long id) {
return query()

View File

@ -0,0 +1,57 @@
package com.red.circle.wallet.infra.database.rds.service.freight.impl;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.wallet.infra.database.rds.dao.FreightSubSellerRelationDAO;
import com.red.circle.wallet.infra.database.rds.entity.freight.FreightSubSellerRelation;
import com.red.circle.wallet.infra.database.rds.service.freight.FreightSubSellerRelationService;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 超级经销商子 coin seller 关系服务实现.
*/
@Service
@RequiredArgsConstructor
public class FreightSubSellerRelationServiceImpl extends
BaseServiceImpl<FreightSubSellerRelationDAO, FreightSubSellerRelation> implements
FreightSubSellerRelationService {
@Override
public boolean existsByChildUserId(String sysOrigin, Long childUserId) {
return Optional.ofNullable(query()
.select(FreightSubSellerRelation::getId)
.eq(FreightSubSellerRelation::getSysOrigin, sysOrigin)
.eq(FreightSubSellerRelation::getChildUserId, childUserId)
.last(PageConstant.LIMIT_ONE)
.getOne()).isPresent();
}
@Override
public FreightSubSellerRelation getByParentAndChild(String sysOrigin, Long parentUserId,
Long parentFreightId, Long childUserId) {
return query()
.eq(FreightSubSellerRelation::getSysOrigin, sysOrigin)
.eq(FreightSubSellerRelation::getParentUserId, parentUserId)
.eq(FreightSubSellerRelation::getParentFreightId, parentFreightId)
.eq(FreightSubSellerRelation::getChildUserId, childUserId)
.last(PageConstant.LIMIT_ONE)
.getOne();
}
@Override
public List<FreightSubSellerRelation> listByParentUserId(String sysOrigin, Long parentUserId,
Long parentFreightId) {
return Optional.ofNullable(query()
.eq(FreightSubSellerRelation::getSysOrigin, sysOrigin)
.eq(FreightSubSellerRelation::getParentUserId, parentUserId)
.eq(FreightSubSellerRelation::getParentFreightId, parentFreightId)
.orderByDesc(FreightSubSellerRelation::getId)
.last(PageConstant.MAX_LIMIT)
.list())
.orElseGet(CollectionUtils::newArrayList);
}
}

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS `user_freight_sub_seller_relation` (
`id` bigint NOT NULL COMMENT '主键标识',
`sys_origin` varchar(32) NOT NULL COMMENT '来源系统',
`parent_freight_id` bigint NOT NULL COMMENT '超级经销商 freight balance id',
`parent_user_id` bigint NOT NULL COMMENT '超级经销商用户 id',
`child_user_id` bigint NOT NULL COMMENT '子 coin seller 用户 id',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_user` bigint DEFAULT NULL COMMENT '创建人',
`update_user` bigint DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_sys_origin_child_user_id` (`sys_origin`, `child_user_id`),
KEY `idx_sys_origin_parent_user_id` (`sys_origin`, `parent_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级经销商子 coin seller 关系';