Merge remote-tracking branch 'origin/master'

This commit is contained in:
zjf committed 2025-09-22 17:00:57 +08:00
commit daa318f320
22 files changed
+844 -32

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
-- ----------------------------
-- Table structure for act_cancel_item
-- ----------------------------
DROP TABLE IF EXISTS act_cancel_item;
CREATE TABLE act_cancel_item
(
id bigint NOT NULL AUTO_INCREMENT COMMENT '活动项id',
act_id bigint NOT NULL COMMENT '活动id',
mch_id bigint NOT NULL COMMENT '活动商户id',
receiver_user_id bigint NOT NULL COMMENT '领取人用户id',
receiver_time datetime NOT NULL COMMENT '领取时间',
act_status char(32) NOT NULL COMMENT '活动状态(1:未核销 2:已核销)',
cancel_user_id bigint DEFAULT NULL COMMENT '核销人用户id',
cancel_time datetime DEFAULT NULL COMMENT '核销时间',
expire_time datetime DEFAULT NULL COMMENT '过期时间',
create_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
create_time datetime DEFAULT NULL COMMENT '创建时间',
update_by varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
update_time datetime DEFAULT NULL COMMENT '修改时间',
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
PRIMARY KEY (id)
) ENGINE = InnoDB COMMENT ='核销活动项';
@@ -2,6 +2,7 @@ package com.bsd.pjcloud.common.data.datascope;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
@@ -58,7 +59,7 @@ public class DataScopeInnerInterceptor implements DataScopeInterceptor {
dataScope.getFunc().getType(), originalSql);
}
// 2.如果为本人权限 + 部门权限控制
else if (StrUtil.isNotBlank(dataScope.getUsername()) && CollUtil.isNotEmpty(deptIds)) {
else if (StrUtil.isNotBlank(dataScope.getUsername()) && ObjectUtil.isNotEmpty(dataScope.getScopeUserName()) && CollUtil.isNotEmpty(deptIds)) {
String join = CollectionUtil.join(deptIds, ",");
originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' OR temp_data_scope.%s IN (%s)",
dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(), dataScope.getScopeDeptName(), join);
@@ -20,7 +20,8 @@ public enum HardTypeEnum {
READ_CARD("3", "读卡器"),
DOOR("4", "生鲜柜"),
MERCHANT_QR_CODE("5", "商户二维码扣款牌"),
OTHER("6","其他支付方式(第三方接口对接)")
OTHER("6","其他支付方式(第三方接口对接)"),
MERCHANT_ACT("7", "商户活动"),
;
private final String type;
private final String description;
@@ -29,6 +29,7 @@ public enum HardPayTypeEnum {
ZYY("12", "中医院"),
RMYY("13", "人民医院"),
CSXFK("14", "超市消费卡"),
ACT_CANCEL("50", "活动核销"),
;
private final String type;
private final String description;
@@ -70,7 +71,8 @@ public enum HardPayTypeEnum {
// 简单支付(没有购物车)
public static final List<HardPayTypeEnum> isSimplePayList = List.of(
WALLET_QR_CODE_SCAN
WALLET_QR_CODE_SCAN,
ACT_CANCEL
);
public static HardPayTypeEnum valueOfByType(String type) {
@@ -24,7 +24,7 @@ public class HardCarSaveOrUpdateDto {
private Long merchantId;
@Schema(description = "购物车")
@NotEmpty(message = "购物车不能为空")
// @NotEmpty(message = "购物车不能为空")
private List<HardCreateCarDto> list;
@Schema(description = "用户id")
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.hard.api.dto.order.act;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class HardCreateActOrderDto {
@Schema(description = "活动类型")
private String actType;
@Schema(description = "活动id")
@NotNull(message = "活动id")
private Long actId;
@Schema(description = "总金额不能为空")
@NotNull(message = "总金额")
private Integer totalPrice;
@Schema(description = "核销用户id")
private Long cancelUserId;
}
@@ -31,6 +31,7 @@ import com.bsd.pjcloud.hard.api.dto.hard.CurrentUserHardTypeInfoDto;
import com.bsd.pjcloud.hard.api.dto.hard.HardOfflinePayDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderPreferencesDto;
import com.bsd.pjcloud.hard.api.dto.order.act.HardCreateActOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.app.HardAppCreateOrderDto;
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
import com.bsd.pjcloud.hard.api.vo.order.HardOrderVo;
@@ -173,7 +174,7 @@ public class HardOrderController {
@Operation(summary = "app创建线上超市预定订单", description = "app创建线上超市预定订单")
@GetMapping("/app/create-supermarket-order")
@Idempotent(expireTime = 3, info = "正在创建订单中,请勿重复操作")
public R appCreateSupermarketReserveOrder(@RequestParam Long carParentId, @RequestParam HardTypeEnum hardType) {
public R<Long> appCreateSupermarketReserveOrder(@RequestParam Long carParentId, @RequestParam HardTypeEnum hardType) {
PjcloudUser user = SecurityUtils.getUser();
HardAppCreateOrderDto dto = HardAppCreateOrderDto.builder()
.carParentId(carParentId).walletType(WalletTypeEnum.EAT)
@@ -190,7 +191,7 @@ public class HardOrderController {
@Operation(summary = "app创建订单扫一扫", description = "app创建订单扫一扫")
@PostMapping("/app/create-order-scan-qrcode")
@Idempotent(expireTime = 5, info = "正在创建订单中,请勿重复操作")
public R appCreateOrderScancode(@Validated @RequestBody HardAppCreateOrderScancode body) {
public R<Long> appCreateOrderScancode(@Validated @RequestBody HardAppCreateOrderScancode body) {
PjcloudUser user = SecurityUtils.getUser();
HardAppCreateOrderDto dto = HardAppCreateOrderDto.builder()
.customPrice(body.getCustomPrice())
@@ -203,6 +204,16 @@ public class HardOrderController {
.build();
return R.ok(hardwareOrderService.appCreateOrderScancode(dto));
}
@Operation(summary = "app创建活动订单", description = "app创建活动订单")
@PostMapping("/app/create-act-order")
@Idempotent(expireTime = 5, info = "正在创建订单中,请勿重复操作")
public R<Long> appCreateActOrder(@Validated @RequestBody HardCreateActOrderDto body) {
PjcloudUser user = SecurityUtils.getUser();
body.setCancelUserId(user.getId());
return R.ok(hardwareOrderService.appCreateActOrder(body));
}
/* ************************************* end app end ************************************* */
@Operation(summary = "分页查询", description = "分页查询")
@@ -14,6 +14,7 @@ import com.bsd.pjcloud.hard.api.dto.hard.HardCreateOrderDto;
import com.bsd.pjcloud.hard.api.dto.hard.HardDoneHardQrCodeScanOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.HardCreateCarDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.act.HardCreateActOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.app.HardAppCreateOrderDto;
import com.bsd.pjcloud.hard.api.entity.order.HardCarEntity;
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
@@ -70,6 +71,11 @@ public interface HardOrderService extends IService<HardOrderEntity> {
*/
Long appCreateOrderScancode(@Valid HardAppCreateOrderDto dto);
/**
* 创建活动订单
*/
Long appCreateActOrder(HardCreateActOrderDto body);
/**
* 创建智能柜订单
*/
@@ -131,7 +131,9 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
if (ObjectUtil.isNotEmpty(parentCar)) {
baseMapper.delete(new LambdaQueryWrapper<HardCarEntity>().eq(HardCarEntity::getParentCarId, parentCar.getId()));
// 添加购物车
getSelf().appCreateCard(parentCar.getId(), dto.getHardType(), dto.getOrderType(), carList);
if (ObjectUtil.isNotEmpty(carList)) {
getSelf().appCreateCard(parentCar.getId(), dto.getHardType(), dto.getOrderType(), carList);
}
return;
}
// 添加父订单
@@ -169,24 +171,46 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
yield canteenTotalPrice.get();
}
// 查询超市商品
case SUPERMARKET -> {
// 查询超市商品
AtomicInteger supermarketTotalPrice = new AtomicInteger();
List<SmErpUnitWithSpuVo> supList = supermarketUnitSkuService.getSupermarketUnits(productIds).getData();
for (HardCreateCarDto v : list) {
supList.stream().filter(i -> ObjectUtil.equals(i.getId(), v.getProductId())).findFirst().ifPresent(spu -> {
Integer price = getSupermarket(spu);
if (ObjectUtil.isNotEmpty(v.getProductCount()) && !ObjectUtil.equals(v.getProductCount(), 0)) {
// 按数量
int itemTotalPrice = v.getProductCount() * price;
supermarketTotalPrice.addAndGet(itemTotalPrice);
} else if (ObjectUtil.isNotEmpty(v.getProductWeight()) && v.getProductWeight().compareTo(BigDecimal.valueOf(0)) != 0) {
// 按重量
// 向上取整
int itemTotalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
supermarketTotalPrice.addAndGet(itemTotalPrice);
}
});
// 线上
if (ObjectUtil.equals(HardOrderTypeEnum.RESERVE, orderType)) {
List<ShopSkuEntity> skuList = remoteShopSkuService.getSelectListAll(productIds).getData();
for (HardCreateCarDto v : list) {
skuList.stream().filter(i -> ObjectUtil.equals(i.getId(), v.getProductId())).findFirst().ifPresent(sku -> {
Integer price = getSupermarket(sku);
if (ObjectUtil.isNotEmpty(v.getProductCount()) && !ObjectUtil.equals(v.getProductCount(), 0)) {
// 按数量
int itemTotalPrice = v.getProductCount() * price;
supermarketTotalPrice.addAndGet(itemTotalPrice);
} else if (ObjectUtil.isNotEmpty(v.getProductWeight()) && v.getProductWeight().compareTo(BigDecimal.valueOf(0)) != 0) {
// 按重量
// 向上取整
int itemTotalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
supermarketTotalPrice.addAndGet(itemTotalPrice);
}
});
}
}
// 线下
else {
List<SmErpUnitWithSpuVo> supList = supermarketUnitSkuService.getSupermarketUnits(productIds).getData();
for (HardCreateCarDto v : list) {
supList.stream().filter(i -> ObjectUtil.equals(i.getId(), v.getProductId())).findFirst().ifPresent(spu -> {
Integer price = getSupermarket(spu);
if (ObjectUtil.isNotEmpty(v.getProductCount()) && !ObjectUtil.equals(v.getProductCount(), 0)) {
// 按数量
int itemTotalPrice = v.getProductCount() * price;
supermarketTotalPrice.addAndGet(itemTotalPrice);
} else if (ObjectUtil.isNotEmpty(v.getProductWeight()) && v.getProductWeight().compareTo(BigDecimal.valueOf(0)) != 0) {
// 按重量
// 向上取整
int itemTotalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
supermarketTotalPrice.addAndGet(itemTotalPrice);
}
});
}
}
yield supermarketTotalPrice.get();
}
@@ -19,6 +19,8 @@ import com.bsd.pjcloud.admin.api.vo.UserVO;
import com.bsd.pjcloud.canteen.api.feign.RemoteOnlineCanteenSpuService;
import com.bsd.pjcloud.canteen.dto.CanteenSpuUpdateStockReqDTO;
import com.bsd.pjcloud.common.core.constant.CacheConstants;
import com.bsd.pjcloud.common.core.constant.CommonConstants;
import com.bsd.pjcloud.common.core.constant.CommonRoleConstant;
import com.bsd.pjcloud.common.core.constant.enums.MerchantTypeEnum;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.common.core.util.BeanUtils;
@@ -39,6 +41,7 @@ import com.bsd.pjcloud.hard.api.dto.hard.HandCancelCodeDto;
import com.bsd.pjcloud.hard.api.dto.hard.HardCreateOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.HardCreateCarDto;
import com.bsd.pjcloud.hard.api.dto.order.HardOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.act.HardCreateActOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.app.HardAppCreateOrderDto;
import com.bsd.pjcloud.hard.api.dto.spu.DeductionSpuStockDto;
import com.bsd.pjcloud.hard.api.dto.wechat.WechantReverseDto;
@@ -70,12 +73,15 @@ import com.bsd.pjcloud.thirdparty.dto.ks.KsRecognitionCallbackDto;
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
import com.bsd.pjcloud.wallet.api.constant.WalletCodeBizEnum;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActCancelStatusEnum;
import com.bsd.pjcloud.wallet.api.constant.mch.WalletMchRulePriceTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.mch.WalletMchSupType;
import com.bsd.pjcloud.wallet.api.dto.WalletBalanceTransactionDto;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.WalletCodeUseEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import com.bsd.pjcloud.wallet.api.feign.*;
import com.bsd.pjcloud.wallet.api.utils.WalletDataUtil;
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
@@ -98,10 +104,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -141,6 +144,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
private final RemoteOnlineCanteenSpuService onlineCanteenSpuService;
private final RemoteSupermarketSpuService supermarketSpuService;
private final RemoteShopSkuService remoteShopSkuService;
private final RemoteActCancelService remoteActCancelService;
private final HardManagerMapper hardManagerMapper;
@@ -413,13 +417,12 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
orderObj.setUserId(-1L);
baseMapper.insert(orderObj);
// String qrCodeData = String.format("https://stgl.pj.gov.cn/h5/#/pages/pay/scanPay?action=harQrCodeScanPay&orderId=%s&mchId=%s", orderObj.getId(), orderObj.getMerchantId());
// 在redis里存储订单id如果5分钟没有支付就删除订单
String redisKey = String.format("%s:%s", CacheConstants.HARDER_ORDER_NON_PAY, orderObj.getId());
redisTemplate.opsForValue().set(redisKey, String.valueOf(orderObj.getId()), 300, TimeUnit.SECONDS);
String qrCodeData = String.format("http://192.168.101.173:8889/h5/#/pages/pay/scanPay?action=harQrCodeScanPay&orderId=%s&mchId=%s", orderObj.getId(), orderObj.getMerchantId());
// String qrCodeData = String.format("http://192.168.101.173:8889/h5/#/pages/pay/scanPay?action=harQrCodeScanPay&orderId=%s&mchId=%s", orderObj.getId(), orderObj.getMerchantId());
String qrCodeData = String.format("https://stgl.pj.gov.cn/h5/#/pages/pay/scanPay?action=harQrCodeScanPay&orderId=%s&mchId=%s", orderObj.getId(), orderObj.getMerchantId());
return HardCreateHardQrCodeScanOrderVo.builder().orderId(orderObj.getId()).mchId(orderObj.getMerchantId()).qrCodeData(qrCodeData).build();
}
@@ -679,6 +682,90 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
return orderObj.getId();
}
/**
* 创建活动订单
*/
@Override
@Lock4j(keys = {"#dto.getActId()"})
@Transactional(rollbackFor = Exception.class)
public Long appCreateActOrder(HardCreateActOrderDto dto) {
if (ObjectUtil.isEmpty(dto.getCancelUserId())) throw new RuntimeException("核销用户不能为空");
// 查询活动详情
ActCancelItemEntity actDetail = remoteActCancelService.getDetail(dto.getActId()).getData();
if (ObjectUtil.isEmpty(actDetail)) throw new RuntimeException("活动不存在");
if (ObjectUtil.isNotEmpty(actDetail.getExpireTime())) {
if (actDetail.getExpireTime().isBefore(LocalDateTime.now())) {
throw new RuntimeException("核销失败,您领取的优惠券已过期");
}
}
if (ObjectUtil.equals(actDetail.getActStatus(), ActCancelStatusEnum.CANCEL_SUCCESS.getCode())) {
throw new RuntimeException("核销失败,您领取的优惠券已核销");
}
// 查询活动商户
MchManageVo mch = remoteMchService.getById(actDetail.getMchId()).getData();
if (ObjectUtil.isEmpty(mch)) throw new RuntimeException("商户不存在");
// 查询核销人
UserInfo cancelUserDetail = userService.getInnerByUserId(dto.getCancelUserId()).getData();
if (ObjectUtil.isEmpty(cancelUserDetail)) throw new RuntimeException("核销用户不存在不存在");
if (ObjectUtil.isEmpty(cancelUserDetail.getRoleCodes()) || !Arrays.asList(cancelUserDetail.getRoleCodes()).contains(CommonRoleConstant.MCH_USER)) {
throw new RuntimeException("当前用户没有核销权限");
}
MchManageVo cancelUserMch = remoteMchService.getFirstMch(List.of(cancelUserDetail.getSysUser().getDeptId()), MerchantTypeEnum.SUPERMARKET).getData();
if (ObjectUtil.isEmpty(cancelUserMch) || ObjectUtil.notEqual(cancelUserMch.getId(), actDetail.getMchId())) {
throw new RuntimeException("当前活动,该用户没有核销权限");
}
// 查询用户
UserInfo userDetail = userService.getInnerByUserId(actDetail.getReceiverUserId()).getData();
if (ObjectUtil.isEmpty(userDetail)) throw new RuntimeException("领券用户不存在不存在");
// 钱包
WalletManageEntity wallet = remoteWalletService.getWallet(userDetail.getSysUser().getUserId(), WalletTypeEnum.PAY).getData();
if (ObjectUtil.isEmpty(wallet)) throw new RuntimeException("用户钱包不存在");
Integer balance = wallet.getBalance();
int totalPrice = 1 * 100;
String subject = "活动商品x?";
if (balance < totalPrice) throw new RuntimeException("钱包余额不足");
// 添加硬件订单
HardOrderEntity orderObj = HardOrderEntity.builder()
.userId(userDetail.getSysUser().getUserId())
.userDeptId(userDetail.getSysUser().getDeptId())
.merchantId(mch.getId())
.merchantType(mch.getTypes())
.merchantDeptId(mch.getDeptId())
.parentCarId(null)
.sysType(HardTypeEnum.MERCHANT_ACT.getType())
.orderStatus(HardOrderStatusEnum.CREATE.getType())
.orderType(HardOrderTypeEnum.RESERVE.getType())
.payType(HardPayTypeEnum.ACT_CANCEL.getType())
.orderSourceTypeId(dto.getActId())
.totalPrice(totalPrice)
.extraPrice(0)
.payPrice(totalPrice)
.isOfflineOrder(YesNoEnum.NO.getCode())
.productDesc(subject)
.walletId(wallet.getId())
.build();
baseMapper.insert(orderObj);
// 核销
ActCancelItemDto cancelDto = new ActCancelItemDto();
cancelDto.setActId(dto.getActId());
cancelDto.setCancelUserId(dto.getCancelUserId());
R<Boolean> cancelVo = remoteActCancelService.cancel(cancelDto);
if (ObjectUtil.notEqual(cancelVo.getCode(), CommonConstants.SUCCESS)) {
String errMsg = cancelVo.getMsg();
if (ObjectUtil.isEmpty(errMsg)) errMsg = "核销失败,位置的错误";
throw new RuntimeException(errMsg);
}
// 钱包扣款
getSelf().fastReduceBalance(orderObj);
return orderObj.getId();
}
/**
* 创建智能柜订单
*/
@@ -257,7 +257,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
*/
@Override
public IPage<UserVO> getUsersWithRolePage(Page page, UserDTO userDTO) {
IPage<UserVO> list = baseMapper.getUserVosPage(page, userDTO, DataScope.of());
DataScope dataScope = DataScope.of();
dataScope.setScopeUserName(null);
IPage<UserVO> list = baseMapper.getUserVosPage(page, userDTO, dataScope);
if (ObjectUtil.isNotEmpty(list.getRecords())) {
List<Long> userIds = list.getRecords().stream().map(UserVO::getUserId).distinct().toList();
if (ObjectUtil.isNotEmpty(userIds)) {
@@ -939,7 +941,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
public List<SimpleUserListVO> listUserIdByDeptId(Long deptId, String name) {
List<SysUser> sysUsers = baseMapper.selectList(Wrappers.<SysUser>lambdaQuery()
.eq(SysUser::getDeptId, deptId)
@@ -0,0 +1,36 @@
package com.bsd.pjcloud.wallet.api.constant.act;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 核销活动状体啊
*/
@Getter
@AllArgsConstructor
public enum ActCancelStatusEnum {
CANCEL_AWAIT("1", "未核销"),
CANCEL_SUCCESS("2", "已核销"),
CANCEL_EXPIRE("3", "已过期"),
;
private final String code;
private final String desc;
public static ActCancelStatusEnum valueOfByType(String code) {
return Arrays.stream(ActCancelStatusEnum.values())
.filter(v -> ObjectUtil.equal(v.getCode(), code))
.findFirst().get();
}
public static ActCancelStatusEnum descOf(String description) {
return Arrays.stream(ActCancelStatusEnum.values())
.filter(v -> ObjectUtil.equal(v.getDesc(), description))
.findFirst().get();
}
}
@@ -0,0 +1,13 @@
package com.bsd.pjcloud.wallet.api.dto.act;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ActCancelItemDto extends ActCancelItemEntity {
private LocalDateTime[] cancelTimeList;
}
@@ -0,0 +1,124 @@
package com.bsd.pjcloud.wallet.api.entity.act;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.bsd.pjcloud.common.core.util.TenantTable;
import java.time.LocalDateTime;
/**
* 核销活动项
*/
@Data
@TenantTable
@TableName("act_cancel_item")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "核销活动项")
public class ActCancelItemEntity extends Model<ActCancelItemEntity> {
/**
* 活动项id
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "活动项id")
private Long id;
/**
* 活动id
*/
@Schema(description = "活动id")
private Long actId;
/**
* 活动商户id
*/
@Schema(description = "活动商户id")
private Long mchId;
/**
* 领取人用户id
*/
@Schema(description = "领取人用户id")
private Long receiverUserId;
/**
* 领取人用户名
*/
@Schema(description = "领取人用户名")
private String receiverUserName;
/**
* 领取时间
*/
@Schema(description = "领取时间")
private LocalDateTime receiverTime;
/**
* 活动状态(1:未核销 2:已核销)
*/
@Schema(description = "活动状态(1:未核销 2:已核销)")
private String actStatus;
/**
* 核销人用户id
*/
@Schema(description = "核销人用户id")
private Long cancelUserId;
/**
* 核销时间
*/
@Schema(description = "核销时间")
private LocalDateTime cancelTime;
/**
* 过期时间
*/
@Schema(description = "过期时间")
private LocalDateTime expireTime;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 修改人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "修改时间")
private LocalDateTime updateTime;
/**
* 删除标记
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记")
private String delFlag;
/**
* 租户ID
*/
@Schema(description = "租户ID")
private Long tenantId;
}
@@ -0,0 +1,26 @@
package com.bsd.pjcloud.wallet.api.feign;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.feign.annotation.NoToken;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(contextId = "RemoteActCancelService", value = ServiceNameConstants.WALLET_SERVICE)
public interface RemoteActCancelService {
@NoToken
@GetMapping("/act/cancel-item/get-detail")
R<ActCancelItemEntity> getDetail(@RequestParam Long id);
@NoToken
@PostMapping("/act/cancel-item/cancel")
R<Boolean> cancel(@RequestBody ActCancelItemDto dto);
}
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.wallet.api.vo.act;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import lombok.Data;
@Data
public class ActCancelItemVo extends ActCancelItemEntity {
/**
* 领取人
*/
private String receiverUsername;
/**
* 核销人
*/
private String cancelUsername;
/**
* 活动商户名
*/
private String mchName;
}
@@ -1,3 +1,4 @@
com.bsd.pjcloud.wallet.api.feign.RemoteActCancelService
com.bsd.pjcloud.wallet.api.feign.RemoteWalletService
com.bsd.pjcloud.wallet.api.feign.RemoteMchRuleJoinService
com.bsd.pjcloud.wallet.api.feign.RemoteCodeUseService
@@ -0,0 +1,121 @@
package com.bsd.pjcloud.wallet.controller.act;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCancelItemVo;
import com.bsd.pjcloud.wallet.service.ActCancelItemService;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.HttpHeaders;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 核销活动项
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/act/cancel-item")
@Tag(description = "cancel-item", name = "核销活动项管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ActCancelItemController {
private final ActCancelItemService actCancelItemService;
/* ************************************* start 远程调用 start ************************************* */
@Inner()
@Operation(summary = "查询优惠券详情", description = "查询优惠券详情")
@GetMapping("/get-detail")
public R<ActCancelItemEntity> getDetail(@RequestParam Long id) {
return R.ok(actCancelItemService.getById(id));
}
@Inner()
@Operation(summary = "核销优惠券", description = "核销优惠券")
@PostMapping("/cancel")
public R<Boolean> cancel(@RequestBody ActCancelItemDto dto) {
return R.ok(actCancelItemService.cancel(dto));
}
/* ************************************* end 远程调用 end ************************************* */
/* ************************************* start app start ************************************* */
@Operation(summary = "app领取优惠券", description = "app领取优惠券")
@PostMapping("/app/get")
@Idempotent(expireTime = 2, key = "'/act/cancel/app/get/' + #dto.getReceiverUserId()", info = "正在领取中,请勿重复操作")
public R<Long> appGet(@RequestBody ActCancelItemDto dto) {
PjcloudUser user = SecurityUtils.getUser();
return R.ok(actCancelItemService.appGet(user, dto));
}
@Operation(summary = "app查询用户的唯一优惠券", description = "app查询用户的唯一优惠券")
@GetMapping("/app/find-unique")
public R<ActCancelItemVo> appFindUnique() {
PjcloudUser user = SecurityUtils.getUser();
return R.ok(actCancelItemService.appFindUnique(user));
}
@Operation(summary = "app查询本店优惠券列表", description = "app查询本店优惠券列表")
@GetMapping("/app/mch/page")
public R<Page<ActCancelItemVo>> appMchPage(@ParameterObject Page page, @ParameterObject ActCancelItemDto dto) {
PjcloudUser user = SecurityUtils.getUser();
return R.ok(actCancelItemService.appMchPage(page, user, dto));
}
/* ************************************* start app start ************************************* */
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('wallet_actCancelItem_view')")
public R getActCancelItemPage(@ParameterObject Page page, @ParameterObject ActCancelItemDto dto) {
return R.ok(actCancelItemService.pageList(page, dto));
}
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('wallet_actCancelItem_view')")
public R getById(@PathVariable("id") Long id) {
return R.ok(actCancelItemService.getById(id));
}
@Operation(summary = "新增核销活动项", description = "新增核销活动项")
@SysLog("新增核销活动项")
@PostMapping
@PreAuthorize("@pms.hasPermission('wallet_actCancelItem_add')")
public R save(@RequestBody ActCancelItemEntity actCancelItem) {
return R.ok(actCancelItemService.save(actCancelItem));
}
@Operation(summary = "修改核销活动项", description = "修改核销活动项")
@SysLog("修改核销活动项")
@PutMapping
@PreAuthorize("@pms.hasPermission('wallet_actCancelItem_edit')")
public R updateById(@RequestBody ActCancelItemEntity actCancelItem) {
return R.ok(actCancelItemService.updateById(actCancelItem));
}
@Operation(summary = "通过id删除核销活动项", description = "通过id删除核销活动项")
@SysLog("通过id删除核销活动项")
@DeleteMapping
@PreAuthorize("@pms.hasPermission('wallet_actCancelItem_del')")
public R removeById(@RequestBody Long[] ids) {
return R.ok(actCancelItemService.removeBatchByIds(CollUtil.toList(ids)));
}
}
@@ -0,0 +1,11 @@
package com.bsd.pjcloud.wallet.mapper.act;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ActCancelItemMapper extends PjcloudBaseMapper<ActCancelItemEntity> {
}
@@ -0,0 +1,34 @@
package com.bsd.pjcloud.wallet.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCancelItemVo;
public interface ActCancelItemService extends IService<ActCancelItemEntity> {
Page<ActCancelItemVo> pageList(Page page, ActCancelItemDto dto);
/**
* 领取优惠券
*/
Long appGet(PjcloudUser user, ActCancelItemDto dto);
/**
* 查询用户的唯一优惠券
*/
ActCancelItemVo appFindUnique(PjcloudUser user);
/**
* app查询本店优惠券列表
*/
Page<ActCancelItemVo> appMchPage(Page page, PjcloudUser user, ActCancelItemDto dto);
/**
* 核销
*/
Boolean cancel(ActCancelItemDto dto);
}
@@ -0,0 +1,236 @@
package com.bsd.pjcloud.wallet.service.impl.act;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.admin.api.vo.UserVO;
import com.bsd.pjcloud.common.core.constant.CommonRoleConstant;
import com.bsd.pjcloud.common.core.constant.enums.MerchantTypeEnum;
import com.bsd.pjcloud.common.core.util.CollectionUtils;
import com.bsd.pjcloud.common.data.resolver.ParamResolver;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActCancelStatusEnum;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import com.bsd.pjcloud.wallet.api.entity.mch.MchManageEntity;
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
import com.bsd.pjcloud.wallet.api.vo.act.ActCancelItemVo;
import com.bsd.pjcloud.wallet.mapper.act.ActCancelItemMapper;
import com.bsd.pjcloud.wallet.service.ActCancelItemService;
import com.bsd.pjcloud.wallet.service.MchManageService;
import com.bsd.pjcloud.wallet.service.WalletManageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 核销活动项
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class ActCancelItemServiceImpl extends ServiceImpl<ActCancelItemMapper, ActCancelItemEntity> implements ActCancelItemService {
private final String TAG = "核销活动";
private final MchManageService mchManageService;
private final WalletManageService walletManageService;
private final RemoteUserService remoteUserService;
@Override
public Page<ActCancelItemVo> pageList(Page page, ActCancelItemDto dto) {
Page<ActCancelItemVo> pageList = baseMapper.selectPage(page, new LambdaQueryWrapper<ActCancelItemEntity>()
.eq(ObjectUtil.isNotEmpty(dto.getMchId()), ActCancelItemEntity::getMchId, dto.getMchId())
.eq(ObjectUtil.isNotEmpty(dto.getReceiverUserId()), ActCancelItemEntity::getReceiverUserId, dto.getReceiverUserId())
.like(ObjectUtil.isNotEmpty(dto.getReceiverUserName()), ActCancelItemEntity::getReceiverUserName, dto.getReceiverUserName())
.eq(ObjectUtil.isNotEmpty(dto.getCancelUserId()), ActCancelItemEntity::getCancelUserId, dto.getCancelUserId())
.eq(ObjectUtil.isNotEmpty(dto.getActStatus()), ActCancelItemEntity::getActStatus, dto.getActStatus())
.orderByDesc(ActCancelItemEntity::getReceiverTime)
.orderByDesc(ActCancelItemEntity::getId)
);
if (ObjectUtil.isNotEmpty(pageList.getRecords())) {
List<ActCancelItemVo> list = BeanUtil.copyToList(pageList.getRecords(), ActCancelItemVo.class);
disposeList(list, dto);
pageList.setRecords(list);
}
return pageList;
}
private void disposeList(List<ActCancelItemVo> list, ActCancelItemDto dto) {
if (ObjectUtil.isEmpty(list)) return;
List<Long> receiverUserIds = list.stream().map(ActCancelItemVo::getReceiverUserId).toList();
List<Long> cancelUserIds = list.stream().map(ActCancelItemVo::getCancelUserId).toList();
List<Long> userIds = new ArrayList<Long>(receiverUserIds);
userIds.addAll(cancelUserIds);
userIds = userIds.stream().distinct().filter(ObjectUtil::isNotEmpty).toList();
if (ObjectUtil.isNotEmpty(userIds)) {
// 用户信息
List<UserVO> userList = remoteUserService.getUserDeptByIds(userIds).getData();
if (ObjectUtil.isNotEmpty(userList)) {
Map<Long, UserVO> userVOMap = CollectionUtils.convertMap(userList, UserVO::getUserId);
for (ActCancelItemVo item : list) {
UserVO receiverUser = userVOMap.get(item.getReceiverUserId());
if (ObjectUtil.isNotEmpty(receiverUser)) {
item.setReceiverUsername(receiverUser.getName());
}
UserVO cancelUser = userVOMap.get(item.getCancelUserId());
if (ObjectUtil.isNotEmpty(cancelUser)) {
item.setCancelUsername(cancelUser.getName());
}
}
}
}
List<Long> mchIds = list.stream().map(ActCancelItemVo::getMchId).distinct().filter(ObjectUtil::isNotEmpty).toList();
if (ObjectUtil.isNotEmpty(mchIds)) {
// 商户信息
List<MchManageEntity> mchList = mchManageService.list(new LambdaQueryWrapper<MchManageEntity>()
.in(MchManageEntity::getId, mchIds)
);
if (ObjectUtil.isNotEmpty(mchList)) {
Map<Long, MchManageEntity> mchMap = CollectionUtils.convertMap(mchList, MchManageEntity::getId);
for (ActCancelItemVo item : list) {
MchManageEntity mchManage = mchMap.get(item.getMchId());
if (ObjectUtil.isNotEmpty(mchManage)) {
item.setMchName(mchManage.getName());
}
}
}
}
}
/**
* 领取优惠券
*/
@Override
public Long appGet(PjcloudUser user, ActCancelItemDto dto) {
// 获取商户
MchManageEntity actMch = null;
try {
Long mchId = ParamResolver.getLong("ACT_CANCEL_MCH_ID");
if (ObjectUtil.isEmpty(mchId)) throw new Exception("活动商户id为空");
actMch = mchManageService.getById(mchId);
} catch (Exception e) {
e.printStackTrace();
log.error("|{}|获取商户ID失败,error:|{}|", TAG, e.getMessage());
}
if (ObjectUtil.isEmpty(actMch)) throw new RuntimeException("核销活动商户不存在");
// 判断是否已经领取
if (existAct(user.getId())) throw new RuntimeException("您已经领,请勿重复领取");
// 查询余是否大于1元
WalletManageEntity wallet = walletManageService.getWallet(user.getId(), WalletTypeEnum.PAY);
if (wallet.getBalance() < 100) throw new RuntimeException("消费钱包余额不足1元,无法领取");
LocalDateTime now = LocalDateTime.now();
LocalDateTime expireTime = now.plusDays(90);
ActCancelItemEntity saveObj = new ActCancelItemEntity();
saveObj.setActId(-1L);
saveObj.setMchId(actMch.getId());
saveObj.setReceiverUserId(user.getId());
saveObj.setReceiverUserName(user.getName());
saveObj.setReceiverTime(now);
saveObj.setActStatus(ActCancelStatusEnum.CANCEL_AWAIT.getCode());
saveObj.setExpireTime(expireTime);
this.save(saveObj);
return saveObj.getActId();
}
/**
* 查询用户的唯一优惠券
*/
@Override
public ActCancelItemVo appFindUnique(PjcloudUser user) {
ActCancelItemEntity detail = baseMapper.selectOne(new LambdaQueryWrapper<ActCancelItemEntity>()
.eq(ActCancelItemEntity::getReceiverUserId, user.getId())
);
if (ObjectUtil.isEmpty(detail)) throw new RuntimeException("您未领取优惠券,请前往领取");
if (ObjectUtil.isNotEmpty(detail.getExpireTime())) {
if (detail.getExpireTime().isBefore(LocalDateTime.now())) {
throw new RuntimeException("您领取的优惠券已过期");
}
}
return BeanUtil.toBean(detail, ActCancelItemVo.class);
}
/**
* app查询本店优惠券列表
*/
@Override
public Page<ActCancelItemVo> appMchPage(Page page, PjcloudUser user, ActCancelItemDto dto) {
if (ObjectUtil.isEmpty(user.getDeptId())) return null;
// 查询用户
UserInfo userInfo = remoteUserService.getByUserId(user.getId()).getData();
if (ObjectUtil.isEmpty(userInfo)) return null;
// 查询商户
MchManageVo mchDetail = mchManageService.getByDeptId(userInfo.getSysUser().getDeptId(), MerchantTypeEnum.SUPERMARKET);
if (ObjectUtil.isEmpty(mchDetail)) return null;
// 判断是否有商户权限
if (ObjectUtil.isEmpty(userInfo.getRoleCodes()) || !Arrays.asList(userInfo.getRoleCodes()).contains(CommonRoleConstant.MCH_USER)) {
return null;
}
dto.setMchId(mchDetail.getId());
return pageList(page, dto);
}
/**
* 核销
*/
@Override
public Boolean cancel(ActCancelItemDto dto) {
if (ObjectUtil.isEmpty(dto.getActId()) || ObjectUtil.isEmpty(dto.getCancelUserId())) {
log.error("|{}|核销失败,参数错误", TAG);
return false;
}
// 查询活动
ActCancelItemEntity detail = baseMapper.selectById(dto.getActId());
if (ObjectUtil.isEmpty(detail)) {
log.error("|{}|核销失败,活动不存在", TAG);
return false;
}
// 查询核销用户
UserInfo userDetail = remoteUserService.getInnerByUserId(dto.getCancelUserId()).getData();
if (ObjectUtil.isEmpty(userDetail)) {
log.error("|{}|核销失败,核销用户不存在", TAG);
throw new RuntimeException("核销失败,核销用户不存在");
}
if (ObjectUtil.isNotEmpty(detail.getExpireTime())) {
if (detail.getExpireTime().isBefore(LocalDateTime.now())) {
throw new RuntimeException("核销失败,您领取的优惠券已过期");
}
}
if (ObjectUtil.equals(detail.getActStatus(), ActCancelStatusEnum.CANCEL_SUCCESS.getCode())) {
throw new RuntimeException("核销失败,您领取的优惠券已核销");
}
ActCancelItemEntity updateObj = new ActCancelItemEntity();
updateObj.setId(detail.getId());
updateObj.setActStatus(ActCancelStatusEnum.CANCEL_SUCCESS.getCode());
updateObj.setCancelUserId(dto.getCancelUserId());
updateObj.setCancelTime(LocalDateTime.now());
baseMapper.updateById(updateObj);
return true;
}
// 判断车牌号是否重复
private boolean existAct(Long userId) {
Long count = baseMapper.selectCount(new LambdaQueryWrapper<ActCancelItemEntity>()
.eq(ObjectUtil.isNotEmpty(userId), ActCancelItemEntity::getReceiverUserId, userId)
);
return count > 0;
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.wallet.mapper.act.ActCancelItemMapper">
</mapper>