Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
329687d5df
|
|
@ -1,5 +1,6 @@
|
|||
package com.czg.controller.admin;
|
||||
|
||||
import com.czg.account.dto.WxMsgSubDTO;
|
||||
import com.czg.account.dto.msg.ShopMsgEditDTO;
|
||||
import com.czg.account.dto.msg.ShopPushOpenIdEditDTO;
|
||||
import com.czg.account.entity.ShopPushOpenId;
|
||||
|
|
@ -14,6 +15,7 @@ import com.czg.validator.group.DefaultGroup;
|
|||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
|
@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
*
|
||||
* @author Administrator
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/admin/shopMsgPush")
|
||||
public class ShopMsgPushController {
|
||||
|
|
@ -30,6 +33,32 @@ public class ShopMsgPushController {
|
|||
@Resource
|
||||
private ShopPushOpenIdService shopPushOpenIdService;
|
||||
|
||||
|
||||
/**
|
||||
* 店铺推送状态获取
|
||||
*/
|
||||
@PostMapping("/subscribe")
|
||||
public CzgResult<Void> subscribe(@RequestBody WxMsgSubDTO wxMsgSubDTO) {
|
||||
log.info("接收到订阅消息接口调用,携带数据: {}", wxMsgSubDTO);
|
||||
if (wxMsgSubDTO.getOpenId() == null || wxMsgSubDTO.getShopId() == null) {
|
||||
return CzgResult.failure("shopId或openId缺失");
|
||||
}
|
||||
ShopPushOpenId entity = shopPushOpenIdService.getOne(QueryWrapper.create().eq(ShopPushOpenId::getOpenId, wxMsgSubDTO.getOpenId()).eq(ShopPushOpenId::getShopId, wxMsgSubDTO.getShopId()));
|
||||
if (entity == null) {
|
||||
entity = new ShopPushOpenId();
|
||||
}
|
||||
entity.setShopId(wxMsgSubDTO.getShopId());
|
||||
entity.setOpenId(wxMsgSubDTO.getOpenId());
|
||||
entity.setNickname(wxMsgSubDTO.getNickname());
|
||||
entity.setAvatar(wxMsgSubDTO.getAvatar());
|
||||
if (entity.getId() == null) {
|
||||
shopPushOpenIdService.save(entity);
|
||||
} else {
|
||||
shopPushOpenIdService.updateById(entity);
|
||||
}
|
||||
return CzgResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 店铺推送状态获取
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
package com.czg.account.dto;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.czg.account.vo.HandoverCategoryListVo;
|
||||
import com.czg.account.vo.HandoverProductListVo;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package com.czg.account.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 微信订阅消息
|
||||
*
|
||||
* @author Tankaikai tankaikai@aliyun.com
|
||||
* @since 1.0 2025-02-27
|
||||
*/
|
||||
@Data
|
||||
public class WxMsgSubDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Long shopId;
|
||||
private String openId;
|
||||
private String nickname;
|
||||
private String avatar;
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.mybatisflex.annotation.Table;
|
|||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -29,7 +30,7 @@ public class ShopPushOpenId implements Serializable {
|
|||
@Id(keyType = KeyType.Auto)
|
||||
private Integer id;
|
||||
|
||||
private Integer shopId;
|
||||
private Long shopId;
|
||||
|
||||
/**
|
||||
* 微信openid
|
||||
|
|
|
|||
|
|
@ -70,4 +70,10 @@ public interface ConsStockFlowService extends IService<ConsStockFlow> {
|
|||
* @return 分页数据
|
||||
*/
|
||||
Page<ConsStockFlowDTO> findConsStockFlowPage(ConsStockFlowParam param);
|
||||
|
||||
/**
|
||||
* 保存库存变动记录
|
||||
* @param entity 库存变动记录实体
|
||||
*/
|
||||
void saveFlow(ConsStockFlow entity);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.czg.product.service;
|
||||
|
||||
import com.czg.product.entity.ProductStockFlow;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
|
||||
/**
|
||||
* +
|
||||
* 商品库存流水服务类
|
||||
*
|
||||
* @author tankaikai
|
||||
* @since 2025-03-14 15:44
|
||||
*/
|
||||
public interface ProductStockFlowService extends IService<ProductStockFlow> {
|
||||
|
||||
void saveFlow(ProductStockFlow entity);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.czg.product.service;
|
||||
|
||||
/**
|
||||
* 敏感操作Service
|
||||
*
|
||||
* @author Tankaikai tankaikai@aliyun.com
|
||||
* @since 1.0 2025-02-16
|
||||
*/
|
||||
public interface SensitiveOperationService {
|
||||
|
||||
void send(String operationDesc);
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.czg.service.account.util.AliOssUtil;
|
|||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -50,6 +51,8 @@ public class ShopMsgStateServiceImpl extends ServiceImpl<ShopMsgStateMapper, Sho
|
|||
private AliOssUtil aliOssUtil;
|
||||
@Resource
|
||||
private ShopPushOpenIdMapper shopPushOpenIdMapper;
|
||||
@Value("${spring.profiles.active}")
|
||||
private String active;
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -98,7 +101,7 @@ public class ShopMsgStateServiceImpl extends ServiceImpl<ShopMsgStateMapper, Sho
|
|||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:/static/logo.jpg");
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
String url = StrUtil.format("https://invoice.sxczgkj.cn/index/wechat/weuserk?shopId={}", shopId);
|
||||
String url = StrUtil.format("https://invoice.sxczgkj.cn/index/wechat/weuserk?source=ysk&active={}&shopId={}", active, shopId);
|
||||
QrCodeUtil.generate(url, new QrConfig(500, 500).
|
||||
setImg(ImageIO.read(inputStream)).setErrorCorrection(ErrorCorrectionLevel.H).setRatio(4), "png", outputStream);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package com.czg.service.product.mapper;
|
|||
import com.czg.product.entity.ConsInfo;
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 耗材信息
|
||||
|
|
@ -12,5 +15,5 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
*/
|
||||
@Mapper
|
||||
public interface ConsInfoMapper extends BaseMapper<ConsInfo> {
|
||||
|
||||
List<String> findOpenIdList(@Param("shopId") Long shopId, @Param("type") String type);
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.czg.service.product.service.impl;
|
|||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
|
@ -21,10 +23,12 @@ import com.czg.product.vo.ConsCheckStockRecordVo;
|
|||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.product.mapper.ConsInfoMapper;
|
||||
import com.czg.service.product.mapper.ConsStockFlowMapper;
|
||||
import com.czg.service.product.util.WxAccountUtil;
|
||||
import com.czg.utils.PageUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -44,6 +48,8 @@ import java.util.List;
|
|||
public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, ConsStockFlow> implements ConsStockFlowService {
|
||||
|
||||
private final ConsInfoMapper consInfoMapper;
|
||||
@Resource
|
||||
private WxAccountUtil wxAccountUtil;
|
||||
|
||||
private QueryWrapper buildQueryWrapper(ConsStockFlowDTO param) {
|
||||
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
|
||||
|
|
@ -155,7 +161,7 @@ public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, C
|
|||
}
|
||||
entity.setSubTotal(NumberUtil.mul(winLossNumber, param.getPrice()));
|
||||
entity.setRemark(param.getRemark());
|
||||
super.save(entity);
|
||||
saveFlow(entity);
|
||||
consInfo.setStockNumber(entity.getAfterNumber());
|
||||
consInfoMapper.update(consInfo);
|
||||
}
|
||||
|
|
@ -199,7 +205,7 @@ public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, C
|
|||
entity.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
|
||||
entity.setSubTotal(NumberUtil.mul(param.getNumber(), consInfo.getPrice()));
|
||||
entity.setImgUrls(JSON.toJSONString(param.getImgUrls()));
|
||||
super.save(entity);
|
||||
saveFlow(entity);
|
||||
consInfo.setStockNumber(entity.getAfterNumber());
|
||||
consInfoMapper.update(consInfo);
|
||||
}
|
||||
|
|
@ -222,4 +228,27 @@ public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, C
|
|||
return pageAs(PageUtil.buildPage(), queryWrapper, ConsStockFlowDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveFlow(ConsStockFlow entity) {
|
||||
super.save(entity);
|
||||
Long shopId = entity.getShopId();
|
||||
BigDecimal afterNumber = entity.getAfterNumber();
|
||||
ConsInfo consInfo = consInfoMapper.selectOneById(entity.getConId());
|
||||
String shopName = StpKit.USER.getShopName();
|
||||
BigDecimal conWarning = consInfo.getConWarning();
|
||||
// 库存小于警告值,发送消息提醒
|
||||
if (NumberUtil.isLess(afterNumber, conWarning)) {
|
||||
List<String> openIdList = consInfoMapper.findOpenIdList(shopId, "con");
|
||||
if (CollUtil.isEmpty(openIdList)) {
|
||||
return;
|
||||
}
|
||||
String conName = StrUtil.format("{}数量<预警值{}", consInfo.getConName(), conWarning);
|
||||
ThreadUtil.execAsync(() -> {
|
||||
openIdList.parallelStream().forEach(openId -> {
|
||||
wxAccountUtil.sendStockMsg(shopName, conName, afterNumber, openId);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.czg.product.entity.ProdGroup;
|
|||
import com.czg.product.entity.ProdGroupRelation;
|
||||
import com.czg.product.entity.Product;
|
||||
import com.czg.product.service.ProdGroupService;
|
||||
import com.czg.product.service.SensitiveOperationService;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.product.mapper.ProdGroupMapper;
|
||||
import com.czg.service.product.mapper.ProdGroupRelationMapper;
|
||||
|
|
@ -41,8 +42,8 @@ import java.util.List;
|
|||
public class ProdGroupServiceImpl extends ServiceImpl<ProdGroupMapper, ProdGroup> implements ProdGroupService {
|
||||
|
||||
private final ProdGroupRelationMapper prodGroupRelationMapper;
|
||||
|
||||
private final ProductMapper productMapper;
|
||||
private final SensitiveOperationService sensitiveOperationService;
|
||||
|
||||
private QueryWrapper buildQueryWrapper(ProdGroupDTO param) {
|
||||
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
|
||||
|
|
@ -152,6 +153,8 @@ public class ProdGroupServiceImpl extends ServiceImpl<ProdGroupMapper, ProdGroup
|
|||
.eq(ProdGroup::getId, id)
|
||||
.eq(ProdGroup::getShopId, shopId)
|
||||
.update();
|
||||
ProdGroup prodGroup = mapper.selectOneById(id);
|
||||
sensitiveOperationService.send("关闭分组:" + prodGroup.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -162,6 +165,8 @@ public class ProdGroupServiceImpl extends ServiceImpl<ProdGroupMapper, ProdGroup
|
|||
.eq(ProdGroup::getId, id)
|
||||
.eq(ProdGroup::getShopId, shopId)
|
||||
.update();
|
||||
ProdGroup prodGroup = mapper.selectOneById(id);
|
||||
sensitiveOperationService.send("开启分组:" + prodGroup.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -17,7 +17,10 @@ import com.czg.product.dto.ProductDTO;
|
|||
import com.czg.product.entity.*;
|
||||
import com.czg.product.enums.*;
|
||||
import com.czg.product.param.*;
|
||||
import com.czg.product.service.ConsStockFlowService;
|
||||
import com.czg.product.service.ProductService;
|
||||
import com.czg.product.service.ProductStockFlowService;
|
||||
import com.czg.product.service.SensitiveOperationService;
|
||||
import com.czg.product.vo.ProductStatisticsVo;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.product.mapper.*;
|
||||
|
|
@ -59,6 +62,9 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
private final ConsInfoMapper consInfoMapper;
|
||||
private final ConsStockFlowMapper consStockFlowMapper;
|
||||
private final ProductStockFlowMapper productStockFlowMapper;
|
||||
private final ProductStockFlowService productStockFlowService;
|
||||
private final ConsStockFlowService consStockFlowService;
|
||||
private final SensitiveOperationService sensitiveOperationService;
|
||||
|
||||
private QueryWrapper buildQueryWrapper(ProductDTO param) {
|
||||
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
|
||||
|
|
@ -222,7 +228,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
flow.setAfterNumber(NumberUtil.toBigDecimal(entity.getStockNumber()));
|
||||
flow.setInOutType(InOutTypeEnum.IN.value());
|
||||
flow.setInOutItem(InOutItemEnum.WIN_IN.value());
|
||||
productStockFlowMapper.insert(flow);
|
||||
productStockFlowService.saveFlow(flow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -310,7 +316,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
return;
|
||||
}
|
||||
flow.setAfterNumber(NumberUtil.toBigDecimal(entity.getStockNumber()));
|
||||
productStockFlowMapper.insert(flow);
|
||||
productStockFlowService.saveFlow(flow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -331,6 +337,12 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
String type = param.getType();
|
||||
Long id = param.getId();
|
||||
Integer isSale = param.getIsSale();
|
||||
String sensitiveOperation = "";
|
||||
if (isSale == 1) {
|
||||
sensitiveOperation = "上架";
|
||||
} else {
|
||||
sensitiveOperation = "下架";
|
||||
}
|
||||
if (ProductIsSaleTypeEnum.SKU.value().equals(type)) {
|
||||
ProdSku prodSku = prodSkuMapper.selectOneById(id);
|
||||
if (prodSku == null) {
|
||||
|
|
@ -346,6 +358,9 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
.eq(Product::getShopId, shopId)
|
||||
.update();
|
||||
}
|
||||
Long productId = prodSku.getProductId();
|
||||
Product product = mapper.selectOneById(productId);
|
||||
sensitiveOperation = sensitiveOperation + "商品:" + product.getName() + " 规格:" + prodSku.getSpecInfo();
|
||||
} else if (ProductIsSaleTypeEnum.PRODUCT.value().equals(type)) {
|
||||
UpdateChain.of(Product.class)
|
||||
.set(Product::getIsSale, isSale)
|
||||
|
|
@ -357,10 +372,10 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
.eq(ProdSku::getProductId, id)
|
||||
.eq(ProdSku::getShopId, shopId)
|
||||
.update();
|
||||
Product product = mapper.selectOneById(id);
|
||||
sensitiveOperation = sensitiveOperation + "商品:" + product.getName();
|
||||
}
|
||||
|
||||
prodSkuMapper.selectOneById(id);
|
||||
|
||||
sensitiveOperationService.send(sensitiveOperation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -438,7 +453,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
flow.setInOutType(InOutTypeEnum.OUT.value());
|
||||
flow.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
|
||||
flow.setImgUrls(JSON.toJSONString(param.getImgUrls()));
|
||||
productStockFlowMapper.insert(flow);
|
||||
productStockFlowService.saveFlow(flow);
|
||||
// 如果绑定了耗材,则同步更新耗材库存
|
||||
List<ProdConsRelationDTO> consList = prodConsRelationMapper.selectListByProdId(param.getProductId());
|
||||
if (CollUtil.isEmpty(consList)) {
|
||||
|
|
@ -465,6 +480,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
|
|||
entity.setImgUrls(JSON.toJSONString(param.getImgUrls()));
|
||||
entity.setRemark("【商品报损,自动报损相关耗材】" + StrUtil.nullToDefault(param.getRemark(), ""));
|
||||
consStockFlowMapper.insert(entity);
|
||||
consStockFlowService.saveFlow(entity);
|
||||
consInfo.setStockNumber(entity.getAfterNumber());
|
||||
UpdateChain.of(ConsInfo.class)
|
||||
.set(ConsInfo::getStockNumber, consInfo.getStockNumber())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package com.czg.service.product.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.czg.product.entity.Product;
|
||||
import com.czg.product.entity.ProductStockFlow;
|
||||
import com.czg.product.service.ProductStockFlowService;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.product.mapper.ConsInfoMapper;
|
||||
import com.czg.service.product.mapper.ProductMapper;
|
||||
import com.czg.service.product.mapper.ProductStockFlowMapper;
|
||||
import com.czg.service.product.util.WxAccountUtil;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品库存流水服务实现类
|
||||
*
|
||||
* @author tankaikai
|
||||
* @since 2025-03-14 15:45
|
||||
*/
|
||||
@Service
|
||||
public class ProductStockFlowServiceImpl extends ServiceImpl<ProductStockFlowMapper, ProductStockFlow> implements ProductStockFlowService {
|
||||
|
||||
@Resource
|
||||
private ProductMapper productMapper;
|
||||
@Resource
|
||||
private ConsInfoMapper consInfoMapper;
|
||||
@Resource
|
||||
private WxAccountUtil wxAccountUtil;
|
||||
|
||||
@Override
|
||||
public void saveFlow(ProductStockFlow entity) {
|
||||
mapper.insert(entity);
|
||||
Long shopId = entity.getShopId();
|
||||
BigDecimal afterNumber = entity.getAfterNumber();
|
||||
Product product = productMapper.selectOneById(entity.getProductId());
|
||||
String shopName = StpKit.USER.getShopName();
|
||||
BigDecimal warnLine = Convert.toBigDecimal(product.getWarnLine());
|
||||
// 库存小于警告值,发送消息提醒
|
||||
if (NumberUtil.isLess(afterNumber, warnLine)) {
|
||||
List<String> openIdList = consInfoMapper.findOpenIdList(shopId, "pro");
|
||||
if (CollUtil.isEmpty(openIdList)) {
|
||||
return;
|
||||
}
|
||||
String conName = StrUtil.format("{}数量<预警值{}", product.getName(), warnLine);
|
||||
ThreadUtil.execAsync(() -> {
|
||||
openIdList.parallelStream().forEach(openId -> {
|
||||
wxAccountUtil.sendStockMsg(shopName, conName, afterNumber, openId);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.czg.service.product.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import com.czg.product.service.SensitiveOperationService;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.service.product.mapper.ConsInfoMapper;
|
||||
import com.czg.service.product.util.WxAccountUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 敏感操作ServiceImpl
|
||||
*
|
||||
* @author tankaikai
|
||||
* @since 2025-03-17 11:47
|
||||
*/
|
||||
@Service
|
||||
public class SensitiveOperationServiceImpl implements SensitiveOperationService {
|
||||
|
||||
@Resource
|
||||
private ConsInfoMapper consInfoMapper;
|
||||
@Resource
|
||||
private WxAccountUtil wxAccountUtil;
|
||||
|
||||
@Override
|
||||
public void send(String operationDesc) {
|
||||
Long shopId = StpKit.USER.getShopId(0L);
|
||||
String userName = StpKit.USER.getAccount();
|
||||
List<String> openIdList = consInfoMapper.findOpenIdList(shopId, "ope");
|
||||
if (CollUtil.isEmpty(openIdList)) {
|
||||
return;
|
||||
}
|
||||
ThreadUtil.execAsync(() -> {
|
||||
wxAccountUtil.sendOperationMsg(openIdList, userName, operationDesc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.czg.service.product.util;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.czg.service.RedisService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 微信公众号消息推送
|
||||
*
|
||||
* @author tankaikai
|
||||
* @since 2025-03-14 14:56
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WxAccountUtil {
|
||||
|
||||
@Value("${wx.ysk.appId}")
|
||||
private String appId = "wx212769170d2c6b2a";
|
||||
@Value("${wx.ysk.secrete}")
|
||||
private String secrete = "8492a7e8d55bbb1b57f5c8276ea1add0";
|
||||
@Value("${wx.ysk.operationMsgTmpId}")
|
||||
private String operationMsgTmpId;
|
||||
@Value("${wx.ysk.warnMsgTmpId}")
|
||||
private String warnMsgTmpId;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private RedisService redisService;
|
||||
|
||||
static LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
linkedHashMap.put("40001", "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40003", "不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID");
|
||||
linkedHashMap.put("40014", "不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口");
|
||||
linkedHashMap.put("40037", "不合法的 template_id");
|
||||
linkedHashMap.put("43101", "用户未订阅消息");
|
||||
linkedHashMap.put("43107", "订阅消息能力封禁");
|
||||
linkedHashMap.put("43108", "并发下发消息给同一个粉丝");
|
||||
linkedHashMap.put("45168", "命中敏感词");
|
||||
linkedHashMap.put("47003", "参数错误");
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
String accessToken = Convert.toStr(redisService.get("accessToken"));
|
||||
accessToken = "90_OzMitqhQZ1EPbaqFqGiaCeIhCWCPerSs43dTSNVdriltnbu6F-13Yao0ByELKKP50LtyFo2Kw9xnjivQhNagYqQoJy_vsqP7Nk5l0gfBfOBZDqrlKSFyKKiiIVoBPScAJASGJ";
|
||||
if (StrUtil.isNotEmpty(accessToken)) {
|
||||
return accessToken;
|
||||
}
|
||||
String resp = HttpUtil.get(StrUtil.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", appId, secrete));
|
||||
JSONObject respInfo = JSONObject.parseObject(resp);
|
||||
if (!respInfo.containsKey("access_token")) {
|
||||
log.warn("公众号获取token失败, 响应内容: {}", resp);
|
||||
throw new RuntimeException(resp);
|
||||
}
|
||||
accessToken = respInfo.getString("access_token");
|
||||
int expiresIn = respInfo.getInteger("expires_in");
|
||||
redisService.set("accessToken", accessToken, expiresIn - 10);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public JSONObject sendTemplateMsg(String templateId, String toUserOpenId, Map<String, Object> data) {
|
||||
log.info("开始发送微信模板消息, 接收用户openId: {}, 消息数据: {}", toUserOpenId, data);
|
||||
String accessToken = getAccessToken();
|
||||
|
||||
JSONObject object1 = new JSONObject();
|
||||
|
||||
object1.put("template_id", templateId);
|
||||
object1.put("touser", toUserOpenId);
|
||||
object1.put("data", data);
|
||||
|
||||
String response = HttpRequest.post("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".concat(accessToken)).body(object1.toString()).execute().body();
|
||||
log.info("微信模板消息发送成功,响应内容:{}", response);
|
||||
JSONObject resObj = JSONObject.parseObject(response);
|
||||
if (ObjectUtil.isNotEmpty(resObj) && ObjectUtil.isNotNull(resObj) && "0".equals(resObj.get("errcode") + "")) {
|
||||
return resObj;
|
||||
}
|
||||
|
||||
throw new RuntimeException(linkedHashMap.getOrDefault(resObj.get("errcode") + "", "未知错误"));
|
||||
}
|
||||
|
||||
public void sendOperationMsg(List<String> openIdList, String userName, String operationDesc) {
|
||||
openIdList.forEach(openId -> {
|
||||
Map<String, Object> data = new HashMap<String, Object>() {{
|
||||
put("thing19", new HashMap<String, Object>() {{
|
||||
put("value", userName);
|
||||
}});
|
||||
put("thing8", new HashMap<String, Object>() {{
|
||||
put("value", operationDesc);
|
||||
}});
|
||||
put("time21", new HashMap<String, Object>() {{
|
||||
put("value", DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss"));
|
||||
}});
|
||||
}};
|
||||
log.info("开始发送敏感操作消息, 接收用户openId: {}, 操作用户: {}, 操作描述: {}", openId, userName, operationDesc);
|
||||
try {
|
||||
sendTemplateMsg(operationMsgTmpId, openId, data);
|
||||
} catch (Exception e) {
|
||||
log.error("发送失败, openId: {}, 响应: {}", openId, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void sendStockMsg(String shopName, String productName, BigDecimal stockNum, String openId) {
|
||||
String stockNumStr = stockNum.toPlainString();
|
||||
Map<String, Object> data = new HashMap<String, Object>() {{
|
||||
put("thing22", new HashMap<String, Object>() {{
|
||||
put("value", shopName);
|
||||
}});
|
||||
put("thing4", new HashMap<String, Object>() {{
|
||||
put("value", productName);
|
||||
}});
|
||||
put("number5", new HashMap<String, Object>() {{
|
||||
put("value", stockNumStr);
|
||||
}});
|
||||
}};
|
||||
log.info("开始发送库存预警消息, 接收用户openId: {}, 消息数据: {}", openId, data);
|
||||
try {
|
||||
sendTemplateMsg(warnMsgTmpId, openId, data);
|
||||
} catch (Exception e) {
|
||||
log.error("发送失败, openId:{}, msg: {}", openId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,4 +3,12 @@
|
|||
|
||||
<mapper namespace="com.czg.service.product.mapper.ConsInfoMapper">
|
||||
|
||||
<select id="findOpenIdList" resultType="java.lang.String">
|
||||
select
|
||||
open_id
|
||||
from tb_shop_push_open_id
|
||||
where shop_id = #{shopId}
|
||||
and status = 1
|
||||
and type_info like concat('%', #{type}, '%')
|
||||
</select>
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue