抽奖代码

main
13233904609 2 months ago
parent 237cde5ae0
commit 0918b9ca88

@ -28,6 +28,11 @@
<artifactId>yudao-module-member-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-trade-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->

@ -1,13 +1,19 @@
package cn.iocoder.yudao.module.product.controller.app.spu;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.member.api.distributionconfig.DistributionConfigApi;
import cn.iocoder.yudao.module.member.api.distributionconfig.vo.DistributionConfigRespDTO;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRespVO;
@ -17,9 +23,11 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -33,6 +41,7 @@ import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_ENABLE;
@ -56,6 +65,13 @@ public class AppProductSpuController {
@Resource
private MemberUserApi memberUserApi;
@Resource
private DistributionConfigApi distributionConfigApi;
@Resource
@Lazy // 延迟加载,解决循环依赖的问题
private TradeOrderApi tradeOrderApi;
@GetMapping("/list-by-ids")
@Operation(summary = "获得商品 SPU 列表")
@Parameter(name = "ids", description = "编号列表", required = true)
@ -95,8 +111,35 @@ public class AppProductSpuController {
@Operation(summary = "获得商品 SPU 明细")
@Parameter(name = "id", description = "编号", required = true)
public CommonResult<AppProductSpuDetailRespVO> getSpuDetail(@RequestParam("id") Long id) {
//获取用户id
Long loginUserId = WebFrameworkUtils.getLoginUserId();
// 获得商品 SPU
ProductSpuDO spu = productSpuService.getSpu(id);
//判断商品是否需要会员等级
List<DistributionConfigRespDTO> distributionConfigDOS = distributionConfigApi.distributionConfigDOS(id);
if(ObjectUtil.isNotEmpty(distributionConfigDOS)){
DistributionConfigRespDTO distributionConfigDO = distributionConfigDOS.get(0);
if(ObjectUtil.isNotEmpty(distributionConfigDO.getBuyerLevelId())){
//查询会员当前等级
MemberLevelRespDTO memberLevelDO = memberLevelApi.getMemberLevel(distributionConfigDO.getBuyerLevelId());
//查询会员当前等级
MemberUserRespDTO user = memberUserApi.getUser(loginUserId);
MemberLevelRespDTO memberLevelDO1 = memberLevelApi.getMemberLevel(user.getLevelId());
if(ObjectUtil.isEmpty(memberLevelDO1)){
return error(1_013_018_008,"商品"+spu.getName()+"购买需要升级至会员等级"+memberLevelDO.getName());
}
if(memberLevelDO.getLevel()>memberLevelDO1.getLevel()){
return error(1_013_018_008,"商品"+spu.getName()+"购买需要升级至会员等级"+memberLevelDO.getName());
}
}
Integer orderCountBySpuId = tradeOrderApi.getOrderCountBySpuId(id);
if(ObjectUtil.isNotEmpty(distributionConfigDO.getPurchaseNum())){
if(orderCountBySpuId>=distributionConfigDO.getPurchaseNum()){
return error(1_013_018_009,"商品"+spu.getName()+"购买已达上限");
}
}
}
if (spu == null) {
throw exception(SPU_NOT_EXISTS);
}

@ -98,7 +98,7 @@ public class MemberStatisticsController {
// TODO @疯狂:要注意 date 的排序;
@GetMapping("/user-count-comparison")
@Operation(summary = "获得用户数量对照")
@PreAuthorize("@ss.hasPermission('statistics:member:query')")
// @PreAuthorize("@ss.hasPermission('statistics:member:query')")
public CommonResult<DataComparisonRespVO<MemberCountRespVO>> getUserCountComparison() {
return success(memberStatisticsService.getUserCountComparison());
}

@ -93,4 +93,5 @@ public interface ErrorCodeConstants {
ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "提现金额不能低于 {} 元");
ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, "您当前最多可提现 {} 元");
ErrorCode VERIFY_LOG_NOT_EXISTS = new ErrorCode(1_011_008_004, "门店核销记录不存在");
}

@ -17,6 +17,7 @@ public enum BrokerageRecordBizTypeEnum implements IntArrayValuable {
ORDER(1, "获得推广佣金", "获得推广佣金 {}", true),
WITHDRAW(2, "提现申请", "提现申请扣除佣金 {}", false),
WITHDRAWWEALLET(4, "提现申请", "提现申请扣除余额 {}", false),
WITHDRAW_REJECT(3, "提现申请驳回", "提现申请驳回,返还佣金 {}", true),
;

@ -57,7 +57,7 @@ public class DeliveryPickUpStoreController {
@GetMapping("/get")
@Operation(summary = "获得自提门店")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')")
// @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')")
public CommonResult<DeliveryPickUpStoreRespVO> getDeliveryPickUpStore(@RequestParam("id") Long id) {
DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(id);
return success(DeliveryPickUpStoreConvert.INSTANCE.convert(deliveryPickUpStore));

@ -53,7 +53,7 @@ public class TradeOrderController {
@GetMapping("/page")
@Operation(summary = "获得交易订单分页")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<PageResult<TradeOrderPageItemRespVO>> getOrderPage(TradeOrderPageReqVO reqVO) {
// 查询订单
PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(reqVO);
@ -74,7 +74,7 @@ public class TradeOrderController {
@GetMapping("/summary")
@Operation(summary = "获得交易订单统计")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {
return success(tradeOrderQueryService.getOrderSummary(reqVO));
}
@ -82,7 +82,7 @@ public class TradeOrderController {
@GetMapping("/get-detail")
@Operation(summary = "获得交易订单详情")
@Parameter(name = "id", description = "订单编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
// 查询订单
TradeOrderDO order = tradeOrderQueryService.getOrder(id);
@ -103,7 +103,7 @@ public class TradeOrderController {
@GetMapping("/get-express-track-list")
@Operation(summary = "获得交易订单的物流轨迹")
@Parameter(name = "id", description = "交易订单编号")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam("id") Long id) {
return success(TradeOrderConvert.INSTANCE.convertList02(
tradeOrderQueryService.getExpressTrackList(id)));
@ -144,7 +144,7 @@ public class TradeOrderController {
@PutMapping("/pick-up-by-id")
@Operation(summary = "订单核销")
@Parameter(name = "id", description = "交易订单编号")
@PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
// @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
public CommonResult<Boolean> pickUpOrderById(@RequestParam("id") Long id) {
tradeOrderUpdateService.pickUpOrderByAdmin(id);
return success(true);
@ -153,7 +153,7 @@ public class TradeOrderController {
@PutMapping("/pick-up-by-verify-code")
@Operation(summary = "订单核销")
@Parameter(name = "pickUpVerifyCode", description = "自提核销码")
@PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
// @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
public CommonResult<Boolean> pickUpOrderByVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
// 根据管理系统登录用户信息获取APP系统用户信息
String mobile = getLoginUserMobile();
@ -165,7 +165,7 @@ public class TradeOrderController {
@GetMapping("/get-by-pick-up-verify-code")
@Operation(summary = "查询核销码对应的订单")
@Parameter(name = "pickUpVerifyCode", description = "自提核销码")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
// @PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<TradeOrderDetailRespVO> getByPickUpVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
TradeOrderDO tradeOrder = tradeOrderUpdateService.getByPickUpVerifyCode(pickUpVerifyCode);
return success(TradeOrderConvert.INSTANCE.convert2(tradeOrder, null));

@ -0,0 +1,113 @@
package cn.iocoder.yudao.module.trade.controller.admin.verifylog;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.convert.verifylog.VerifyLogConvert;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo.*;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import cn.iocoder.yudao.module.trade.service.verifylog.VerifyLogService;
@Tag(name = "管理后台 - 门店核销记录")
@RestController
@RequestMapping("/trade/verify-log")
@Validated
public class VerifyLogController {
@Resource
private VerifyLogService verifyLogService;
@Resource
private MemberUserApi memberUserApi;
@PostMapping("/create")
@Operation(summary = "创建门店核销记录")
@PreAuthorize("@ss.hasPermission('trade:verify-log:create')")
public CommonResult<Long> createVerifyLog(@Valid @RequestBody VerifyLogSaveReqVO createReqVO) {
return success(verifyLogService.createVerifyLog(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新门店核销记录")
@PreAuthorize("@ss.hasPermission('trade:verify-log:update')")
public CommonResult<Boolean> updateVerifyLog(@Valid @RequestBody VerifyLogSaveReqVO updateReqVO) {
verifyLogService.updateVerifyLog(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除门店核销记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('trade:verify-log:delete')")
public CommonResult<Boolean> deleteVerifyLog(@RequestParam("id") Long id) {
verifyLogService.deleteVerifyLog(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得门店核销记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('trade:verify-log:query')")
public CommonResult<VerifyLogRespVO> getVerifyLog(@RequestParam("id") Long id) {
VerifyLogDO verifyLog = verifyLogService.getVerifyLog(id);
return success(BeanUtils.toBean(verifyLog, VerifyLogRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得门店核销记录分页")
@PreAuthorize("@ss.hasPermission('trade:verify-log:query')")
public CommonResult<PageResult<VerifyLogRespVO>> getVerifyLogPage(@Valid VerifyLogPageReqVO pageReqVO) {
PageResult<VerifyLogRespVO> pageResult = verifyLogService.getVerifyLogPage(pageReqVO);
List<MemberUserRespDTO> users = memberUserApi.getUserList(
convertSet(pageResult.getList(), VerifyLogRespVO::getMebId));
return success(VerifyLogConvert.INSTANCE.convertPage(pageResult, users));
// return success(BeanUtils.toBean(pageResult, VerifyLogRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出门店核销记录 Excel")
@PreAuthorize("@ss.hasPermission('trade:verify-log:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportVerifyLogExcel(@Valid VerifyLogPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<VerifyLogRespVO> list = verifyLogService.getVerifyLogPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "门店核销记录.xls", "数据", VerifyLogRespVO.class,
BeanUtils.toBean(list, VerifyLogRespVO.class));
}
@PutMapping("/updateAlready")
@Operation(summary = "结算核销统计")
@PreAuthorize("@ss.hasPermission('member:distribution-statistics:update')")
public CommonResult<Boolean> updateAlready(@Valid @RequestBody VerifyLogSaveReqVO updateReqVO) {
verifyLogService.updateAlready(updateReqVO);
return success(true);
}
}

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 门店核销记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class VerifyLogPageReqVO extends PageParam {
@Schema(description = "会员id", example = "11234")
private Long mebId;
@Schema(description = "订单id", example = "20689")
private Long orderId;
@Schema(description = "核销编码")
private String pickUpVerifyCode;
@Schema(description = "核销时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] verifyTime;
@Schema(description = "兑换状态", example = "1")
private String status;
@Schema(description = "备注", example = "你猜")
private String remark;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 门店核销记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class VerifyLogRespVO {
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24309")
@ExcelProperty("订单编号")
private Long id;
@Schema(description = "会员id", example = "11234")
@ExcelProperty("会员id")
private Long mebId;
@Schema(description = "订单id", example = "20689")
@ExcelProperty("订单id")
private Long orderId;
@Schema(description = "核销编码")
@ExcelProperty("核销编码")
private String pickUpVerifyCode;
@Schema(description = "核销时间")
@ExcelProperty("核销时间")
private LocalDateTime verifyTime;
@Schema(description = "兑换状态", example = "1")
@ExcelProperty("兑换状态")
private String status;
@Schema(description = "备注", example = "你猜")
@ExcelProperty("备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "用户昵称", example = "2")
@ExcelProperty("用户昵称")
private String nickname;
@Schema(description = "手机号", example = "2")
@ExcelProperty("手机号")
private String phone;
@Schema(description = "已结算", example = "2")
@ExcelProperty("已结算")
private Integer haveAlready;
@Schema(description = "未结算", example = "2")
@ExcelProperty("未结算")
private Integer unAlready;
}

@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 门店核销记录新增/修改 Request VO")
@Data
public class VerifyLogSaveReqVO {
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24309")
private Long id;
@Schema(description = "会员id", example = "11234")
private Long mebId;
@Schema(description = "订单id", example = "20689")
private Long orderId;
@Schema(description = "核销编码")
private String pickUpVerifyCode;
@Schema(description = "核销时间")
private LocalDateTime verifyTime;
@Schema(description = "兑换状态", example = "1")
private String status;
@Schema(description = "备注", example = "你猜")
private String remark;
}

@ -4,8 +4,11 @@ import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*;
import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageRecordConvert;
import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageUserConvert;
@ -54,10 +57,27 @@ public class AppBrokerageUserController {
@Resource
private MemberUserApi memberUserApi;
@Resource
private PayWalletApi payWalletApi;
@GetMapping("/get")
@Operation(summary = "获得个人分销信息")
@Operation(summary = "获得个人钱包信息")
@PreAuthenticated
public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
// Optional<BrokerageUserDO> user = Optional.ofNullable(brokerageUserService.getBrokerageUser(getLoginUserId()));
Optional<PayWalletDTO> user = Optional.ofNullable(payWalletApi.getOrCreateWallet(WebFrameworkUtils.getLoginUserId(), 1));
// 返回数据
AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
.setBrokerageEnabled(true)
.setBrokeragePrice(user.map(PayWalletDTO::getBalance).orElse(0))
.setFrozenPrice(user.map(PayWalletDTO::getFreezePrice).orElse(0));
return success(respVO);
}
@GetMapping("/get1")
@Operation(summary = "获得个人分销信息")
@PreAuthenticated
public CommonResult<AppBrokerageUserRespVO> getBrokerageUser1() {
Optional<BrokerageUserDO> user = Optional.ofNullable(brokerageUserService.getBrokerageUser(getLoginUserId()));
// 返回数据
AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()

@ -47,4 +47,11 @@ public class AppBrokerageWithdrawController {
return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO));
}
@PostMapping("/create1")
@Operation(summary = "创建余额提现")
@PreAuthenticated
public CommonResult<Long> createBrokerageWithdraw1(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) {
return success(brokerageWithdrawService.createBrokerageWithdraw1(getLoginUserId(), createReqVO));
}
}

@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
@ -56,6 +57,22 @@ public interface BrokerageRecordConvert {
.setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime)
.setSourceUserLevel(sourceUserLevel).setSourceUserId(sourceUserId);
}
default BrokerageRecordDO convert1(PayWalletDTO user, BrokerageRecordBizTypeEnum bizType, String bizId,
Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime,
String title, Long sourceUserId, Integer sourceUserLevel) {
brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0);
// 不冻结时,佣金直接就是结算状态
Integer status = brokerageFrozenDays > 0
? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus()
: BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
return new BrokerageRecordDO().setUserId(user.getId())
.setBizType(bizType.getType()).setBizId(bizId)
.setPrice(brokeragePrice).setTotalPrice(user.getBalance())
.setTitle(title)
.setDescription(StrUtil.format(bizType.getDescription(), MoneyUtils.fenToYuanStr(Math.abs(brokeragePrice))))
.setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime)
.setSourceUserLevel(sourceUserLevel).setSourceUserId(sourceUserId);
}
default PageResult<BrokerageRecordRespVO> convertPage(PageResult<BrokerageRecordDO> pageResult, Map<Long, MemberUserRespDTO> userMap) {
PageResult<BrokerageRecordRespVO> result = convertPage(pageResult);

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.trade.convert.verifylog;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo.VerifyLogRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@Mapper
public interface VerifyLogConvert {
VerifyLogConvert INSTANCE = Mappers.getMapper(VerifyLogConvert.class);
default PageResult<VerifyLogRespVO> convertPage(PageResult<VerifyLogRespVO> pageResult, List<MemberUserRespDTO> users) {
PageResult<VerifyLogRespVO> voPageResult = convertPage(pageResult);
// user 拼接
Map<Long, MemberUserRespDTO> userMap = convertMap(users, MemberUserRespDTO::getId);
voPageResult.getList().forEach(record -> {
MapUtils.findAndThen(userMap, record.getMebId(),
memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname()).setPhone(memberUserRespDTO.getMobile()));
});
return voPageResult;
}
PageResult<VerifyLogRespVO> convertPage(PageResult<VerifyLogRespVO> page);
}

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.trade.dal.dataobject.verifylog;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* DO
*
* @author
*/
@TableName("trade_verify_log")
@KeySequence("trade_verify_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VerifyLogDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
* id
*/
private Long mebId;
/**
* id
*/
private Long orderId;
/**
*
*/
private String pickUpVerifyCode;
/**
*
*/
private LocalDateTime verifyTime;
/**
*
*/
private String status;
/**
*
*/
private String remark;
}

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.trade.dal.mysql.verifylog;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo.*;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* Mapper
*
* @author
*/
@Mapper
public interface VerifyLogMapper extends BaseMapperX<VerifyLogDO> {
default PageResult<VerifyLogDO> selectPage(VerifyLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<VerifyLogDO>()
.eqIfPresent(VerifyLogDO::getMebId, reqVO.getMebId())
.eqIfPresent(VerifyLogDO::getOrderId, reqVO.getOrderId())
.eqIfPresent(VerifyLogDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode())
.betweenIfPresent(VerifyLogDO::getVerifyTime, reqVO.getVerifyTime())
.eqIfPresent(VerifyLogDO::getStatus, reqVO.getStatus())
.eqIfPresent(VerifyLogDO::getRemark, reqVO.getRemark())
.betweenIfPresent(VerifyLogDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(VerifyLogDO::getId));
}
@Select({"<script>",
"SELECT\n" +
"\tl.meb_id,\n" +
"\tsum( CASE WHEN l.STATUS = 1 THEN 1 ELSE 0 END ) AS 'have_already',\n" +
"\tsum( CASE WHEN l.STATUS = 0 THEN 1 ELSE 0 END ) AS 'un_already' \n" +
"FROM\n" +
"\ttrade_verify_log l",
"where l.deleted = FALSE ",
// "<if test='status !=null and status!=\"\" '>" ,
// " and l.status = #{status}" ,
// "</if> ",
"GROUP BY\n" +
"\tl.meb_id\n" +
"order BY\n" +
"\tl.meb_id\n" +
"</script>"})
IPage<VerifyLogRespVO> selectPage1(IPage<?> page, @Param("reqVO") VerifyLogPageReqVO reqVO);
}

@ -61,6 +61,16 @@ public interface BrokerageRecordService {
* @param title
*/
void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title);
/**
*
*
* @param userId
* @param bizType
* @param bizId
* @param brokeragePrice
* @param title
*/
void addBrokerage1(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title);
/**
*
@ -74,6 +84,18 @@ public interface BrokerageRecordService {
default void reduceBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
addBrokerage(userId, bizType, bizId, -brokeragePrice, title);
}
/**
*
*
* @param userId
* @param bizType
* @param bizId
* @param brokeragePrice
* @param title
*/
default void reduceBrokerage1(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
addBrokerage1(userId, bizType, bizId, -brokeragePrice, title);
}
/**
*

@ -7,6 +7,9 @@ import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletTransactionDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@ -39,6 +42,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMinValue;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.WITHDRAW;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH;
/**
@ -63,6 +67,9 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
@Resource
private ProductSkuApi productSkuApi;
@Resource
private PayWalletApi payWalletApi;
@Override
public BrokerageRecordDO getBrokerageRecord(Integer id) {
return brokerageRecordMapper.selectById(id);
@ -314,6 +321,31 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
null, title, null, null);
brokerageRecordMapper.insert(record);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addBrokerage1(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
// 1. 校验佣金余额
PayWalletDTO wallet = payWalletApi.getOrCreateWallet(userId, 1);
// BrokerageUserDO user = brokerageUserService.getBrokerageUser(userId);
int balance = Optional.of(wallet)
.map(PayWalletDTO::getBalance).orElse(0);
if (balance + brokeragePrice < 0) {
throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
}
PayWalletTransactionDTO payWalletTransactionDTO = payWalletApi.reduceWalletBalance1(userId, Long.valueOf(bizId), WITHDRAW, -brokeragePrice);
// 2. 更新佣金余额
// boolean success = brokerageUserService.updateUserPrice(userId, brokeragePrice);
if (ObjectUtil.isEmpty(payWalletTransactionDTO)) {
// 失败时,则抛出异常。只会出现扣减佣金时,余额不足的情况
throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
}
// 3. 新增记录
// BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert1(wallet, bizType, bizId, 0, brokeragePrice,
// null, title, null, null);
// brokerageRecordMapper.insert(record);
}
@Override
public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId) {

@ -54,6 +54,14 @@ public interface BrokerageWithdrawService {
* @return
*/
Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO);
/**
*
*
* @param userId
* @param createReqVO
* @return
*/
Long createBrokerageWithdraw1(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO);
/**
* userId

@ -6,6 +6,9 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
@ -34,6 +37,7 @@ import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.RECHARGE;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
/**
@ -56,6 +60,9 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
@Resource
private NotifyMessageSendApi notifyMessageSendApi;
@Resource
private PayWalletApi payWalletApi;
@Resource
private Validator validator;
@ -128,19 +135,54 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
TradeConfigDO tradeConfig = validateWithdrawPrice(createReqVO.getPrice());
// 1.2 校验提现参数
createReqVO.validate(validator);
Integer brokerageWithdrawFeePercent = 0;
if (!BrokerageWithdrawTypeEnum.WALLET.getType().equals(createReqVO.getType())) {//提现到钱包
brokerageWithdrawFeePercent = tradeConfig.getBrokerageWithdrawFeePercent();
}
//处理提现逻辑
// 2.1 计算手续费
Integer feePrice = calculateFeePrice(createReqVO.getPrice(), tradeConfig.getBrokerageWithdrawFeePercent());
Integer feePrice = calculateFeePrice(createReqVO.getPrice(), brokerageWithdrawFeePercent);
// 2.2 创建佣金提现记录
BrokerageWithdrawDO withdraw = BrokerageWithdrawConvert.INSTANCE.convert(createReqVO, userId, feePrice);
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(createReqVO.getType())) {//提现到钱包
withdraw.setStatus(11);
withdraw.setAuditTime(LocalDateTime.now());
}
brokerageWithdrawMapper.insert(withdraw);
if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(createReqVO.getType())) {//提现到钱包处理提现到零钱逻辑
PayWalletDTO orCreateWallet = payWalletApi.getOrCreateWallet(userId, 1);
// PayWalletBizTypeEnum.RECHARGE.getType() bizType = new PayWalletBizTypeEnum();
payWalletApi.addWalletBalance(orCreateWallet.getId(),withdraw.getId()+"",RECHARGE,withdraw.getPrice());
}
// 3. 创建用户佣金记录
// 注意佣金是否充足reduceBrokerage 已经进行校验
brokerageRecordService.reduceBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()),
createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
return withdraw.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long createBrokerageWithdraw1(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO) {
// 1.1 校验提现金额
TradeConfigDO tradeConfig = validateWithdrawPrice(createReqVO.getPrice());
// 1.2 校验提现参数
createReqVO.validate(validator);
//处理提现逻辑
// 2.1 计算手续费
Integer feePrice = calculateFeePrice(createReqVO.getPrice(), tradeConfig.getBrokerageWithdrawFeePercent());
// 2.2 创建佣金提现记录
BrokerageWithdrawDO withdraw = BrokerageWithdrawConvert.INSTANCE.convert(createReqVO, userId, feePrice);
brokerageWithdrawMapper.insert(withdraw);
// 3. 创建用户佣金记录
// 注意佣金是否充足reduceBrokerage 已经进行校验
// 3. 创建用户佣金记录
// 注意佣金是否充足reduceBrokerage 已经进行校验
brokerageRecordService.reduceBrokerage1(userId, BrokerageRecordBizTypeEnum.WITHDRAWWEALLET, String.valueOf(withdraw.getId()),
createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle());
// payWalletApi.reduceWalletBalance1(userId,withdraw.getId(), PayWalletBizTypeEnum.WITHDRAW,
// createReqVO.getPrice());
return withdraw.getId();
}
@Override
public List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,

@ -36,9 +36,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageRecordMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.verifylog.VerifyLogMapper;
import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
@ -120,6 +122,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Resource
private BrokerageRecordMapper brokerageRecordMapper;
@Resource
private VerifyLogMapper verifyLogMapper;
// =================== Order ===================
@ -694,14 +699,30 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
public void pickUpOrderByAdmin(Long id, String pickUpVerifyCode) {
boolean b = brokerageUserService.updateUserPriceByBindUserId(id, 3000);
// 3. 新增记录
BrokerageUserDO user = brokerageUserService.getBrokerageUser(id);
BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert(user, BrokerageRecordBizTypeEnum.ORDER, "", 0, 3000,
null, "门店核销佣金", null, null);
brokerageRecordMapper.insert(record);
getSelf().pickUpOrder(tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode));
}
TradeOrderDO tradeOrderDO = tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode);
if(ObjectUtil.isEmpty(tradeOrderDO)){
throw exception(ORDER_NOT_FOUND);
}
VerifyLogDO verifyLogDO = new VerifyLogDO();
verifyLogDO.setMebId(id);
verifyLogDO.setOrderId(tradeOrderDO.getId());
verifyLogDO.setPickUpVerifyCode(pickUpVerifyCode);
verifyLogDO.setVerifyTime(LocalDateTime.now());
verifyLogDO.setStatus("0");
verifyLogMapper.insert(verifyLogDO);
getSelf().pickUpOrder(tradeOrderDO);
}
// @Override
// @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
// public void pickUpOrderByAdmin(Long id, String pickUpVerifyCode) {
// boolean b = brokerageUserService.updateUserPriceByBindUserId(id, 3000);
// // 3. 新增记录
// BrokerageUserDO user = brokerageUserService.getBrokerageUser(id);
// BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert(user, BrokerageRecordBizTypeEnum.ORDER, "", 0, 3000,
// null, "门店核销佣金", null, null);
// brokerageRecordMapper.insert(record);
// getSelf().pickUpOrder(tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode));
// }
@Override
public TradeOrderDO getByPickUpVerifyCode(String pickUpVerifyCode) {

@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.trade.service.verifylog;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo.*;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* Service
*
* @author
*/
public interface VerifyLogService {
/**
*
*
* @param createReqVO
* @return
*/
Long createVerifyLog(@Valid VerifyLogSaveReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateVerifyLog(@Valid VerifyLogSaveReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteVerifyLog(Long id);
/**
*
*
* @param id
* @return
*/
VerifyLogDO getVerifyLog(Long id);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<VerifyLogRespVO> getVerifyLogPage(VerifyLogPageReqVO pageReqVO);
/**
*
*
* @param updateReqVO
*/
void updateAlready(@Valid VerifyLogSaveReqVO updateReqVO);
}

@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.trade.service.verifylog;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.trade.controller.admin.verifylog.vo.*;
import cn.iocoder.yudao.module.trade.dal.dataobject.verifylog.VerifyLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.trade.dal.mysql.verifylog.VerifyLogMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author
*/
@Service
@Validated
public class VerifyLogServiceImpl implements VerifyLogService {
@Resource
private VerifyLogMapper verifyLogMapper;
@Override
public Long createVerifyLog(VerifyLogSaveReqVO createReqVO) {
// 插入
VerifyLogDO verifyLog = BeanUtils.toBean(createReqVO, VerifyLogDO.class);
verifyLogMapper.insert(verifyLog);
// 返回
return verifyLog.getId();
}
@Override
public void updateVerifyLog(VerifyLogSaveReqVO updateReqVO) {
// 校验存在
validateVerifyLogExists(updateReqVO.getId());
// 更新
VerifyLogDO updateObj = BeanUtils.toBean(updateReqVO, VerifyLogDO.class);
verifyLogMapper.updateById(updateObj);
}
@Override
public void deleteVerifyLog(Long id) {
// 校验存在
validateVerifyLogExists(id);
// 删除
verifyLogMapper.deleteById(id);
}
private void validateVerifyLogExists(Long id) {
if (verifyLogMapper.selectById(id) == null) {
throw exception(VERIFY_LOG_NOT_EXISTS);
}
}
@Override
public VerifyLogDO getVerifyLog(Long id) {
return verifyLogMapper.selectById(id);
}
@Override
public PageResult<VerifyLogRespVO> getVerifyLogPage(VerifyLogPageReqVO pageReqVO) {
IPage<VerifyLogRespVO> pageResult = verifyLogMapper.selectPage1(MyBatisUtils.buildPage(pageReqVO),pageReqVO);
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
}
@Override
public void updateAlready(VerifyLogSaveReqVO updateReqVO) {
// 结算
verifyLogMapper.update(new UpdateWrapper<VerifyLogDO>().lambda().set(VerifyLogDO::getStatus,1).eq(VerifyLogDO::getMebId,updateReqVO.getMebId()));
}
}

@ -90,7 +90,6 @@ public class DistributionStatisticsServiceImpl implements DistributionStatistics
MyBatisUtils.buildPage(pageReqVO),
pageReqVO.getStatus());
return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
// return distributionStatisticsMapper.selectPage1(pageReqVO);
}
}

@ -464,12 +464,32 @@ public class MemberUserServiceImpl implements MemberUserService {
DistributionLogDO vo = distributionLogDOS.get((distributionLogDOS.size()/5)-1);
vo.setGameStatus("0");
distributionLogMapper.updateById(vo);
// List<DistributionLogDO> distributionLogDOS1 = distributionLogDOS.subList(0, (distributionLogDOS.size() / 5));
// for (DistributionLogDO dt:distributionLogDOS1) {
// dt.setGameStatus("0");
// }
// distributionLogMapper.updateBatch(distributionLogDOS1);
// distributionLogMapper.up
//添加佣金记录
BrokerageRecordDO brokerageRecordDO = new BrokerageRecordDO();
brokerageRecordDO.setBizId(orderId+"");
brokerageRecordDO.setUserId(vo.getMebId());
brokerageRecordDO.setBizType(1);
brokerageRecordDO.setTitle("5占一游戏分润");
brokerageRecordDO.setPrice(1188*100);
//查询当前总佣金
Integer totalPrice = 0;
List<BrokerageRecordDO> brokerageRecordDOS = brokerageRecordMapper.selectList(new LambdaQueryWrapperX<BrokerageRecordDO>()
.eq(BrokerageRecordDO::getUserId, vo.getMebId())
.eq(BrokerageRecordDO::getDeleted, 0)
.orderByDesc(BrokerageRecordDO::getCreateTime));
if(ObjectUtil.isNotEmpty(brokerageRecordDOS)){
totalPrice = brokerageRecordDOS.get(0).getTotalPrice();
}
brokerageRecordDO.setTotalPrice(totalPrice+1188*100);//当前总佣金
brokerageRecordDO.setStatus(1);
brokerageRecordDO.setSourceUserLevel(configLevelDO.getLevel());
brokerageRecordDO.setSourceUserId(userId);
brokerageRecordMapper.insert(brokerageRecordDO);
//修改推广员佣金
BrokerageUserDO brokerageUserDO1 = brokerageUserMapper.selectById(vo.getMebId());
brokerageUserDO1.setBrokeragePrice(1188*100+brokerageUserDO1.getBrokeragePrice());
brokerageUserMapper.updateById(brokerageUserDO1);
}
}
}

@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.pay.api.wallet;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletTransactionDTO;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
/**
* Service
*
* @author jason
*/
public interface PayWalletApi {
/**
*
* <p>
*
*
* @param userId
* @param userType
*/
PayWalletDTO getOrCreateWallet(Long userId, Integer userType);
/**
*
*
* @param walletId id
*/
PayWalletDTO getWallet(Long walletId);
/**
*
*
* @param walletId id
* @param bizId id
* @param bizType
* @param price
* @return
*/
PayWalletTransactionDTO reduceWalletBalance(Long walletId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price);
/**
*
*
* @param userId id
* @param bizId id
* @param bizType
* @param price
* @return
*/
PayWalletTransactionDTO reduceWalletBalance1(Long userId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price);
/**
*
*
* @param walletId id
* @param bizId id
* @param bizType
* @param price
* @return
*/
PayWalletTransactionDTO addWalletBalance(Long walletId, String bizId,
PayWalletBizTypeEnum bizType, Integer price);
/**
*
*
* @param id
* @param price
*/
void freezePrice(Long id, Integer price);
/**
*
*
* @param id
* @param price
*/
void unfreezePrice(Long id, Integer price);
}

@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.pay.api.wallet.dto;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import lombok.Data;
import java.time.LocalDateTime;
/**
* DO
*
* @author jason
*/
@Data
public class PayWalletDTO {
/**
*
*/
private Long id;
/**
* id
*
* MemberUserDO id
* AdminUserDO id
*/
private Long userId;
/**
* ,
*
* {@link UserTypeEnum}
*/
private Integer userType;
/**
*
*/
private Integer balance;
/**
*
*/
private Integer freezePrice;
/**
*
*/
private Integer totalExpense;
/**
*
*/
private Integer totalRecharge;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
/**
* 使 SysUser id
*
* 使 String
*/
private String creator;
/**
* 使 SysUser id
*
* 使 String
*/
private String updater;
/**
*
*/
private Boolean deleted;
}

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.pay.api.wallet.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Data
public class PayWalletRespDTO {
private Long id;
private LocalDateTime createTime;
private String nickname;
private String avatar;
@NotNull(message = "用户编号不能为空")
private Long userId;
@NotNull(message = "用户类型不能为空")
private Integer userType;
@NotNull(message = "余额,单位分不能为空")
private Integer balance;
@NotNull(message = "累计支出,单位分不能为空")
private Integer totalExpense;
@NotNull(message = "累计充值,单位分不能为空")
private Integer totalRecharge;
@NotNull(message = "冻结金额,单位分不能为空")
private Integer freezePrice;
}

@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.pay.api.wallet.dto;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import lombok.Data;
import java.time.LocalDateTime;
/**
* DO
*
* @author jason
*/
@Data
public class PayWalletTransactionDTO {
/**
*
*/
private Long id;
/**
*
*/
private String no;
/**
*
*
*/
private Long walletId;
/**
*
*
* {@link PayWalletBizTypeEnum#getType()}
*/
private Integer bizType;
/**
*
*/
private String bizId;
/**
*
*/
private String title;
/**
*
*
*
*/
private Integer price;
/**
*
*/
private Integer balance;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
/**
* 使 SysUser id
*
* 使 String
*/
private String creator;
/**
* 使 SysUser id
*
* 使 String
*/
private String updater;
/**
*
*/
private Boolean deleted;
}

@ -18,7 +18,11 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable {
RECHARGE(1, "充值"),
RECHARGE_REFUND(2, "充值退款"),
PAYMENT(3, "支付"),
PAYMENT_REFUND(4, "支付退款");
PAYMENT_REFUND(4, "支付退款"),
WITHDRAW(5, "提现申请"),
WITHDRAW_REJECT(6, "提现申请驳回");
// TODO 后续增加

@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.pay.api.wallet;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.api.distributionconfig.vo.DistributionConfigRespDTO;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletDTO;
import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletTransactionDTO;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
import cn.iocoder.yudao.module.pay.service.wallet.PayWalletTransactionService;
import cn.iocoder.yudao.module.pay.service.wallet.bo.WalletTransactionCreateReqBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT_REFUND;
/**
* Service
*
* @author jason
*/
@Service
@Slf4j
public class PayWalletApiImpl implements PayWalletApi {
@Resource
@Lazy // 延迟加载,避免循环依赖
private PayWalletService payWalletService;
@Override
public PayWalletDTO getOrCreateWallet(Long userId, Integer userType) {
return BeanUtils.toBean(payWalletService.getOrCreateWallet(userId,userType), PayWalletDTO.class);
}
@Override
public PayWalletDTO getWallet(Long walletId) {
return BeanUtils.toBean(payWalletService.getWallet(walletId), PayWalletDTO.class);
}
@Override
public PayWalletTransactionDTO reduceWalletBalance(Long walletId, Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
return BeanUtils.toBean(payWalletService.reduceWalletBalance(walletId,bizId,bizType,price), PayWalletTransactionDTO.class);
}
@Override
public PayWalletTransactionDTO reduceWalletBalance1(Long userId, Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
return BeanUtils.toBean(payWalletService.reduceWalletBalance1(userId,bizId,bizType,price), PayWalletTransactionDTO.class);
}
@Override
public PayWalletTransactionDTO addWalletBalance(Long walletId, String bizId, PayWalletBizTypeEnum bizType, Integer price) {
return BeanUtils.toBean(payWalletService.addWalletBalance(walletId,bizId,bizType,price), PayWalletTransactionDTO.class);
}
@Override
public void freezePrice(Long id, Integer price) {
payWalletService.freezePrice(id,price);
}
@Override
public void unfreezePrice(Long id, Integer price) {
payWalletService.unfreezePrice(id,price);
}
}

@ -68,6 +68,17 @@ public interface PayWalletService {
*/
PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price);
/**
*
*
* @param walletId id
* @param bizId id
* @param bizType
* @param price
* @return
*/
PayWalletTransactionDO reduceWalletBalance1(Long userId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price);
/**
*

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.service.wallet;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
@ -12,6 +13,9 @@ import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import cn.iocoder.yudao.module.pay.service.wallet.bo.WalletTransactionCreateReqBO;
import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageRecordConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -19,11 +23,13 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Optional;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT;
import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT_REFUND;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH;
/**
* Service
@ -156,6 +162,57 @@ public class PayWalletServiceImpl implements PayWalletService {
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
return walletTransactionService.createWalletTransaction(bo);
}
@Override
public PayWalletTransactionDO reduceWalletBalance1(Long userId, Long bizId,
PayWalletBizTypeEnum bizType, Integer price) {
// 1. 获取钱包
PayWalletDO payWallet = getOrCreateWallet(userId,1);
if (payWallet == null) {
log.error("[reduceWalletBalance],用户钱包({})不存在.", userId);
throw exception(WALLET_NOT_FOUND);
}
// 1. 校验佣金余额
int balance = Optional.of(payWallet)
.map(PayWalletDO::getBalance).orElse(0);
if (balance - price < 0) {
throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
}
// 2.1 扣除余额
int updateCounts;
switch (bizType) {
case WITHDRAW: {
updateCounts = walletMapper.updateWhenConsumption(payWallet.getId(), price);
if (updateCounts<1) {
// 失败时,则抛出异常。只会出现扣减佣金时,余额不足的情况
throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
}
break;
}
// case RECHARGE_REFUND: {
// updateCounts = walletMapper.updateWhenRechargeRefund(payWallet.getId(), price);
// break;
// }
default: {
// TODO 其它类型待实现
throw new UnsupportedOperationException("待实现");
}
}
if (updateCounts == 0) {
throw exception(WALLET_BALANCE_NOT_ENOUGH);
}
// 2.2 生成钱包流水
Integer afterBalance = payWallet.getBalance() - price;
WalletTransactionCreateReqBO bo = new WalletTransactionCreateReqBO().setWalletId(payWallet.getId())
.setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
// 3. 新增记录
// BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert(user, bizType, bizId, 0, brokeragePrice,
// null, title, null, null);
// brokerageRecordMapper.insert(record);
return walletTransactionService.createWalletTransaction(bo);
}
@Override
public PayWalletTransactionDO addWalletBalance(Long walletId, String bizId,

@ -4,18 +4,18 @@ NODE_ENV=production
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://localhost:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server
# 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
# VITE_BASE_URL='http://zd.huamar.com'
# VITE_BASE_URL='http://localhost:48080'
# # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
# VITE_UPLOAD_TYPE=server
# # 上传路径
# VITE_UPLOAD_URL='http://zd.huamar.com/admin-api/infra/file/upload'
# VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
VITE_BASE_URL='http://zd.huamar.com'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server
# 上传路径
VITE_UPLOAD_URL='http://zd.huamar.com/admin-api/infra/file/upload'
# 接口地址
VITE_API_URL=/admin-api

@ -0,0 +1,49 @@
import request from '@/config/axios'
// 门店核销记录 VO
export interface VerifyLogVO {
id: number // 订单编号
mebId: number // 会员id
orderId: number // 订单id
pickUpVerifyCode: string // 核销编码
verifyTime: Date // 核销时间
status: string // 兑换状态
remark: string // 备注
}
// 门店核销记录 API
export const VerifyLogApi = {
// 查询门店核销记录分页
getVerifyLogPage: async (params: any) => {
return await request.get({ url: `/trade/verify-log/page`, params })
},
// 查询门店核销记录详情
getVerifyLog: async (id: number) => {
return await request.get({ url: `/trade/verify-log/get?id=` + id })
},
// 新增门店核销记录
createVerifyLog: async (data: VerifyLogVO) => {
return await request.post({ url: `/trade/verify-log/create`, data })
},
// 修改门店核销记录
updateVerifyLog: async (data: VerifyLogVO) => {
return await request.put({ url: `/trade/verify-log/update`, data })
},
// 删除门店核销记录
deleteVerifyLog: async (id: number) => {
return await request.delete({ url: `/trade/verify-log/delete?id=` + id })
},
// 导出门店核销记录 Excel
exportVerifyLog: async (params) => {
return await request.download({ url: `/trade/verify-log/export-excel`, params })
},
// 结算核销统计
updateAlready: async (data: VerifyLogVO) => {
return await request.put({ url: `/trade/verify-log/updateAlready`, data })
},
}

@ -0,0 +1,42 @@
<template>
<div class="flex flex-col gap-2 bg-[var(--el-bg-color-overlay)] p-6">
<div class="flex items-center justify-between text-gray-500">
<span>{{ title }}</span>
<el-tag>{{ tag }}</el-tag>
</div>
<div class="flex flex-row items-baseline justify-between">
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" class="text-3xl" />
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
{{ Math.abs(toNumber(percent)) }}%
<Icon :icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'" class="!text-sm" />
</span>
</div>
<el-divider class="mb-1! mt-2!" />
<div class="flex flex-row items-center justify-between text-sm">
<span class="text-gray-500">昨日数据</span>
<span>{{ prefix || '' }}{{ reference }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { toNumber } from 'lodash-es'
import { calculateRelativeRate } from '@/utils'
/** 交易对照卡片 */
defineOptions({ name: 'ComparisonCard' })
const props = defineProps({
title: propTypes.string.def('').isRequired,
tag: propTypes.string.def(''),
prefix: propTypes.string.def(''),
value: propTypes.number.def(0).isRequired,
reference: propTypes.number.def(0).isRequired,
decimals: propTypes.number.def(0)
})
//
const percent = computed(() =>
calculateRelativeRate(props.value as number, props.reference as number)
)
</script>

@ -0,0 +1,91 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="用户统计" />
</template>
<!-- 折线图 -->
<Echart :height="300" :options="lineChartOptions" />
</el-card>
</template>
<script lang="ts" setup>
import dayjs from 'dayjs'
import { EChartsOption } from 'echarts'
import * as MemberStatisticsApi from '@/api/mall/statistics/member'
import { formatDate } from '@/utils/formatTime'
import { CardTitle } from '@/components/Card'
/** 会员用户统计卡片 */
defineOptions({ name: 'MemberStatisticsCard' })
const loading = ref(true) //
/** 折线图配置 */
const lineChartOptions = reactive<EChartsOption>({
dataset: {
dimensions: ['date', 'count'],
source: []
},
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
legend: {
top: 50
},
series: [{ name: '注册量', type: 'line', smooth: true, areaStyle: {} }],
toolbox: {
feature: {
//
dataZoom: {
yAxisIndex: false // Y
},
brush: {
type: ['lineX', 'clear'] //
},
saveAsImage: { show: true, name: '会员统计' } //
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
xAxis: {
type: 'category',
boundaryGap: false,
axisTick: {
show: false
},
axisLabel: {
formatter: (date: string) => formatDate(date, 'MM-DD')
}
},
yAxis: {
axisTick: {
show: false
}
}
}) as EChartsOption
const getMemberRegisterCountList = async () => {
loading.value = true
//
const beginTime = dayjs().subtract(30, 'd').startOf('d')
const endTime = dayjs().endOf('d')
const list = await MemberStatisticsApi.getMemberRegisterCountList(beginTime, endTime)
// Echarts
if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
lineChartOptions.dataset['source'] = list
}
loading.value = false
}
/** 初始化 **/
onMounted(() => {
getMemberRegisterCountList()
})
</script>

@ -0,0 +1,92 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="运营数据" />
</template>
<div class="flex flex-row flex-wrap items-center gap-8 p-4">
<div
v-for="item in data"
:key="item.name"
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
@click="handleClick(item.routerName)"
>
<CountTo
:prefix="item.prefix"
:end-val="item.value"
:decimals="item.decimals"
class="text-3xl"
/>
<span class="text-center">{{ item.name }}</span>
</div>
</div>
</el-card>
</template>
<script lang="ts" setup>
import * as ProductSpuApi from '@/api/mall/product/spu'
import * as TradeStatisticsApi from '@/api/mall/statistics/trade'
import * as PayStatisticsApi from '@/api/mall/statistics/pay'
import { CardTitle } from '@/components/Card'
/** 运营数据卡片 */
defineOptions({ name: 'OperationDataCard' })
const router = useRouter() //
/** 数据 */
const data = reactive({
orderUndelivered: { name: '待发货订单', value: 9, routerName: 'TradeOrder' },
orderAfterSaleApply: { name: '退款中订单', value: 4, routerName: 'TradeAfterSale' },
orderWaitePickUp: { name: '待核销订单', value: 0, routerName: 'TradeOrder' },
productAlertStock: { name: '库存预警', value: 0, routerName: 'ProductSpu' },
productForSale: { name: '上架商品', value: 0, routerName: 'ProductSpu' },
productInWarehouse: { name: '仓库商品', value: 0, routerName: 'ProductSpu' },
withdrawAuditing: { name: '提现待审核', value: 0, routerName: 'TradeBrokerageWithdraw' },
rechargePrice: {
name: '账户充值',
value: 0.0,
prefix: '¥',
decimals: 2,
routerName: 'PayWalletRecharge'
}
})
/** 查询订单数据 */
const getOrderData = async () => {
const orderCount = await TradeStatisticsApi.getOrderCount()
data.orderUndelivered.value = orderCount.undelivered
data.orderAfterSaleApply.value = orderCount.afterSaleApply
data.orderWaitePickUp.value = orderCount.pickUp
data.withdrawAuditing.value = orderCount.auditingWithdraw
}
/** 查询商品数据 */
const getProductData = async () => {
// TODO: @
const productCount = await ProductSpuApi.getTabsCount()
data.productForSale.value = productCount['0']
data.productInWarehouse.value = productCount['1']
data.productAlertStock.value = productCount['3']
}
/** 查询钱包充值数据 */
const getWalletRechargeData = async () => {
const paySummary = await PayStatisticsApi.getWalletRechargePrice()
data.rechargePrice.value = paySummary.rechargePrice
}
/**
* 跳转到对应页面
*
* @param routerName 路由页面组件的名称
*/
const handleClick = (routerName: string) => {
router.push({ name: routerName })
}
/** 初始化 **/
onMounted(() => {
getOrderData()
getProductData()
getWalletRechargeData()
})
</script>

@ -0,0 +1,82 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="快捷入口" />
</template>
<div class="flex flex-row flex-wrap gap-8 p-4">
<div
v-for="menu in menuList"
:key="menu.name"
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
@click="handleMenuClick(menu.routerName)"
>
<div
:class="menu.bgColor"
class="h-48px w-48px flex items-center justify-center rounded text-white"
>
<Icon :icon="menu.icon" class="text-7.5!" />
</div>
<span>{{ menu.name }}</span>
</div>
</div>
</el-card>
</template>
<script lang="ts" setup>
/** 快捷入口卡片 */
import { CardTitle } from '@/components/Card'
defineOptions({ name: 'ShortcutCard' })
const router = useRouter() //
/** 菜单列表 */
const menuList = [
{ name: '用户管理', icon: 'ep:user-filled', bgColor: 'bg-red-400', routerName: 'MemberUser' },
{
name: '商品管理',
icon: 'fluent-mdl2:product',
bgColor: 'bg-orange-400',
routerName: 'ProductSpu'
},
{ name: '订单管理', icon: 'ep:list', bgColor: 'bg-yellow-500', routerName: 'TradeOrder' },
{
name: '售后管理',
icon: 'ri:refund-2-line',
bgColor: 'bg-green-600',
routerName: 'TradeAfterSale'
},
{
name: '分销管理',
icon: 'fa-solid:project-diagram',
bgColor: 'bg-cyan-500',
routerName: 'TradeBrokerageUser'
},
{
name: '优惠券',
icon: 'ep:ticket',
bgColor: 'bg-blue-500',
routerName: 'PromotionCoupon'
},
{
name: '拼团活动',
icon: 'fa:group',
bgColor: 'bg-purple-500',
routerName: 'PromotionBargainActivity'
},
{
name: '佣金提现',
icon: 'vaadin:money-withdraw',
bgColor: 'bg-rose-500',
routerName: 'TradeBrokerageWithdraw'
}
]
/**
* 跳转到菜单对应页面
*
* @param routerName 路由页面组件的名称
*/
const handleMenuClick = (routerName: string) => {
router.push({ name: routerName })
}
</script>

@ -0,0 +1,208 @@
<template>
<el-card shadow="never">
<template #header>
<div class="flex flex-row items-center justify-between">
<CardTitle title="交易量趋势" />
<!-- 查询条件 -->
<div class="flex flex-row items-center gap-2">
<el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange">
<el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :label="key">
{{ value.name }}
</el-radio-button>
</el-radio-group>
</div>
</div>
</template>
<!-- 折线图 -->
<Echart :height="300" :options="eChartOptions" />
</el-card>
</template>
<script lang="ts" setup>
import dayjs, { Dayjs } from 'dayjs'
import { EChartsOption } from 'echarts'
import * as TradeStatisticsApi from '@/api/mall/statistics/trade'
import { fenToYuan } from '@/utils'
import { formatDate } from '@/utils/formatTime'
import { CardTitle } from '@/components/Card'
/** 交易量趋势 */
defineOptions({ name: 'TradeTrendCard' })
enum TimeRangeTypeEnum {
DAY30 = 1,
WEEK = 7,
MONTH = 30,
YEAR = 365
} //
const timeRangeType = ref(TimeRangeTypeEnum.DAY30) // , 30
const loading = ref(true) //
// Map
const timeRange = new Map()
.set(TimeRangeTypeEnum.DAY30, {
name: '30天',
series: [
{ name: '订单金额', type: 'bar', smooth: true, data: [] },
{ name: '订单数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.WEEK, {
name: '周',
series: [
{ name: '上周金额', type: 'bar', smooth: true, data: [] },
{ name: '本周金额', type: 'bar', smooth: true, data: [] },
{ name: '上周数量', type: 'line', smooth: true, data: [] },
{ name: '本周数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.MONTH, {
name: '月',
series: [
{ name: '上月金额', type: 'bar', smooth: true, data: [] },
{ name: '本月金额', type: 'bar', smooth: true, data: [] },
{ name: '上月数量', type: 'line', smooth: true, data: [] },
{ name: '本月数量', type: 'line', smooth: true, data: [] }
]
})
.set(TimeRangeTypeEnum.YEAR, {
name: '年',
series: [
{ name: '去年金额', type: 'bar', smooth: true, data: [] },
{ name: '今年金额', type: 'bar', smooth: true, data: [] },
{ name: '去年数量', type: 'line', smooth: true, data: [] },
{ name: '今年数量', type: 'line', smooth: true, data: [] }
]
})
/** 图表配置 */
const eChartOptions = reactive<EChartsOption>({
grid: {
left: 20,
right: 20,
bottom: 20,
top: 80,
containLabel: true
},
legend: {
top: 50,
data: []
},
series: [],
toolbox: {
feature: {
//
dataZoom: {
yAxisIndex: false // Y
},
brush: {
type: ['lineX', 'clear'] //
},
saveAsImage: { show: true, name: '订单量趋势' } //
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
xAxis: {
type: 'category',
inverse: true,
boundaryGap: false,
axisTick: {
show: false
},
data: [],
axisLabel: {
formatter: (date: string) => {
switch (timeRangeType.value) {
case TimeRangeTypeEnum.DAY30:
return formatDate(date, 'MM-DD')
case TimeRangeTypeEnum.WEEK:
let weekDay = formatDate(date, 'ddd')
if (weekDay == '0') weekDay = '日'
return '周' + weekDay
case TimeRangeTypeEnum.MONTH:
return formatDate(date, 'D')
case TimeRangeTypeEnum.YEAR:
return formatDate(date, 'M') + '月'
default:
return date
}
}
}
},
yAxis: {
axisTick: {
show: false
}
}
}) as EChartsOption
/** 时间范围类型单选按钮选中 */
const handleTimeRangeTypeChange = async () => {
//
let beginTime: Dayjs
let endTime: Dayjs
switch (timeRangeType.value) {
case TimeRangeTypeEnum.WEEK:
beginTime = dayjs().startOf('week')
endTime = dayjs().endOf('week')
break
case TimeRangeTypeEnum.MONTH:
beginTime = dayjs().startOf('month')
endTime = dayjs().endOf('month')
break
case TimeRangeTypeEnum.YEAR:
beginTime = dayjs().startOf('year')
endTime = dayjs().endOf('year')
break
case TimeRangeTypeEnum.DAY30:
default:
beginTime = dayjs().subtract(30, 'day').startOf('d')
endTime = dayjs().endOf('d')
break
}
//
await getOrderCountTrendComparison(beginTime, endTime)
}
/** 查询订单数量趋势对照数据 */
const getOrderCountTrendComparison = async (
beginTime: dayjs.ConfigType,
endTime: dayjs.ConfigType
) => {
loading.value = true
//
const list = await TradeStatisticsApi.getOrderCountTrendComparison(
timeRangeType.value,
beginTime,
endTime
)
//
const dates: string[] = []
const series = [...timeRange.get(timeRangeType.value).series]
for (let item of list) {
dates.push(item.value.date)
if (series.length === 2) {
series[0].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) //
series[1].data.push(item?.value?.orderPayCount || 0) //
} else {
series[0].data.push(fenToYuan(item?.reference?.orderPayPrice || 0)) //
series[1].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) //
series[2].data.push(item?.reference?.orderPayCount || 0) //
series[3].data.push(item?.value?.orderPayCount || 0) //
}
}
eChartOptions.xAxis!['data'] = dates
eChartOptions.series = series
// legend424
eChartOptions.legend['data'] = series.map((item) => item.name)
loading.value = false
}
/** 初始化 **/
onMounted(() => {
handleTimeRangeTypeChange()
})
</script>

@ -73,7 +73,7 @@
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button @click="handlePickup" type="success" plain v-hasPermi="['trade:order:pick-up']">
<el-button @click="handlePickup" type="success" plain>
<Icon class="mr-5px" icon="ep:check" />
核销
</el-button>

@ -38,7 +38,6 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" align="center" prop="id" />
<el-table-column label="用户名称" align="center" prop="nickname" />
<el-table-column label="真实姓名" align="center" prop="userName" />
<el-table-column label="手机号" align="center" prop="phone" />

@ -0,0 +1,123 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="会员id" prop="mebId">
<el-input v-model="formData.mebId" placeholder="请输入会员id" />
</el-form-item>
<el-form-item label="订单id" prop="orderId">
<el-input v-model="formData.orderId" placeholder="请输入订单id" />
</el-form-item>
<el-form-item label="核销编码" prop="pickUpVerifyCode">
<el-input v-model="formData.pickUpVerifyCode" placeholder="请输入核销编码" />
</el-form-item>
<el-form-item label="核销时间" prop="verifyTime">
<el-date-picker
v-model="formData.verifyTime"
type="date"
value-format="x"
placeholder="选择核销时间"
/>
</el-form-item>
<el-form-item label="兑换状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { VerifyLogApi, VerifyLogVO } from '@/api/trade/verifylog'
/** 门店核销记录 表单 */
defineOptions({ name: 'VerifyLogForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
mebId: undefined,
orderId: undefined,
pickUpVerifyCode: undefined,
verifyTime: undefined,
status: undefined,
remark: undefined,
})
const formRules = reactive({
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await VerifyLogApi.getVerifyLog(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as VerifyLogVO
if (formType.value === 'create') {
await VerifyLogApi.createVerifyLog(data)
message.success(t('common.createSuccess'))
} else {
await VerifyLogApi.updateVerifyLog(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
mebId: undefined,
orderId: undefined,
pickUpVerifyCode: undefined,
verifyTime: undefined,
status: undefined,
remark: undefined,
}
formRef.value?.resetFields()
}
</script>

@ -0,0 +1,166 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="兑换状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择兑换状态"
clearable
class="!w-240px"
>
<el-option label="已兑换" value="1" />
<el-option label="未兑换" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="会员名称" align="center" prop="nickname" />
<el-table-column label="手机号" align="center" prop="phone" />
<el-table-column label="已结算订单数" align="center" prop="haveAlready" />
<el-table-column label="未结算订单数" align="center" prop="unAlready" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="handleAlready(scope.row)"
v-hasPermi="['member:distribution-statistics:update']"
>
结算
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<VerifyLogForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { VerifyLogApi, VerifyLogVO } from '@/api/trade/verifylog'
import VerifyLogForm from './VerifyLogForm.vue'
/** 门店核销记录 列表 */
defineOptions({ name: 'VerifyLog' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<VerifyLogVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
mebId: undefined,
orderId: undefined,
pickUpVerifyCode: undefined,
verifyTime: [],
status: undefined,
remark: undefined,
createTime: [],
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await VerifyLogApi.getVerifyLogPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await VerifyLogApi.deleteVerifyLog(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 结算按钮 */
const handleAlready = async (row) => {
try {
//
await message.confirm("确认结算用户"+row.nickname+"的订单吗?")
//
await VerifyLogApi.updateAlready(row)
message.success('结算完成!')
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await VerifyLogApi.exportVerifyLog(queryParams)
download.excel(data, '门店核销记录.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
Loading…
Cancel
Save