292 Commits
v4.3 ... master

Author SHA1 Message Date
pikachu1995@126.com
b5b1c38f71 docs(readme): 更新README文档链接和社区支持信息
- 移除了聚合版相关说明
-修正了部署文档的链接地址
- 更新了社区支持部分,添加在线客服和微信群信息
- 调整了文档中的联系方式展示方式
2025-10-20 14:49:54 +08:00
pikachu1995@126.com
e1f834313a feat(search): 新增ES商品索引管理功能
- 添加删除下架商品索引接口
- 实现清理无效SKU索引逻辑
- 增加商品缓存生成功能
- 扩展商品搜索服务方法
-优化商品详情缓存时间配置
- 新增ES删除参数传输对象EsDeleteDTO
2025-10-20 13:35:00 +08:00
pikachu1995@126.com
dedcc0a556 refactor(payment): 统一使用标准库进行URL编解码
- 将支付宝插件中的Hutool URLEncoder替换为java.net.URLEncoder- 将支付宝插件中的Hutool URLDecoder替换为java.net.URLDecoder
- 在微信支付插件中统一使用标准库进行URL编解码
- 处理UnsupportedEncodingException异常情况- 更新相关注释和日志信息以反映变更
2025-10-11 18:50:44 +08:00
pikachu1995@126.com
33fcce1cc9 refactor(core):优化字符串比较逻辑以提高代码健壮性
- 将多个equals方法调用改为常量在前的写法,避免空指针异常
- 统一了所有字符串比较的格式,增强代码可读性和一致性- 修复潜在的NPE风险,提升系统稳定性
2025-10-11 18:19:52 +08:00
pikachu1995@126.com
85c8c5d021 style(logistics): 格式化顺丰插件代码- 移除了多余的@Component注解- 统一了if语句中的空格格式
- 调整了方法参数间的空格
- 修正了对象创建时的空格问题
- 规范了变量声明时的空格- 优化了代码缩进和换行- 修正了方法调用时的空格问题
- 统一了布尔值比较的格式
- 修正了JSON数据处理时的空格- 优化了代码注释的格式- 修正了sendPost方法中的工具类调用方式
2025-10-11 18:10:14 +08:00
pikachu1995@126.com
2ab2c4ff5d feat(sms): 华为短信插件信任所有HTTPS证书- 在华为短信插件中添加X509TrustManager实现
- 覆盖checkClientTrusted方法以跳过客户端证书验证
- 覆盖checkServerTrusted方法以跳过服务端证书验证- 实现getAcceptedIssuers方法返回null以接受所有颁发者
- 为所有新增方法添加@Override注解确保正确重写

fix(im): 补充IM对话服务实现注解

-为getTalkByUser方法添加@Override注解

fix(member): 补充会员服务实现注解

- 为modifyPass方法添加@Override注解

fix(promotion): 补充促销服务实现注解- 为getGoodsSkuPromotionMap方法添加@Override注解
- 为wrapperPromotionMapList方法添加@Override注解

fix(system): 补充地区服务实现注解- 为updateById方法添加@Override注解
-为save方法添加@Override注解

fix(logistics): 补充顺丰物流插件实现注解

- 为createOrder方法添加@Override注解
2025-10-11 18:07:39 +08:00
OceansDeep
feff49c79b !368 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg
Merge pull request !368 from OceansDeep/pg
2025-10-11 02:51:24 +00:00
misworga831
c813ed0062 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2025-10-11 11:49:51 +09:00
misworga831
f658d43a5a fix: 添加空指针检查以优化日志记录逻辑 (pg)
- 在日志切面中为方法返回值和请求对象增加空指针检查,确保稳定性。
- 调整日志记录逻辑以避免潜在异常。
2025-10-11 11:49:36 +09:00
chc
fe0b9c37a4 获取订单货物售后数量调整,以防止申请售后未审核依然取消订单 2025-09-24 15:20:19 +08:00
chc
28cc0617c3 获取订单货物售后数量调整,以防止申请售后未审核依然取消订单 2025-09-23 18:06:40 +08:00
chc
f2cb6b56ea 商品审核swagger 2025-09-23 13:31:11 +08:00
chc
81b7da07f4 sql增加 2025-09-12 16:53:45 +08:00
OceansDeep
db3f775c50 !367 修复商品索引无效促销活动清理逻辑
Merge pull request !367 from 羚羊跳/N/A
2025-09-11 11:47:30 +00:00
羚羊跳
93a45319ac update framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java.
fix: 修复商品索引无效促销活动清理逻辑,方法(executeCleanInvalidPromotions)
- 修复分页查询起始索引错误,从1改为0,避免跳过第一页数据
- 修复促销活动清洗后未同步更新到索引的问题,清洗后的promotionMap需重新序列化并设置回goodsIndex

Signed-off-by: 羚羊跳 <zxc2399897@vip.qq.com>
2025-09-11 08:49:49 +00:00
pikachu1995@126.com
7627614284 feat(order): 添加订单数量统计功能
- 新增 OrderNumVO 类用于订单数量统计
- 在 OrderService 接口中添加 getOrderNumVO 方法
- 在 OrderServiceImpl 类中实现 getOrderNumVO 方法
- 在 OrderMapper 接口中添加 getOrderNumVO SQL 查询
- 在 OrderManagerController 和 OrderStoreController 中添加获取订单数量的 API 接口- 优化物流公司的获取方式,
2025-08-29 11:37:41 +08:00
pikachu1995@126.com
ca5887028a feat(order): 添加订单数量统计功能
- 新增 OrderNumVO 类用于订单数量统计
- 在 OrderService 接口中添加 getOrderNumVO 方法
- 在 OrderServiceImpl 类中实现 getOrderNumVO 方法
- 在 OrderMapper 接口中添加 getOrderNumVO SQL 查询
- 在 OrderManagerController 和 OrderStoreController 中添加获取订单数量的 API 接口- 优化物流公司的获取方式,
2025-08-28 11:22:32 +08:00
pikachu1995@126.com
1a3736fd29 refactor(framework): 优化代码中的空值判断和字符串处理- 使用 CollUtil.isNotEmpty() 替代空集合判断
- 使用 CharSequenceUtil.isNotEmpty() 替代空字符串判断
- 使用 Objects.nonNull() 替代对象非空判断- 优化部分代码结构以提高可读性
2025-08-28 09:34:39 +08:00
pikachu1995@126.com
09d00260f6 feat(goods): 添加商品数量统计接口并优化商品查询
- 新增 getGoodsNumVO 接口,用于获取不同状态的商品数量统计
- 在 GoodsSearchParams 中添加 goodsStatus 字段,用于筛选商品状态
- 修改 queryByParams 方法,支持按商品状态进行筛选
- 新增 GoodsNumVO 类,用于返回商品数量统计结果
-优化商品查询性能,通过聚合查询一次性获取所有状态的商品数量
2025-08-27 16:15:56 +08:00
pikachu1995@126.com
2e019ef933 feat(order): 优化订单搜索功能
- 在订单查询中增加用户
2025-08-27 10:12:50 +08:00
pikachu1995@126.com
6acc99c7cf perf(es): 优化品牌聚合查询和网络连接配置
-增加 connectionRequestTimeout 配置,设置为 1000 毫秒
-调整 maxConnPerRoute 为 50,maxConnTotal为 200
-限制 terms 聚合 size,防止内存压力
- 优化品牌聚合查询逻辑,提高查询效率和准确性
2025-08-26 19:33:15 +08:00
pikachu1995@126.com
b57e7813d6 feat(statistics): 添加营业概览统计功能
- 新增营业概览、收款构成、营业构成等统计接口和页面
- 实现订单数、营业额、优惠金额等统计指标
- 添加虚拟发货的物流方式
- 优化订单和充值相关查询方法
2025-08-26 17:32:16 +08:00
OceansDeep
09e6f4a1a1 !366 docs: 为 README.md 中的徽章添加链接 (pg)
Merge pull request !366 from OceansDeep/pg
2025-06-20 02:19:44 +00:00
misworga831
872bb220dc docs: 为 README.md 中的徽章添加链接 (pg) 2025-06-20 10:18:09 +08:00
misworga831
2498e57600 docs: 重构README.md,完善项目介绍与功能说明 (pg)
- 更新项目标题为“Lilishop B2B2C 商城系统”
- 新增GitHub、Gitee、Spring Boot等徽章展示
- 重新组织项目简介,突出技术栈和架构特点
- 详细列出平台管理端与商家端功能模块
- 优化快速开始、数据库初始化及在线演示说明
- 补充技术架构图及前后端技术栈说明
- 增加开源协议与商业授权说明
- 丰富社区支持与贡献内容介绍
- 调整图片大小及格式,提升文档可读性
2025-06-20 10:14:22 +08:00
misworga831
cfbb673387 feat: 添加项目概述、技术架构、开发指南和部署指南文档 2025-06-20 09:57:16 +08:00
chc
05db6501f7 优惠券价格未渲染,但是优惠券却使用问题 2025-06-03 10:52:01 +08:00
chc
a5aa090f4a 使用优惠券时的价格获取方式修改 2025-05-28 10:02:13 +08:00
chc
09aae17a02 渲染优惠券时,增加一次遍历,确保优惠券一定可以正常使用 2025-05-27 19:31:13 +08:00
chc
7f5739f89b 查询bill时间参数错误问题 2025-05-16 14:36:27 +08:00
chc
131d4eb156 config下的application中服务IP地址更换为127.0.0.1 2025-05-08 14:26:03 +08:00
chc
ffa0b0db30 h5支付没有传入sceneInfo问题 2025-04-02 11:00:03 +08:00
chc
7bd0084802 Merge remote-tracking branch 'origin/master' 2025-04-01 15:25:13 +08:00
pikachu1995@126.com
d88762f3ab refactor(connect): 修改微信连接器用户名获取逻辑
- 在 BaseAuthWeChatPCRequest 和 BaseAuthWeChatRequest 中,将用户名获取方式从 nickname 改为 unionid- 更新 Kuaidi100Plugin 中的物流订单请求逻辑,增加多个电子面单相关参数- 在 StoreLogistics 和 StoreLogisticsCustomerDTO 中添加电子面单客户账户名称字段
2025-03-17 15:00:06 +08:00
pikachu1995@126.com
ebdbed6d08 docs(README): 更新交流群信息并优化文档内容
- 移除已满的 QQ 交流群信息
- 添加微信群交流信息- 删除重复的商业授权联系方式- 简化交流群部分的信息
2025-03-04 11:09:06 +08:00
Chopper711
efdf505e63 fix: 修复ServiceException类中异常消息拼接问题 (master)
修改ServiceException类中默认异常消息的设置方式,使用
ResultCode.ERROR.message()代替DEFAULT_MESSAGE。
2025-02-21 11:11:05 +08:00
Chopper711
98f25179d3 fix: 异常消息拼接问题处理 2025-02-20 10:53:40 +08:00
chc
9a0c7dd73d Merge remote-tracking branch 'origin/master' 2025-02-20 10:41:07 +08:00
pikachu1995@126.com
0cf464e549 feat(payment): 微信支付支持公钥和证书两种验证方式- 在 WechatPaymentSetting 中添加 publicType 字段,用于选择验证方式
- 修改 WechatPlugin 中的支付、退款等方法,支持两种验证方式
- 新增 getPublicKeyConfig 和 getCertificateConfig 方法,分别用于获取公钥和证书配置
- 优化退款通知处理逻辑,使用 NotificationParser 进行验证和解析
2025-02-11 15:42:35 +08:00
chc
2e6ddeafdd 优惠券删除时,只有指定商品时,才传递scopeId。 2025-01-22 15:48:10 +08:00
chc
633b94c375 获取用户信息时判断用户是否已禁用 2025-01-20 17:09:24 +08:00
chc
6441018d95 获取用户信息时判断用户是否已禁用 2025-01-20 16:38:10 +08:00
pikachu1995@126.com
e706431df5 refactor(payment): 重构微信支付模块
- 更新微信支付相关工具类和接口实现
- 新增微信支付配置项
- 优化微信支付流程和参数处理
- 引入微信支付官方SDK
2025-01-08 16:13:39 +08:00
pikachu1995@126.com
db1a3566ae 初始化 2024-12-27 14:07:35 +08:00
pikachu1995@126.com
9225b4ff10 refactor(payment): 重构微信支付私钥处理逻辑
- 将方法参数从 keyPath 修改为 key,移除对文件路径的依赖
- 更新方法签名和文档注释,以适应新的参数
- 删除 WechatPaymentSetting 中未使用的证书字段
2024-12-27 11:02:57 +08:00
chc
586a507bb1 订单分包裹发货问题修复 2024-12-25 10:01:47 +08:00
OceansDeep
034f29c734 !362 fix & improve
Merge pull request !362 from OceansDeep/pg
2024-12-12 08:54:57 +00:00
misworga831
478dd1d201 fix: 添加订单和商品存在性检查以改善评价流程 (pg)
- 在MemberEvaluationServiceImpl中引入订单和商品存在性检查。
- 当订单或商品不存在时,抛出异常以避免错误日志的产生。
- 同时,在OrderEveryDayTaskExecute中捕获异常时更新评价状态。
2024-12-12 16:53:20 +08:00
misworga831
554aed024c fix: 添加会员评价时商品存在性检查以修复潜在的空指针异常 (pg)
- 在会员评价时获取商品信息后增加商品存在性检查,避免空指针异常。当商品不存在时,记录错误日志并返回null。
2024-12-12 16:08:13 +08:00
OceansDeep
b2e0fe1de4 !361 fix & improve
Merge pull request !361 from OceansDeep/pg
2024-12-11 08:20:07 +00:00
misworga831
b69a558df0 fix: 优化积分商品,修复促销时间段重叠问题 (pg)
- 添加方法以检查促销时间段是否重叠
- 更新文档注释以提高可读性
- 使用@NotNull注解来增强空值检查
2024-12-11 16:18:58 +08:00
misworga831
2f180a73ed fix: 优化积分商品,移除异步删除索引 (pg)
- 重构deleteByQuery方法,移除异步调用,改为同步处理。
添加了异常处理以记录删除索引时的错误日志。
2024-12-11 16:18:37 +08:00
chc
82f3223a03 订单取消时,将订单货物设置为全部退款 2024-12-11 14:53:47 +08:00
chc
cfe48d539b 拼团问题处理 2024-11-06 18:19:02 +08:00
OceansDeep
8ef00ae285 !360 fix & improve
Merge pull request !360 from pg
2024-10-31 08:47:11 +00:00
misworga831
ed032653ee fix: 优化积分商品活动可以同一时间创建多个 2024-10-31 16:45:38 +08:00
OceansDeep
9108a1585c !359 fix & improve
Merge pull request !359 from pg
2024-10-31 03:50:57 +00:00
misworga831
d4855b702b fix: 增加添加验证码时,检查验证码是否可以正常生成问题 2024-10-31 11:47:41 +08:00
chc
40822ca05b 砍价活动,当随机金额超过剩余砍价金额时,返回剩余金额,防止超过 2024-10-24 14:04:39 +08:00
chc
4dac80f084 砍价活动时间校验问题导致可以创建多个砍价活动 2024-10-23 17:51:31 +08:00
chc
8609216ef5 重复砍价商品导致获取商品信息错误问题 2024-10-23 11:18:26 +08:00
chc
9678c7f506 缺少事务导致发货消息发送失败问题处理 2024-10-18 15:56:23 +08:00
pikachu1995@126.com
63cdebdf1f 微信支付,微信小程序订单,推送发货信息 2024-10-15 10:17:09 +08:00
Chopper711
e89be8eb8c !358 优化 web-二维码登录 优化睡眠时间使用指数退避策略来延长每次轮询的时间间隔,减少CPU占用率
Merge pull request !358 from 铉清/N/A
2024-09-25 01:59:23 +00:00
铉清
3559971a8d 优化 web-二维码登录 优化睡眠时间使用指数退避策略来延长每次轮询的时间间隔,减少CPU占用率
Signed-off-by: 铉清 <13698617+xuanqings@user.noreply.gitee.com>
2024-09-21 16:17:53 +00:00
Chopper711
dc1d1a7e7e Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-09-18 15:40:02 +08:00
Chopper711
10ecce1e0d fix:修复代码与注释不匹配问题 2024-09-18 15:39:56 +08:00
chc
2520c27f77 Merge remote-tracking branch 'origin/master' 2024-09-18 15:16:35 +08:00
chc
63d684c972 订单增加部分发货状态。
部分发货无法申请售后
2024-09-18 15:16:28 +08:00
Chopper711
35739b64a3 fix: 部分字段重复问题处理 2024-09-18 14:20:13 +08:00
Chopper711
ddcda57a08 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-09-18 12:24:16 +08:00
Chopper711
16ae69ed06 fix: 脚本问题导致限流脚本无效,不确定是否是版本升级带来的问题。 2024-09-18 12:24:11 +08:00
Chopper711
fd067d8abf update DB/version4.3toMASTER.sql.
缺少标点符号导致无法一次性执行 sql

Signed-off-by: Chopper711 <1814994716@qq.com>
2024-09-18 02:09:02 +00:00
pikachu1995@126.com
42b3c72977 微信小程序发货信息录入参数错误,短信发送使用系统后台配置的短信CODE 2024-09-14 16:11:05 +08:00
OceansDeep
e34b4172b8 !356 fix & improve
Merge pull request !356 from OceansDeep/pg
2024-09-14 03:30:15 +00:00
misworga831
30d6ab64f8 fix & improvement 2024-09-14 11:29:39 +08:00
Chopper711
2e8c40bcf3 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-09-14 11:17:44 +08:00
Chopper711
f2385c3ace fix: 订单物流费用展示问题处理 2024-09-14 11:17:37 +08:00
OceansDeep
a45355b125 !355 fix & improve
Merge pull request !355 from OceansDeep/pg
2024-09-13 17:44:17 +00:00
misworga831
b3bb771066 fix: 修复可能会出现在购物车内多促销活动多商品的清空下的促销金额分配问题 2024-09-14 01:39:19 +08:00
OceansDeep
068c6b986e !354 fix & improve
Merge pull request !354 from OceansDeep/pg
2024-09-13 09:29:15 +00:00
misworga831
18e2c89891 fix: 修复批量更新商品配送模版,没有更新sku问题 2024-09-13 17:27:39 +08:00
OceansDeep
7b485753a6 !353 fix & improve
Merge pull request !353 from OceansDeep/pg
2024-07-26 02:33:51 +00:00
misworga831
8556d27173 fix & improvement 2024-07-26 10:31:39 +08:00
chc
d677aabbf4 判断手机号是否存在,如果存在抛出异常 2024-07-24 10:37:41 +08:00
pikachu1995@126.com
aaa0748409 删除文件夹需判断,优化删除逻辑。是否文件夹下存在图片。 2024-07-18 14:28:59 +08:00
pikachu1995@126.com
57ee156798 Merge branch 'master' of https://gitee.com/beijing_hongye_huicheng/lilishop 2024-07-18 13:51:44 +08:00
pikachu1995@126.com
5ae4d8b4fa 微信小程序-上传发货信息 2024-07-18 13:51:37 +08:00
OceansDeep
681327274c !352 fix & improve
Merge pull request !352 from OceansDeep/pg
2024-07-18 03:46:26 +00:00
misworga831
6ab2888fd0 fix: 修复部分情况下秒杀活动商品无法添加问题 2024-07-18 11:45:18 +08:00
pikachu1995@126.com
80a0e0e8c7 增加导出订单数据判断,升级poi版本
传递删除则使用字段内容
2024-07-07 15:55:11 +08:00
pikachu1995@126.com
80773e2d58 优化支付、短信的配置内容 2024-07-07 14:48:11 +08:00
pikachu1995@126.com
065bcc8a55 优化支付、短信的配置内容 2024-07-07 14:03:31 +08:00
pikachu1995@126.com
9398413999 添加qq群 2024-07-02 15:07:51 +08:00
pikachu1995@126.com
cf623f6756 优化店铺结算单、店铺流水生成规则。
优化分销申请逻辑,分销佣金不转入预存款
2024-07-02 14:55:19 +08:00
pikachu1995@126.com
1b316baac6 将微信支付的调用appid进行优化。 2024-06-28 09:08:32 +08:00
pikachu1995@126.com
4fc1d0e346 升级poi 版本 2024-06-27 17:13:33 +08:00
pikachu1995@126.com
159fe37120 支持店铺通过excel批量更新商品库存功能 2024-06-27 17:09:25 +08:00
pikachu1995@126.com
6f9486f065 优化订单导出功能。 2024-06-26 11:50:45 +08:00
Chopper711
94da71336a Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-06-18 09:55:21 +08:00
Chopper711
8c5ce396b2 fix: 2024-03-25被错误的修改一个问题,导致分销订单无法结算,当前 commit 修复问题并优化问题,解决可能出现的事务问题,采用一条 sql 执行 2024-06-18 09:55:15 +08:00
OceansDeep
0063820fdf !351 fix & improve
Merge pull request !351 from OceansDeep/pg
2024-06-17 10:41:51 +00:00
misworga831
fe80edebbd fix: 修复 精确发券 类型的优惠券活动重复领取问题 2024-06-17 18:41:02 +08:00
OceansDeep
6b59c1e643 !350 fix & improve
Merge pull request !350 from OceansDeep/pg
2024-06-17 09:10:44 +00:00
misworga831
1b52381c71 fix: 修复可能出现的手机号登录重复问题 2024-06-17 17:09:33 +08:00
OceansDeep
763519876e !349 fix & improve
Merge pull request !349 from OceansDeep/pg
2024-06-17 07:14:46 +00:00
misworga831
cede181bc0 fix: 生成索引时,店铺商品分类优惠券判断条件补全 2024-06-17 15:13:07 +08:00
Chopper711
cb776eb4c0 merge 2024-06-17 08:31:49 +08:00
Chopper711
8ed4f6eb3c fix: 极端情况下,结算单可能存在未能被同级的订单流水 2024-06-17 08:31:28 +08:00
OceansDeep
9cf97f3203 !348 fix & improve
Merge pull request !348 from OceansDeep/pg
2024-06-14 03:31:36 +00:00
misworga831
2a984b8c56 fix: 优化店铺优惠券商品分类生成 2024-06-14 11:27:54 +08:00
OceansDeep
22ecf48168 !347 fix & improve
Merge pull request !347 from OceansDeep/pg
2024-06-14 01:55:33 +00:00
misworga831
89422b2736 fix: 自动领取优惠券判定同时,将精准发券功能同时判定 2024-06-14 09:54:26 +08:00
Chopper711
bcd4bf4ff1 fix: 用户领取优惠券时,无法领取精准赠送的优惠券 2024-06-12 18:07:53 +08:00
OceansDeep
a263bc6301 !346 fix & improve
Merge pull request !346 from OceansDeep/pg
2024-06-04 06:44:23 +00:00
misworga831
27ae773e20 fix & improvement 2024-06-04 14:43:37 +08:00
Chopper711
085b61c2ab fix: 单个文件夹删除报错问题处理,pom.xml 配置编译运行环境指定,开发测试环境 jdk22 导致无法编译 1.8版本 jdk 问题。 2024-05-27 14:45:42 +08:00
Chopper711
c7682680ee fix:文件目录为空时无法删除文件报错 2024-05-27 10:12:50 +08:00
lele0521
a301432a87 fix: 修复判断订单是否可以售后接口异常 2024-05-27 08:53:43 +08:00
OceansDeep
526de18a42 !343 上传图片时,选择的目录为空时,生成的图片访问不了
Merge pull request !343 from imalasong/pr/1
2024-05-24 03:15:48 +00:00
OceansDeep
c940bf6cb2 !342 多了一个结束符号进行移除
Merge pull request !342 from 铉清/N/A
2024-05-24 03:15:02 +00:00
OceansDeep
a7643a6b93 !345 fix & improve
Merge pull request !345 from OceansDeep/pg
2024-05-23 07:40:45 +00:00
misworga831
24f6b9e80f fix: 修复拼单订单失败时,未付款订单没有取消问题。 2024-05-23 15:39:26 +08:00
lele0521
d349616957 fix: 过滤订单售后状态(砍价、积分、赠品订单不可进行售后) 2024-05-20 11:16:36 +08:00
imalasong
f7c9ba820d Merge remote-tracking branch 'origin/pr/1' into pr/1 2024-05-16 21:16:28 +08:00
imalasong
f99f59963a fix: 上传图片时,选择的目录为空时,生成的图片访问不了 2024-05-16 21:16:17 +08:00
imalasong
caaf9c74f3 fix: 上次图片时,选择的目录为空时,生成的图片访问不了 2024-05-16 21:14:41 +08:00
lele0521
076f47f5c4 fix: 回滚用户注销逻辑 2024-05-16 09:36:52 +08:00
lele0521
f0e4e4c2bc fix: 修改注销账号的实现方式 2024-05-16 08:56:25 +08:00
lele0521
c87fe45dfa fix: 修改注销账号的实现方式 2024-05-15 18:38:41 +08:00
lele0521
49fd03df7f fix: 修改注销账号的实现方式 2024-05-13 19:44:51 +08:00
lele0521
6deed61311 fix: 增加商家订单备注功能 2024-05-11 09:21:27 +08:00
Chopper711
6311767320 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-05-10 17:05:19 +08:00
Chopper711
2463d1b5bb 验证 TOKEN 2024-05-10 17:04:32 +08:00
chc
9d41ae3cae 分销订单立即结算功能注释,新版本逻辑必须通过定时任务处理 2024-05-07 11:13:56 +08:00
chc
2be8bf27bf 虚拟订单无法售后问题 2024-05-07 10:51:01 +08:00
铉清
2d052ffb1a 多了一个结束符号进行移除
Signed-off-by: 铉清 <13698617+xuanqings@user.noreply.gitee.com>
2024-04-19 05:17:44 +00:00
Chopper711
88ab071ac7 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-04-01 19:14:34 +08:00
Chopper711
bda72da679 fix: 足迹为空展示空信息问题,商品库存回滚数量错误问题 2024-04-01 19:14:28 +08:00
Lele
95e5ef2d33 !341 统计数据转换异常
Merge pull request !341 from Lele/dev
2024-03-29 03:29:19 +00:00
lele0521
48a294e7c6 fix: 统计数据转换异常 2024-03-29 11:28:24 +08:00
Chopper711
794fa63302 fix: 核销订单如果订单不存在则抛出异常 2024-03-28 15:36:56 +08:00
lele0521
cdb8e851a8 Merge branch 'master' of https://gitee.com/beijing_hongye_huicheng/lilishop 2024-03-27 15:16:39 +08:00
lele0521
f8ee3a8d15 fix: 统计数据转换异常 2024-03-27 15:16:26 +08:00
Lele
76ab997922 !340 修复会员基本信息显示null异常
Merge pull request !340 from Lele/dev
2024-03-27 01:03:02 +00:00
lele0521
97e7a130f1 fix: 修复会员基本信息显示null异常 2024-03-26 18:50:24 +08:00
Lele
2a96ad4af7 !339 修复商城问题文档中的bug
Merge pull request !339 from Lele/dev
2024-03-25 08:32:24 +00:00
lele0521
e1aee4a744 fix: 修复商城问题文档中的bug 2024-03-25 16:29:24 +08:00
lele0521
ecfe1524d4 fix: 修复商城问题文档中的bug 2024-03-25 16:26:21 +08:00
chc
42ea2dc986 订单后台收款后返回状态不对 2024-03-22 19:07:02 +08:00
Lele
230e2e7407 !336 【交易投诉】交由商家处理报错。
Merge pull request !336 from Lele/dev
2024-03-21 08:22:49 +00:00
lele0521
92639ba309 fix: 【交易投诉】交由商家处理报错。bug修复 2024-03-21 16:21:29 +08:00
Lele
19b3225312 !335 修复添加APP版本控制异常
Merge pull request !335 from Lele/dev
2024-03-21 05:54:53 +00:00
lele0521
e56c20469d fix: APP版本控制区分APP类型 2024-03-21 13:42:58 +08:00
Lele
a28eceaf61 !334 OSS资源管理中增加店铺名称搜索条件
Merge pull request !334 from Lele/dev
2024-03-19 10:03:16 +00:00
lele0521
1925c6d33c fix: OSS资源增加按店铺筛选 2024-03-19 17:55:11 +08:00
lele0521
fcac4002b1 fix: OSS资源增加按店铺筛选 2024-03-19 17:03:55 +08:00
lele0521
ed3a2ae9fc Merge branch 'master' into dev 2024-03-19 16:39:47 +08:00
lele0521
4955d2135b fix: OSS资源增加按店铺筛选 2024-03-19 16:39:36 +08:00
chc
9f1dacc1d4 方法名修改 2024-03-11 15:35:14 +08:00
lele0521
f0765a6627 fix: 修复分销申请逻辑 2024-03-07 11:23:50 +08:00
lele0521
a31a9fdfbd fix: 修复字段错误 2024-03-07 09:22:54 +08:00
Chopper711
fd2d0724a7 fix: token 过期后图片访问抛出一个未被捕获的异常问题处理,优化一个店铺审核的提示语 2024-02-01 15:37:59 +08:00
chc
82d7389092 Merge remote-tracking branch 'origin/master' 2024-01-29 18:33:53 +08:00
chc
a53eb3cfde 售后关闭功能同步 2024-01-29 18:33:47 +08:00
Lele
bd048d725c !333 增加默认退款金额显示
Merge pull request !333 from Lele/dev
2024-01-29 06:37:37 +00:00
lele0521
8e275613e1 fix: 增加默认退款金额显示 2024-01-29 14:35:19 +08:00
Lele
fc869a34e0 !332 fix: 修复分销提现异常
Merge pull request !332 from Lele/dev
2024-01-25 10:42:13 +00:00
lele0521
48c84acb7c fix: 分销提现bug修复 2024-01-25 18:39:27 +08:00
OceansDeep
40d40a344e !331 fix & improve
Merge pull request !331 from OceansDeep/pg
2024-01-25 08:46:48 +00:00
misworga831
d31257ba76 fix: 修复促销拼团订单取消后,活动结束后,检查成团没有筛选取消后的订单返还多余库存问题 2024-01-25 16:12:33 +08:00
chc
f0a80338da Merge remote-tracking branch 'origin/master' 2024-01-25 14:02:24 +08:00
chc
6b5ca5b337 分类缓存获取 2024-01-25 14:02:18 +08:00
OceansDeep
61f9d7812d !330 fix & improve
Merge pull request !330 from OceansDeep/pg
2024-01-24 10:54:33 +00:00
misworga831
d5eeafc554 fix: 优化虚拟商品不需要填写收货地址 2024-01-24 18:53:31 +08:00
misworga831
2175433444 fix: 优化取消订单返还促销商品库存 2024-01-24 18:52:43 +08:00
pikachu1995@126.com
cfbb1a1519 Merge branch 'master' of https://gitee.com/beijing_hongye_huicheng/lilishop 2024-01-24 14:54:29 +08:00
pikachu1995@126.com
1302ca6de4 优化结算单退款计算逻辑 2024-01-24 14:54:23 +08:00
chc
239b520939 Merge remote-tracking branch 'origin/master' 2024-01-24 11:32:43 +08:00
chc
56d9dbee5b 库存预警功能,预警为0不进行预警 2024-01-24 11:32:28 +08:00
OceansDeep
41decf1fa1 !329 fix & improve
Merge pull request !329 from OceansDeep/pg
2024-01-24 02:11:26 +00:00
misworga831
1d2cb5737d fix: 修复促销商品库存扣减问题 2024-01-24 10:10:43 +08:00
misworga831
a2496b267a fix: 优化商品图片获取 2024-01-24 10:10:11 +08:00
pikachu1995@126.com
178544fe2d 管理端支持下载结算单账单 2024-01-24 09:48:25 +08:00
pikachu1995@126.com
9e3b9c8f38 店铺结算单增加积分/砍价商品退款统计 2024-01-23 15:06:14 +08:00
OceansDeep
b439d8283f !328 fix & improve
Merge pull request !328 from OceansDeep/pg
2024-01-22 10:57:02 +00:00
misworga831
fb48bce38d fix: 优化商品图片地址存储结构,只保留图片地址,去除其他无效参数,优化商品图片规则,改为全部通用spu图片,可为第一个 规格项 单独设置一个图片。 2024-01-22 18:56:13 +08:00
pikachu1995@126.com
b189fbe238 腾讯COS图片路径缺少符号 2024-01-22 15:59:33 +08:00
OceansDeep
26c1c97442 !327 fix & improve
Merge pull request !327 from OceansDeep/pg
2024-01-22 02:55:04 +00:00
misworga831
ee8f5b6e1e fix: 优化商品图片地址存储结构,只保留图片地址,去除其他无效参数,优化商品图片规则,改为全部通用spu图片,可为第一个 规格项 单独设置一个图片。 2024-01-22 10:44:24 +08:00
misworga831
6755515b1f upgrade: 更新 hutool 和 mybatis plus 版本 2024-01-22 10:43:09 +08:00
Lele
074036c0b5 !326 退款状态功能增加字段
Merge pull request !326 from Lele/dev
2024-01-18 09:45:11 +00:00
lele0521
c7d0820eed Merge branch 'master' into dev
# Conflicts:
#	DB/version4.3toMASTER.sql
2024-01-18 17:42:37 +08:00
lele0521
97c0fdbf1d feat: 退款状态功能增加字段 2024-01-18 17:40:05 +08:00
chc
a962939db8 库存预警功能 2024-01-18 11:44:08 +08:00
Lele
a9802710ef !325 修复日志格式异常
Merge pull request !325 from Lele/dev
2024-01-17 10:30:54 +00:00
lele0521
aaaa0b1ebf Merge branch 'master' into dev 2024-01-17 18:06:13 +08:00
lele0521
f34d10146a fix: 修复日志异常 2024-01-17 17:07:20 +08:00
Lele
b875cbdda4 !324 补充提交部分退款售后逻辑
Merge pull request !324 from Lele/dev
2024-01-16 09:24:05 +00:00
lele0521
d80a3f7f48 fix: 补充提交部分退款售后逻辑 2024-01-16 17:18:28 +08:00
Lele
259946e78f !323 增加操作日志、新增会员昵称默认规则、增加退款状态
Merge pull request !323 from Lele/dev
2024-01-16 08:49:35 +00:00
lele0521
9c24adf77d fix: 补充提交部分退款售后逻辑 2024-01-16 16:45:27 +08:00
lele0521
4c47d4aba2 fix: 区分商家、管理端OSS资源文件夹的创建 2024-01-16 15:53:37 +08:00
lele0521
7f9061cfc5 fix: Merging dev 2024-01-16 11:29:03 +08:00
chc
085dad2107 库存预警功能回退 2024-01-16 10:10:07 +08:00
chc
055a7fffdb 首页暂不显示预警 2024-01-11 13:33:46 +08:00
chc
6cea6af59b 分包裹发货,库存预警 2024-01-11 13:33:13 +08:00
chc
6f0f8106e0 分包裹SQL 2024-01-08 19:15:07 +08:00
lele0521
c9a0ce7985 feat: 增加退款状态(部分退款、全部退款) 2024-01-05 17:29:34 +08:00
lele0521
b40533c553 feat: 增加操作日志(商品、用户、促销相关修改操作) 2024-01-04 18:10:57 +08:00
lele0521
e2fff23080 feat: 新增会员昵称默认规则 2024-01-04 17:36:39 +08:00
Chopper711
72482ee395 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2023-12-18 08:40:31 +08:00
Chopper711
5366034556 fix: 禁用演示站点地区维护功能费 2023-12-18 08:40:24 +08:00
OceansDeep
484e2a2318 !320 fix & improve
Merge pull request !320 from OceansDeep/pg
2023-12-05 06:23:34 +00:00
misworga831
0873fba7df fix: 优化商品参数聚合搜索 2023-12-05 14:21:55 +08:00
OceansDeep
4aa6f864f0 !319 fix & improve
Merge pull request !319 from OceansDeep/pg
2023-12-05 06:07:08 +00:00
misworga831
183b2ba30c fix: 优化商品参数聚合搜索 2023-12-05 11:48:56 +08:00
lele0521
071f15347e feat: 新增会员绑定手机号功能 2023-12-04 16:05:17 +08:00
OceansDeep
71ed58e665 !318 fix & improve
Merge pull request !318 from OceansDeep/pg
2023-11-23 03:17:21 +00:00
misworga831
50c7e20ba6 fix: 修复活动优惠券,没有生成商品促销信息问题 2023-11-23 11:16:08 +08:00
misworga831
876b801fa1 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-11-21 10:15:06 +08:00
misworga831
bd42b88b71 fix: 优化商品索引不存在,搜索商品报错 2023-11-21 10:14:59 +08:00
Chopper711
b2dfb3abe2 update README.md.
Signed-off-by: Chopper711 <1814994716@qq.com>
2023-11-08 08:18:00 +00:00
Chopper711
164f726222 fix: 将订单创建消息发送节点从业务类内部调整为事务提交节点 2023-11-08 09:15:06 +08:00
misworga831
24cdf5cf00 fix: 商品评价更新后,清除sku缓存不全问题。获取有图评价过滤条件缺少状态问题 2023-11-06 17:50:24 +08:00
OceansDeep
93cb9da558 !317 fix & improve
Merge pull request !317 from OceansDeep/pg
2023-11-06 07:21:01 +00:00
misworga831
66c6bfa0fb fix: 优化自定义分词插入(修改为插入时,遇到唯一索引错误跳过) 2023-11-06 15:19:53 +08:00
Chopper711
edd93a6ad4 update README.md.
Signed-off-by: Chopper711 <1814994716@qq.com>
2023-11-03 07:05:29 +00:00
OceansDeep
ea1626a2ef !315 fix & improve
Merge pull request !315 from OceansDeep/pg
2023-10-24 08:30:44 +00:00
misworga831
1f3b7f21bb fix: 优化更新商品评价为所有Sku的评价 2023-10-24 16:29:04 +08:00
OceansDeep
e6fe055cd5 !314 fix & improve
Merge pull request !314 from OceansDeep/pg
2023-10-24 06:42:40 +00:00
misworga831
1422b6fdbc fix: 优化更新商品评价为所有Sku的评价 2023-10-24 14:41:41 +08:00
misworga831
cfb70eff8a fix: 优化特殊情况下出现的商品数据无商品分类查询报错的问题 2023-10-24 08:27:02 +08:00
misworga831
dfe6085432 fix: buyer端增加获取sku接口 2023-10-23 18:13:54 +08:00
OceansDeep
b8e8db0e36 !313 fix & improve
Merge pull request !313 from OceansDeep/pg
2023-10-23 06:22:27 +00:00
misworga831
fb2e41422b fix: 优化虚拟订单收货地址校验 2023-10-23 14:21:33 +08:00
OceansDeep
f3db8f2e45 !312 fix & improve
Merge pull request !312 from OceansDeep/pg
2023-10-23 03:55:33 +00:00
misworga831
6bc2c0adf1 fix: 优化虚拟订单收货地址校验 2023-10-23 11:54:53 +08:00
OceansDeep
df75e08d22 !311 fix & improve
Merge pull request !311 from OceansDeep/pg
2023-10-23 03:46:10 +00:00
misworga831
b2ccdad483 fix: 去除虚拟订单收货地址校验 2023-10-23 11:45:00 +08:00
misworga831
3d6eb7b4fa fix: 去除虚拟订单收货地址校验 2023-10-23 11:42:22 +08:00
Chopper711
96cfb8a8ce !305 fix bug: 小程序分享二维码,请求参数类型应为Object
Merge pull request !305 from GeekMo/master
2023-10-19 01:54:44 +00:00
OceansDeep
4a4672c80a !310 fix & improve
Merge pull request !310 from OceansDeep/pg
2023-10-18 02:54:03 +00:00
misworga831
a0c1cd1019 fix: 修复订单未完成,售后完成扣减积分问题 2023-10-18 10:53:22 +08:00
OceansDeep
ed20d1abbb !309 fix & improve
Merge pull request !309 from OceansDeep/pg
2023-10-17 09:13:33 +00:00
misworga831
0ebb9c01f8 fix: 优化售后完成扣减积分问题 2023-10-17 17:11:25 +08:00
OceansDeep
dd3176f841 !308 fix & improve
Merge pull request !308 from OceansDeep/pg
2023-10-17 01:07:36 +00:00
misworga831
a910a97719 fix: 优化更新商品索引评论条件 2023-10-17 09:05:33 +08:00
OceansDeep
cdf8ab3a3c !307 fix & improve
Merge pull request !307 from OceansDeep/pg
2023-10-17 00:27:50 +00:00
misworga831
074e4ed5b7 fix: 优化更新商品索引评论,改为实时统计spu的评论总量 2023-10-17 08:26:34 +08:00
OceansDeep
9e43d186d7 !306 fix & improve
Merge pull request !306 from OceansDeep/pg
2023-10-16 10:36:21 +00:00
misworga831
8b61d40ff8 fix: 优化部分优惠券初始化及搜索条件 2023-10-16 18:34:55 +08:00
misworga831
f9b3001eb3 fix: 优化售后搜索条件 2023-10-16 18:34:21 +08:00
misworga831
f1ec44869d Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-10-16 16:40:48 +08:00
misworga831
72341acb57 fix: 优化楼层装修代码 2023-10-16 16:40:37 +08:00
misworga831
fa696e6c11 fix: 优化更新商品评论,改为实时统计评论总量 2023-10-16 16:40:20 +08:00
misworga831
b30909942a fix: 优化im搜索 2023-10-16 11:49:42 +08:00
misworga831
7c64e1f11d fix: 优化更新商品索引,销量,评论方法 2023-10-16 11:48:52 +08:00
Chopper711
d1c6988d0f fix: 定时精准发送优惠券被立即发送问题处理。自动领取优惠券时会触发精准发券全平台会员 2023-10-13 15:35:47 +08:00
misworga831
efe0bd4bad Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-10-11 10:07:25 +08:00
Milo
7523481be0 小程序参数有其它非String类型的值需要设置
例如check_path:false
2023-10-05 19:15:34 +08:00
Chopper711
23182106b9 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2023-09-27 18:43:15 +08:00
Chopper711
515ffbcf0a fix: 下单人数统计问题处理 2023-09-27 18:43:10 +08:00
misworga831
8f52a1e2a2 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-09-27 09:20:03 +08:00
misworga831
bdd9045fc1 fix: 优化商品分类、售后及拼团相关 2023-09-27 09:16:49 +08:00
pikachu1995@126.com
518616483b Merge branch 'master' of https://gitee.com/beijing_hongye_huicheng/lilishop 2023-09-26 18:51:04 +08:00
pikachu1995@126.com
817f662cb7 添加文件-文件夹 2023-09-26 18:50:52 +08:00
Chopper711
01419cde15 refactor: 增加店铺菜单维护页面 2023-09-25 11:58:08 +08:00
Chopper711
118efe0833 merge master 2023-09-25 11:05:04 +08:00
Chopper711
46911004db refactor: 虚拟订单增加取消判定,解决订单退款,待审核订单状态不变的问题。 2023-09-25 10:54:47 +08:00
pikachu1995@126.com
b87190d135 1.判断分销订单是否包含退款
2.订单货物全部退款则订单取消
2023-09-22 17:25:46 +08:00
pikachu1995@126.com
39e22f5561 订单退款,即刻扣减分销员冻结佣金 2023-09-21 18:46:32 +08:00
misworga831
02dd37c87f fix: 优化商品单位不能为空 2023-09-19 17:12:28 +08:00
misworga831
7db7afad4a fix: 优化商品单位不能为空 2023-09-19 17:09:14 +08:00
chc
25582e2958 QQ状态调整 2023-09-19 15:04:14 +08:00
misworga831
3c5609545b fix: 移除促销活动,活动起始时间小于现在时间限制 2023-09-18 16:50:46 +08:00
misworga831
061d924d46 fix: 优化弃用状态 2023-09-18 10:00:44 +08:00
misworga831
49af6777ee Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-09-14 17:04:46 +08:00
misworga831
e9d9a95cb4 improve: 优化订单投诉查询 2023-09-14 17:04:30 +08:00
Chopper711
7e70e7d6cd 优化注释 2023-09-11 17:18:10 +08:00
Chopper711
ebb48c7d0f fix: 商品批量设置物流模板时,没有删除缓存问题处理 2023-08-29 16:00:29 +08:00
Chopper711
e0d464906e fix: minio缩略图需要特殊配置,这里默认不压缩处理 2023-08-28 13:01:51 +08:00
misworga831
9189a6d433 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2023-08-25 14:06:07 +08:00
Chopper711
b3f6bad6f8 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2023-08-25 11:48:10 +08:00
Chopper711
105162aa89 fix: 某种不知名的情况下,微信联合登陆会报错问题处理 2023-08-25 11:48:03 +08:00
misworga831
51c4161f68 fix: 简单优化扫码登录,创建多个线程池导致的性能问题 2023-08-24 18:54:10 +08:00
OceansDeep
488d9352f3 !302 fix & improve
Merge pull request !302 from OceansDeep/pg
2023-08-24 09:02:52 +00:00
Chopper711
56b800e4ff Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2023-08-24 15:49:20 +08:00
Chopper711
03864ca1a9 fix: 部分sql过滤会导致一些问题处理。 2023-08-24 15:49:14 +08:00
misworga831
94c87f5645 fix: 优化商品搜索出现分类id问题 2023-08-24 14:17:21 +08:00
chc
dadf48ac25 fix: sql名称修改 2023-08-23 15:49:37 +08:00
chc
a47bfcc5fd 更新4.3版本 2023-08-23 15:37:31 +08:00
282 changed files with 8971 additions and 3474 deletions

View File

@@ -0,0 +1,30 @@
---
description:
globs:
alwaysApply: false
---
# Lilishop Project Overview
This is a comprehensive B2B2C e-commerce system built with Spring Boot (backend) and Vue/uniapp (frontend). The project supports multi-tenant merchant access and distributed deployment.
## Key Project Components
### Backend APIs
- [buyer-api/](mdc:buyer-api) - Buyer-facing APIs for shopping functionality
- [seller-api/](mdc:seller-api) - Merchant-facing APIs for store management
- [manager-api/](mdc:manager-api) - Platform admin APIs
- [common-api/](mdc:common-api) - Shared API components
- [im-api/](mdc:im-api) - Instant messaging APIs
### Core Components
- [framework/](mdc:framework) - Core framework and shared utilities
- [consumer/](mdc:consumer) - Message queue consumers
- [config/](mdc:config) - System configuration files
### Documentation
- [docs/](mdc:docs) - Project documentation
- [README.md](mdc:README.md) - Project overview and setup instructions
### Build & Deployment
- [pom.xml](mdc:pom.xml) - Maven project configuration
- [deploy-api.yml](mdc:deploy-api.yml) - Deployment configuration

View File

@@ -0,0 +1,38 @@
---
description:
globs:
alwaysApply: false
---
# Technical Architecture
## Backend Stack
- Spring Boot - Core framework
- Spring MVC - Web framework
- Mybatis-Plus - ORM framework
- Spring Security - Security framework
- JWT - Authentication
- RocketMQ - Message queue
- Redis & MongoDB - Caching
- Elasticsearch - Search engine
- MySQL - Primary database
- Sharding - Database sharding
- XXL-Job - Distributed scheduling
## Frontend Stack
### Admin & Seller Portals ([admin/](mdc:admin))
- Vue.js - Frontend framework
- iView - UI components
- Vuex - State management
- Vue Router - Routing
- Axios - HTTP client
### Mobile Applications
- Uni-app - Cross-platform framework
- uViewui - UI components
- SCSS - Styling
## Infrastructure
- Nginx - Load balancing & reverse proxy
- Aliyun OSS - Object storage
- Docker - Containerization
- Docker Compose - Container orchestration

View File

@@ -0,0 +1,43 @@
---
description:
globs:
alwaysApply: false
---
# Development Guidelines
## Project Setup
1. Ensure you have the following prerequisites:
- JDK 1.8+
- Maven 3.x
- MySQL 5.7+
- Redis
- RocketMQ
- Elasticsearch
- Node.js 12+
## Database Setup
- Initial database scripts can be found in [DB/](mdc:DB)
- For Docker deployment, refer to docker-compose configuration
## API Development Guidelines
1. All new APIs should be placed in appropriate modules:
- Customer-facing APIs in [buyer-api/](mdc:buyer-api)
- Merchant APIs in [seller-api/](mdc:seller-api)
- Admin APIs in [manager-api/](mdc:manager-api)
- Shared components in [common-api/](mdc:common-api)
2. Follow RESTful API conventions:
- Use appropriate HTTP methods (GET, POST, PUT, DELETE)
- Use consistent URL patterns
- Implement proper error handling
## Security Guidelines
1. All sensitive operations must be authenticated
2. Use Spring Security for access control
3. Store sensitive configuration in environment variables
4. Never commit sensitive credentials to version control
## Testing
1. Write unit tests for critical business logic
2. Ensure API endpoints are properly documented
3. Test both success and error scenarios

View File

@@ -0,0 +1,37 @@
---
description:
globs:
alwaysApply: false
---
# Deployment Guide
## Local Development Setup
1. Clone the repository
2. Import as Maven project
3. Configure environment variables in [config/](mdc:config)
4. Start required services (MySQL, Redis, RocketMQ, Elasticsearch)
5. Run individual API modules
## Docker Deployment
- Use [deploy-api.yml](mdc:deploy-api.yml) for container configuration
- Execute [docker-image.sh](mdc:docker-image.sh) to build images
## Production Deployment
1. Configure load balancer (Nginx recommended)
2. Set up database replication/clustering
3. Configure message queue clusters
4. Set up monitoring and logging
5. Configure CDN for static assets
## Configuration Files
- Database: [DB/](mdc:DB)
- API deployment: [deploy-api.yml](mdc:deploy-api.yml)
- Docker scripts: [docker-image.sh](mdc:docker-image.sh)
## Deployment Checklist
1. Database migration and backup
2. Environment variable configuration
3. Service dependencies verification
4. SSL certificate setup
5. Monitoring and alerting setup
6. Backup and recovery procedures

14
DB/index.sql Normal file
View File

@@ -0,0 +1,14 @@
-- 针对WHERE条件、排序和分组的组合索引
CREATE INDEX idx_order_delete_flag_create_time_id_sn ON li_order (delete_flag, create_time DESC, id DESC, sn);
-- 针对WHERE条件和连接条件的组合索引
CREATE INDEX idx_order_status_delete_flag_sn ON li_order (order_status, delete_flag, sn);
-- 针对连接条件的索引
CREATE INDEX idx_order_item_order_sn ON li_order_item (order_sn);
-- 针对过滤条件、排序字段的组合索引
CREATE INDEX idx_li_member_disabled_create_time ON li_member (disabled, create_time DESC);
-- 针对过滤条件、排序字段的组合索引
CREATE INDEX idx_li_goods_delete_flag_create_time ON li_goods (delete_flag, create_time DESC);

View File

@@ -84,7 +84,7 @@ ALTER TABLE li_foot_print ADD `store_id` varchar(255) DEFAULT NULL COMMENT '店
/**
*/
ALTER TABLE li_member_withdraw_apply ADD `real_name` varchar(255) DEFAULT NULL;
ALTER TABLE li_member_withdraw_apply ADD `connect_number` varchar(255) DEFAULT NULL;

173
DB/version4.3to4.4.sql Normal file
View File

@@ -0,0 +1,173 @@
CREATE TABLE `li_file_directory`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '创建者',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '更新者',
`delete_flag` tinyint(1) NULL DEFAULT 0 COMMENT '删除标志 true/false 删除/未删除',
`directory_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '文件目录类型',
`directory_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '拥有者名称',
`owner_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '拥有者id',
`parent_id` bigint NULL DEFAULT NULL COMMENT '父分类ID',
`level` int NULL DEFAULT NULL COMMENT '层级',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
AUTO_INCREMENT = 1698937596963311619
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci COMMENT = '文件夹'
ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
ALTER TABLE li_file
ADD file_directory_id varchar(255) COMMENT '文件夹ID';
/**
订单包裹
*/
CREATE TABLE `li_order_package` (
`id` bigint NOT NULL COMMENT 'ID',
`create_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '创建者',
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
`delete_flag` bit(1) DEFAULT NULL COMMENT '是否删除',
`update_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '修改者',
`update_time` datetime(6) DEFAULT NULL COMMENT '修改时间',
`package_no` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '包裹单号',
`order_sn` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '订单编号',
`logistics_no` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '发货单号',
`logistics_code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '物流公司CODE',
`logistics_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '物流公司名称',
`consignee_mobile` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '收件人手机',
`status` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin ROW_FORMAT=DYNAMIC;
/**
分包货物
*/
CREATE TABLE `li_order_package_item` (
`id` bigint NOT NULL COMMENT 'ID',
`create_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '创建者',
`create_time` datetime(6) DEFAULT NULL COMMENT '创建时间',
`delete_flag` bit(1) DEFAULT NULL COMMENT '是否删除',
`update_by` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '修改者',
`update_time` datetime(6) DEFAULT NULL COMMENT '修改时间',
`package_no` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '包裹单号',
`order_sn` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL COMMENT '订单编号',
`order_item_sn` varchar(255) COLLATE utf8mb3_bin DEFAULT NULL COMMENT '子订单编号',
`deliver_number` int DEFAULT NULL COMMENT '发货数量',
`logistics_time` datetime(6) DEFAULT NULL COMMENT '发货时间',
`goods_name` varchar(255) COLLATE utf8mb3_bin DEFAULT NULL,
`thumbnail` varchar(255) COLLATE utf8mb3_bin DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin ROW_FORMAT=DYNAMIC;
/*
订单货物增加发货数量
*/
ALTER TABLE li_order_item ADD `deliver_number` int DEFAULT NULL COMMENT '发货数量';
/*
sku增加预警库存
*/
ALTER TABLE li_goods_sku ADD `alert_quantity` int DEFAULT NULL COMMENT '预警库存';
/*
增加库存预警菜单
*/
INSERT INTO `lilishop`.`li_store_menu`(`id`, `create_by`, `create_time`, `delete_flag`, `update_by`, `update_time`, `description`, `front_route`, `icon`, `level`, `name`, `parent_id`, `path`, `sort_order`, `title`, `permission`) VALUES (1349237928434098177, NULL, '2022-01-11 22:35:45.000000', b'0', NULL, '2022-01-11 22:37:05', NULL, 'goods/goods-seller/alertQuantity', 'ios-american-football', 2, 'alert-goods-quantity', '1348810864748945408', 'alert-goods-quantity', '1.14', '库存预警', NULL);
/**
交易唤醒表,增加交易流水详情
*/
ALTER TABLE li_order_item ADD `is_refund` varchar(255) DEFAULT NULL COMMENT '是否退款';
/**
交易表增加订单状态字段
*/
ALTER TABLE li_order_item ADD `refund_price` decimal(10,2) DEFAULT NULL COMMENT '退款金额';
/**
结算单表,增加砍价/积分退款金额字段
*/
ALTER TABLE li_bill ADD `point_refund_settlement_price` decimal(10,2) DEFAULT NULL COMMENT '退货积分补贴返还';
ALTER TABLE li_bill ADD `kanjia_refund_settlement_price` decimal(10,2) DEFAULT NULL COMMENT '退货砍价补贴返还';
UPDATE li_bill b
SET b.point_refund_settlement_price =IFNULL((
SELECT
SUM( point_settlement_price )
FROM
li_store_flow sf
WHERE
sf.flow_type = 'REFUND'
AND sf.store_id=b.store_id
AND sf.create_time BETWEEN b.start_time
AND b.end_time),0);
UPDATE li_bill b
SET b.kanjia_refund_settlement_price =IFNULL((
SELECT
SUM( kanjia_settlement_price )
FROM
li_store_flow sf
WHERE
sf.flow_type = 'REFUND'
AND sf.store_id=b.store_id
AND sf.create_time BETWEEN b.start_time
AND b.end_time),0);
/**
文件表增加拥有者名称
*/
ALTER TABLE li_file ADD `owner_name` varchar(255) DEFAULT NULL COMMENT '拥有者名称';
/**
初始化文件拥有者名称
*/
UPDATE li_file f JOIN li_store s ON f.owner_id = s.id
SET f.owner_name = s.store_name
WHERE user_enums = 'STORE';
UPDATE li_file f JOIN li_admin_user a ON f.owner_id = a.id
SET f.owner_name = a.nick_name
WHERE user_enums = 'MANAGER';
UPDATE li_file f JOIN li_member m ON f.owner_id = m.id
SET f.owner_name = m.nick_name
WHERE user_enums = 'MEMBER';
ALTER TABLE `li_order`
ADD COLUMN `seller_remark` varchar(255) NULL COMMENT '商家订单备注' AFTER `remark`;
ALTER TABLE `li_distribution_cash`
ADD COLUMN `name` varchar(255) NULL COMMENT '会员姓名';
ALTER TABLE `li_distribution_cash`
ADD COLUMN `id_number` varchar(255) NULL COMMENT '身份证号' ;
ALTER TABLE `li_distribution_cash`
ADD COLUMN `settlement_bank_account_name` varchar(255) NULL COMMENT '结算银行开户行名称' ;
ALTER TABLE `li_distribution_cash`
ADD COLUMN `settlement_bank_account_num` varchar(255) NULL COMMENT '结算银行开户账号' ;
ALTER TABLE `li_distribution_cash`
ADD COLUMN `settlement_bank_branch_name` varchar(255) NULL COMMENT '结算银行开户支行名称' ;
ALTER TABLE `li_distribution` ADD `distribution_order_price` decimal(10,2) DEFAULT NULL COMMENT '分销订单金额';
ALTER TABLE `li_distribution_order` ADD `refund_num` int DEFAULT NULL COMMENT '退款商品数量';
ALTER TABLE `li_store_flow` ADD `bill_time` datetime(6) DEFAULT NULL COMMENT '结算时间';
ALTER TABLE `li_store_flow` ADD `full_refund` bit(1) DEFAULT NULL COMMENT '是否全部退款';
ALTER TABLE `li_store_flow` ADD `profit_sharing_status` varchar(255) NULL COMMENT '分账状态';
ALTER TABLE `li_store_flow` ADD `profit_sharing` varchar(255) NULL COMMENT '分账详情';
INSERT INTO `lilishop`.`li_setting` (`id`, `create_by`, `create_time`, `delete_flag`, `update_by`, `update_time`, `setting_value`) VALUES ('CONNECT_SETTING', 'admin', '2024-07-07 13:55:38.686000', b'0', NULL, NULL, '{\"callbackUrl\":\"https://buyer-api.pickmall.cn\",\"pc\":\"https://pc-b2b2c.pickmall.cn\",\"wap\":\"https://m-b2b2c.pickmall.cn\"}');
UPDATE `lilishop`.`li_setting` SET `create_by` = 'admin', `create_time` = '2021-01-23 02:18:03.299000', `delete_flag` = b'0', `update_by` = 'admin', `update_time` = '2024-07-07 13:53:44.732000', `setting_value` = '{\"accessKeyId\":\"test\",\"tencentSdkAppId\":\"null\",\"registerTemplateCode\":\"SMS_205755298\",\"huaweiSender\":\"null\",\"signName\":\"lili\",\"tencentSecretId\":\"null\",\"huaweiAppKey\":\"null\",\"isTestModel\":\"true\",\"tencentSecretKey\":\"null\",\"type\":\"ALI\",\"accessSecret\":\"test\",\"tencentSignName\":\"null\",\"huaweiSignature\":\"null\",\"payPasswordTemplateCode\":\"SMS_205755301\",\"walletPasswordTemplateCode\":\"SMS_205755297\",\"findPasswordTemplateCode\":\"SMS_205755301\",\"huaweiAppSecret\":\"null\",\"loginTemplateCode\":\"SMS_205755300\"}' WHERE `id` = 'SMS_SETTING';
ALTER TABLE li_store_logistics ADD `partner_name` varchar(255) DEFAULT NULL COMMENT '电子面单客户账户名称';

269
README.md
View File

@@ -1,198 +1,167 @@
## Lilishop B2B2C商城系统
# Lilishop B2B2C 商城系统
#### 欢迎交流需求,交流业务,交流技术(基础问题自行解决,其他问题先看文档后提问)
[![GitHub Stars](https://img.shields.io/github/stars/hongyehuicheng/lilishop.svg?style=social&logo=github)](https://github.com/hongyehuicheng/lilishop)
[![Gitee Stars](https://gitee.com/beijing_hongye_huicheng/lilishop/badge/star.svg?theme=dark)](https://gitee.com/beijing_hongye_huicheng/lilishop)
[![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.html)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7.18-brightgreen)](https://spring.io/projects/spring-boot)
[![Vue.js](https://img.shields.io/badge/Vue.js-2.x-green)](https://vuejs.org/)
[![uni-app](https://img.shields.io/badge/uni--app-3.x-green)](https://uniapp.dcloud.io/)
#### 不用削尖脑袋往老群里加,老群活跃度较低,很多潜水党,新群相对而言活跃一些 :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606:
---
#### PS **演示站点所有环境均部署master分支。如果有演示站点问题可以反馈如果演示站点没问题本地运行有问题需自行处理**
### 1. 项目简介
##### 交流 qq 1群 961316482已满
##### 交流 qq 2群 875294241已满
##### 交流 qq 3群 263785057已满
##### 交流 qq 4群 674617534 (已满)
##### 交流 qq 5群 594675235
**Lilishop** 是一款功能完善的B2B2C多商户商城系统采用前后端分离架构全端代码开源。后端基于 **SpringBoot** 构建具备高内聚、低耦合的特性支持分布式部署。前端覆盖PC、H5、小程序和APP基于 **Vue****uni-app** 开发。
##### 体验 公众号/小程序/APP 体验,扫描二维码
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
[![star](https://gitee.com/beijing_hongye_huicheng/lilishop/badge/star.svg?theme=dark)](https://gitee.com/beijing_hongye_huicheng/lilishop/stargazers)
&nbsp;&nbsp;![github](https://img.shields.io/github/stars/hongyehuicheng/lilishop.svg?style=social&logo=#181717)
- **官方网站**: <https://pickmall.cn>
- **官方文档**: <https://docs.pickmall.cn>
- **Gitee 仓库**: <https://gitee.com/beijing_hongye_huicheng/lilishop>
- **GitHub 仓库**: <https://github.com/lilishop/lilishop>
### 商城介绍
**官网**https://pickmall.cn
---
Lilishop商城系统支持商家入驻后端基于SpringBoot 研发,前端使用 Vue、uniapp开发 **系统全端全部代码开源**
### 2. 核心特性
前后端分离支持分布式部署支持Docker各个API独立并且有独立的消费者
- **全端覆盖**: 一套代码库支持PC、H5、小程序、APP降低开发和维护成本
- **商家入驻**: 支持多商家入驻,构建平台化电商生态。
- **分布式架构**: 后端API服务化支持独立部署和弹性伸缩。
- **前后端分离**: 清晰的职责划分,便于团队协作和独立开发。
- **容器化支持**: 提供Docker镜像和docker-compose配置实现一键部署。
- **功能完善**: 涵盖会员、订单、商品、促销、店铺、运营、统计等完整电商业务模块。
### 商城 API/消费者 聚合版
api不需要单独部署只需启动一个jar包就可以正常运转 如有需要,可以点击跳转
https://gitee.com/beijing_hongye_huicheng/lilishop-simplify
---
### 开发/使用/常见问题 帮助文档
### 3. 在线演示
https://docs.pickmall.cn
**注意**: 演示站手机验证码统一为 `111111`。演示环境部署于 `master` 分支。
### 项目地址
- **平台管理端**: <https://admin-b2b2c.pickmall.cn>
- 账号: `admin`
- 密码: `123456`
- **店铺管理端**: <https://store-b2b2c.pickmall.cn>
- 账号: `13011111111`
- 密码: `111111`
- **商城PC端**: <https://pc-b2b2c.pickmall.cn>
- **移动端 (H5/小程序/APP)**:
![移动端体验二维码](https://static.pickmall.cn/images/h5-qrcode.png)
gitee : https://gitee.com/beijing_hongye_huicheng
---
github 镜像: https://github.com/lilishop?tab=repositories
### 4. 快速开始
商城UI 项目下3个文件夹
buyer买家PC端seller商家端manager后台管理端
#### 环境准备与部署
详细的本地部署指南,请参考官方文档:
[**部署文档 -> 环境准备**](https://docs.pickmall.cn/deply/deply.html)
#### 数据库初始化
- **推荐方式**: 使用项目提供的 `docker-compose` 配置可自动完成数据库MySQL, Redis, Elasticsearch等的部署与初始化。
- **手动方式**: 如果您选择手动部署SQL脚本位于以下地址。请确保获取与您代码版本一致的SQL文件。
[**数据库脚本 (Gitee)**](https://gitee.com/beijing_hongye_huicheng/docker/tree/master/init/mysql)
### 演示地址
PS手机验证码为 111111
---
**平台管理端**https://admin-b2b2c.pickmall.cn 账号admin/123456
### 5. 技术架构
**店铺管理端**https://store-b2b2c.pickmall.cn 账号13011111111/111111
#### 5.1 架构图
![系统架构图](https://lili-system.oss-cn-beijing.aliyuncs.com/docs/%E6%9E%B6%E6%9E%84.png)
**商城PC页面**https://pc-b2b2c.pickmall.cn
#### 5.2 后端技术栈
**商城 小程序/公众号/APP**:扫描二维码
| 技术 | 选型 | 备注/用途 |
| :-------------- | :-------------- | :--------- |
| 核心框架 | Spring Boot | 简化应用开发 |
| ORM框架 | Mybatis-Plus | 数据持久化 |
| 数据库 | MySQL | 关系型数据存储 |
| 消息队列 | RocketMQ | 异步任务与解耦 |
| 缓存 | Redis, MongoDB | 数据缓存与存储 |
| 搜索引擎 | Elasticsearch | 商品搜索 |
| 安全框架 | Spring Security | 认证与授权 |
| 分库分表 | ShardingSphere | 数据水平扩展 |
| 定时任务 | XXL-Job | 分布式任务调度 |
| 认证方案 | JWT | Token |
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
#### 5.3 前端技术栈
### 快速本地部署
**管理端 (平台/商家)**
[点击跳转](https://docs.pickmall.cn/deploy/%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87.html)
**商城数据库**
使用docker-compose部署数据库自动初始化数据库不需要手动下载等操作
如果手动部署才需要获取sql [点击跳转](https://gitee.com/beijing_hongye_huicheng/docker/tree/master/init/mysql) PS这里有与tag版本一致的sql如果是历史版本则docker项目也切换至历史版本获取sql即可历史版本升级则根据java相聚的根目录DB目录下的升级sql按需执行
| 技术 | 选型 | 备注/用途 |
| :--------- | :--------- | :--------- |
| JS框架 | Vue.js | 核心框架 |
| UI库 | iView | 界面组件 |
| 状态管理 | Vuex | 全局状态管理 |
| 路由 | Vue Router | 页面路由 |
| HTTP客户端 | axios | API请求 |
**移动端 (H5/小程序/APP)**
| 技术 | 选型 | 备注/用途 |
| :-------- | :------ | :------------- |
| 核心框架 | uni-app | 跨平台开发框架 |
| UI库 | uViewUI | 丰富的组件库 |
| CSS预处理 | SCSS | 样式开发 |
### 功能列表
### 6. 功能清单
#### 6.1 平台管理端
#### 平台管理端功能
| 模块 | 主要功能 |
| :--- | :--------------------------------------------------------------------- |
| 首页 | 数据看板、待办事项 |
| 会员 | 会员管理、会员评价、积分管理、资金流水、充值管理 |
| 订单 | 商品订单、虚拟订单、售后处理、订单投诉、收款与退款流水 |
| 商品 | 商品管理、商品审核、分类、品牌、规格、计量单位管理 |
| 促销 | 优惠券、秒杀、砍价、拼团、积分商品等营销活动 |
| 店铺 | 店铺管理、入驻审核、结算管理、店铺对账 |
| 运营 | 页面装修、分销管理、文章管理、意见反馈、站内信、短信配置 |
| 统计 | 会员、订单、流量、商品销量等多维度统计 |
| 设置 | 权限、角色、部门、管理员、系统参数、OSS、支付、物流、敏感词等基础配置 |
#### 6.2 商家端
| 模块 <img width=80/> | 功能 |
|--------------------|-----------------------------------------------------------------|
| 首页 | 平台基础信息统计、待办事项 |
| 会员 | 会员列表、评价列表、积分历史、会员资金、会员充值 |
| 订单 | 商品订单、虚拟订单、订单售后、订单投诉、售后原因维护、收款流水、退款流水 |
| 商品 | 商品列表、商品审核、商品分类、商品品牌、商品规格、商品计量单位 |
| 促销 | 优惠券、券活动(每日&每月&每周&邀新 赠券)、秒杀活动、砍价活动、拼团活动、积分商品 |
| 店铺 | 店铺管理、店铺审核、店铺结算、店铺对账 |
| 运营 | 楼层装修、分销商管理、文章管理、意见反馈、站内信、短信、搜索热词管理 |
| 统计 | 会员统计、订单统计、流量统计、商品销量统计 |
| 设置 | 菜单管理、角色管理、部门管理、管理员管理、系统设置、行政地区管理、OSS管理、联合登陆、支付、物流公司、敏感词、验证码资源 |
| 模块 | 主要功能 |
| :--- | :----------------------------------------------------------- |
| 首页 | 店铺看板、待办事项、公告 |
| 商品 | 商品发布、商品管理、运费模板、店铺内分类 |
| 订单 | 订单处理、评价管理、投诉处理、退款/退货申请 |
| 财务 | 店铺对账、结算管理、发票管理 |
| 促销 | 优惠券、满减、秒杀、拼团、分销商品管理 |
| 统计 | 订单统计、流量分析、商品销量排行 |
| 设置 | 物流配送、自提点、店铺信息、PC/移动端装修、店员与权限管理 |
---
### 7. 界面展示
#### 卖家功能
| 模块 <img width=80/> | 功能 |
|----|-------------------------------|
| 首页 | 店铺基础信息统计、待办事项、店铺公告 |
| 商品 | 商品发布、商品列表、商品模板、店铺分类 |
| 订单 | 商品订单、虚拟订单、订单评价、订单投诉、退款申请、退货申请 |
| 财务 | 店铺对账、店铺结算、发票管理 |
| 促销 | 优惠券、满额优惠、秒杀、拼团 、分销商品、分校订单 |
| 统计 |单统计、流量统计、商品销量统计 |
| 设置 | 配送公司、物流模板、店铺设置、店铺自提设置、PC装修、移动端装修、店员管理、部门管理、角色管理 |
| 消息 | 站内信 |
### 商城前端功能展示
#### 商城移动端
<img src="https://static.pickmall.cn/images/other/app.gif" alt="移动端功能展示" style="zoom:50%;" />
#### 移动端
<img src="https://static.pickmall.cn/images/other/app.gif" alt="移动端功能展示" width="300"/>
#### 平台管理端
![管理端功能展示](https://static.pickmall.cn/images/other/manager.gif)
---
### 技术选型
### 8. 开源与授权
#### 架构图
1. **开源协议**: 本项目遵循 `AGPL-3.0` 开源协议。
2. **使用范围**: 仅允许用于个人学习、研究和非商业用途。
3. **禁止行为**: 禁止将本项目的代码和资源用于任何形式的商业销售。
4. **商业授权**: 如需商业使用,必须获得官方授权。授权为一次性永久授权,并提供持续的版本升级服务。详情请联系官网客服。
5. **软件著作权**: 本软件受国家计算机软件著作权保护登记号2021SR0805085
![技术选型](https://lili-system.oss-cn-beijing.aliyuncs.com/docs/%E6%9E%B6%E6%9E%84.png)
---
##### 后台技术选型
### 9. 社区与支持
| 说明 | 框架 | 说明 | |
| -------------- | --------------- | -------------- | ------------- |
| 基础框架 | Spring Boot | MVC框架 | Spring MVC |
| 持久框架 | Mybatis-Plus | 程序构建 | Maven |
| 关系型数据库 | MySQL | 消息中间件AMQP | RocketMQ |
| 缓存 | Redis +MongoDB | 搜索引擎 | Elasticsearch |
| 安全框架 | Spring Security | 数据库连接池 | Druid |
| 数据库分库分表 | sharding | 定时任务 | xxl-job |
| 负载均衡 | Nginx | 静态资源 | 阿里云OSS |
| 短信 | 阿里云短信 | 认证 | JWT |
| 日志处理 | Log4j | 接口规范 | RESTful |
我们欢迎任何形式的交流与贡献。在提问前,请先查阅 [官方文档](https://docs.pickmall.cn/) ,并参考 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md) 以便高效沟通。
##### 前端-运营后台、店铺后台
- **[在线客服](https://work.weixin.qq.com/kfid/kfc4d8dc24a73c15f44)**
- **微信交流1群已满**
- **微信交流2群**:
![微信群](https://lilishop-wechat.oss-cn-beijing.aliyuncs.com/wechat.jpg)
| 说明 | 框架 | 说明 | 框架 |
| ---------- | ---------- | ---------- | ------- |
| 构建工具 | webpack | JS版本 | ES6 |
| 基础JS框架 | Vue.js | 视频播放器 | Dplayer |
| 路由管理 | Vue Router | 状态管理 | Vuex |
| 基础UI库 | iView | UI界面基于 | iView |
| 网络请求 | axios | | |
##### 前端-移动端
| 说明 | 架构 | 说明 | 架构 |
| --------- | ------- | -------- | ------- |
| 基础UI库 | uViewui | 基础框架 | uni-app |
| CSS预处理 | scss | 地图引擎 | amap |
### 版本升级
```
系统后续会提供多场景解决方案。
更多架构微服务、Saas、中台等都会支持。 支持差价升级商业授权
```
### 商业授权
商业版本与开源版本代码一致,没有区分
商业使用需要授权授权方式可选择联系官网客服或者qq群联系群主。
商业授权模式为永久授权,支持永久升级。
商业案例由于涉及部分多层二开关系,如需了解可以咨询销售。
### 开源须知
1.仅允许用于个人学习研究使用.
2.禁止将本开源的代码和资源进行任何形式任何名义的出售.
3.软件受国家计算机软件著作权保护登记号2021SR0805085
4.限制商用如果需要商业使用请联系我们。QQ3409056806.或者加入qq群联系群主。
### 交流群
##### 交流 qq 1群 961316482已满
##### 交流 qq 2群 875294241已满
##### 交流 qq 3群 263785057已满
##### 交流 qq 4群 674617534已满
##### 交流 qq 5群 594675235
### 附录
有人有自己的学习视频、学习记录文档、希望宣传关联开源项目等均可以私聊仓库所有者。
类似:
清晨敲代码同学的分析: https://blog.csdn.net/vaevaevae233/category_12103567.html
- **社区贡献内容**:
- 清晨敲代码的分析: <https://blog.csdn.net/vaevaevae233/category_12103567.html>
- DeepWiki: <https://deepwiki.com/lilishop/lilishop>

View File

@@ -6,6 +6,7 @@ import cn.lili.common.exception.ServiceException;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.vos.GoodsVO;
import cn.lili.modules.goods.service.GoodsService;
@@ -101,6 +102,12 @@ public class GoodsBuyerController {
return ResultUtil.data(goodsService.queryByParams(goodsSearchParams));
}
@ApiOperation(value = "获取商品sku列表")
@GetMapping("/sku")
public ResultMessage<List<GoodsSku>> getSkuByPage(GoodsSearchParams goodsSearchParams) {
return ResultUtil.data(goodsSkuService.getGoodsSkuByList(goodsSearchParams));
}
@ApiOperation(value = "从ES中获取商品信息")
@GetMapping("/es")
public ResultMessage<Page<EsGoodsIndex>> getGoodsByPageFromEs(EsGoodsSearchDTO goodsSearchParams, PageVO pageVO) {

View File

@@ -59,7 +59,7 @@ public class MemberAddressBuyerController {
public ResultMessage<MemberAddress> addShippingAddress(@Valid MemberAddress shippingAddress) {
//添加会员地址
shippingAddress.setMemberId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
if(shippingAddress.getIsDefault()==null){
if(Objects.isNull(shippingAddress.getIsDefault())){
shippingAddress.setIsDefault(false);
}
return ResultUtil.data(memberAddressService.saveMemberAddress(shippingAddress));

View File

@@ -9,11 +9,14 @@ import cn.lili.common.security.OperationalJudgment;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderPackage;
import cn.lili.modules.order.order.entity.dto.OrderSearchParams;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.vo.OrderDetailVO;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import cn.lili.modules.order.order.service.OrderPackageService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.system.entity.vo.Traces;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -44,6 +47,9 @@ public class OrderBuyerController {
@Autowired
private OrderService orderService;
@Autowired
private OrderPackageService orderPackageService;
@ApiOperation(value = "查询会员订单列表")
@GetMapping
public ResultMessage<IPage<OrderSimpleVO>> queryMineOrder(OrderSearchParams orderSearchParams) {
@@ -138,4 +144,21 @@ public class OrderBuyerController {
return ResultUtil.data(orderService.invoice(orderSn));
}
@ApiOperation(value = "查询物流踪迹")
@ApiImplicitParams({
@ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path")
})
@GetMapping(value = "/getTracesList/{orderSn}")
public ResultMessage<Object> getTracesList(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) {
return ResultUtil.data(orderPackageService.getOrderPackageVOList(orderSn));
}
@ApiOperation(value = "查看包裹列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path")
})
@GetMapping(value = "/getPackage/{orderSn}")
public ResultMessage<Object> getPackage(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) {
return ResultUtil.data(orderPackageService.getOrderPackageVOList(orderSn));
}
}

View File

@@ -28,9 +28,8 @@ import org.springframework.web.context.request.async.DeferredResult;
import javax.validation.constraints.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 买家端,会员接口
@@ -74,6 +73,8 @@ public class MemberBuyerController {
new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK);
int timeoutSecond = 20;
DeferredResult<ResponseEntity<Object>> deferredResult = new DeferredResult<>(timeoutSecond * 1000L, timeoutResponseEntity);
// 用于记录重试次数
AtomicInteger retryCount = new AtomicInteger(0);
CompletableFuture.runAsync(() -> {
try {
int i = 0;
@@ -84,7 +85,16 @@ public class MemberBuyerController {
&& (QRCodeLoginSessionStatusEnum.WAIT_SCANNING.getCode() == status
|| QRCodeLoginSessionStatusEnum.SCANNING.getCode() == status)) {
//睡眠一秒种,继续等待结果
TimeUnit.SECONDS.sleep(1);
//TimeUnit.SECONDS.sleep(1);
// 应用指数退避策略
int baseSleepTime = 1000; // 基础退避时间(毫秒)
int maxSleepTime = 10000; // 最大退避时间(毫秒)
int sleepTime = Math.min(maxSleepTime, baseSleepTime * (1 + retryCount.getAndIncrement()));
int randomFactor = (int) (Math.random() * (sleepTime / 2)); // 随机化因子
TimeUnit.MILLISECONDS.sleep(sleepTime + randomFactor);
} else {
deferredResult.setResult(new ResponseEntity<>(ResultUtil.data(queryResult), HttpStatus.OK));
break;
@@ -96,7 +106,7 @@ public class MemberBuyerController {
deferredResult.setResult(new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK));
Thread.currentThread().interrupt();
}
}, Executors.newCachedThreadPool());
});
return deferredResult;
}
@@ -155,6 +165,32 @@ public class MemberBuyerController {
}
}
@ApiOperation(value = "绑定手机号")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"),
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"),
@ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query"),
})
@PostMapping("/bindMobile")
public ResultMessage<Object> bindMobile(@NotNull(message = "用户名不能为空") @RequestParam String username,
@NotNull(message = "手机号为空") @RequestParam String mobile,
@NotNull(message = "验证码为空") @RequestParam String code,
@RequestHeader String uuid) {
if (smsUtil.verifyCode(mobile, VerificationEnums.BIND_MOBILE, uuid, code)) {
Member member = memberService.findByUsername(username);
Member memberByMobile = memberService.findByMobile(mobile);
if (member == null) {
throw new ServiceException(ResultCode.USER_NOT_EXIST);
}
if(memberByMobile != null){
throw new ServiceException(ResultCode.USER_MOBILE_REPEATABLE_ERROR);
}
return ResultUtil.data(memberService.changeMobile(member.getId(), mobile));
} else {
throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR);
}
}
@ApiOperation(value = "注册用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"),
@@ -251,12 +287,9 @@ public class MemberBuyerController {
}
@ApiOperation(value = "注销账号")
@ApiImplicitParams({
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query")
})
@PutMapping("/cancellation")
public ResultMessage<Member> cancellation(@NotNull(message = "密码不能为空") @RequestParam String password) {
memberService.cancellation(password);
public ResultMessage<Member> cancellation() {
memberService.cancellation();
return ResultUtil.success();
}

View File

@@ -64,13 +64,22 @@ public class CouponBuyerController {
@GetMapping("/activity")
@ApiOperation(value = "自动领取优惠券")
public ResultMessage<List<MemberCoupon>> activity() {
return ResultUtil.data(couponActivityService.trigger(
if (UserContext.getCurrentUser() == null) {
return ResultUtil.success();
}
List<MemberCoupon> memberCouponList = couponActivityService.trigger(
CouponActivityTrigger.builder()
.couponActivityTypeEnum(CouponActivityTypeEnum.AUTO_COUPON)
.nickName(UserContext.getCurrentUser().getNickName())
.userId(UserContext.getCurrentUser().getId())
.build())
);
.build());
memberCouponList.addAll(couponActivityService.trigger(
CouponActivityTrigger.builder()
.couponActivityTypeEnum(CouponActivityTypeEnum.SPECIFY)
.nickName(UserContext.getCurrentUser().getNickName())
.userId(UserContext.getCurrentUser().getId())
.build()));
return ResultUtil.data(memberCouponList);
}
@GetMapping

View File

@@ -91,15 +91,16 @@ public class BuyerAuthenticationFilter extends BasicAuthenticationFilter {
try {
Claims claims
= Jwts.parser()
= Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(jwt).getBody();
//获取存储在claims中的用户信息
// 获取存储在claims中的用户信息
String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString();
AuthUser authUser = new Gson().fromJson(json, AuthUser.class);
//校验redis中是否有权限
if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER,authUser.getId()) + jwt)) {
if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER, authUser.getId()) + jwt)) {
//构造返回信息
List<GrantedAuthority> auths = new ArrayList<>();
auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name()));

View File

@@ -213,18 +213,6 @@ lili:
lbs:
key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT
sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6
#域名
domain:
pc: https://pc-b2b2c.pickmall.cn
wap: https://m-b2b2c.pickmall.cn
store: https://store-b2b2c.pickmall.cn
admin: https://admin-b2b2c.pickmall.cn
#api地址
api:
buyer: https://buyer-api.pickmall.cn
common: https://common-api.pickmall.cn
manager: https://admin-api.pickmall.cn
store: https://store-api.pickmall.cn
# jwt 细节设定
jwt-setting:

View File

@@ -1,15 +1,15 @@
package cn.lili.controller.common;
import cn.lili.cache.Cache;
import cn.lili.common.context.ThreadContextHolder;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.common.vo.PageVO;
import cn.lili.common.utils.ResponseUtil;
import cn.lili.common.vo.ResultMessage;
import cn.lili.common.vo.SearchVO;
import cn.lili.modules.file.entity.File;
import cn.lili.modules.file.entity.dto.FileOwnerDTO;
import cn.lili.modules.file.service.FileService;
@@ -43,10 +43,14 @@ public class FileController {
@ApiOperation(value = "获取自己的图片资源")
@GetMapping
@ApiImplicitParam(name = "title", value = "名称模糊匹配")
public ResultMessage<IPage<File>> getFileList(@RequestHeader String accessToken, File file, SearchVO searchVO, PageVO pageVo) {
public ResultMessage<IPage<File>> getFileList(@RequestHeader String accessToken, FileOwnerDTO fileOwnerDTO) {
AuthUser authUser = UserContext.getAuthUser(cache, accessToken);
FileOwnerDTO fileOwnerDTO = new FileOwnerDTO();
if (authUser == null) {
ResponseUtil.output(ThreadContextHolder.getHttpResponse(), 403, ResponseUtil.resultMap(false,
403, "登录已失效,请重新登录"));
return null;
}
//只有买家才写入自己id
if (authUser.getRole().equals(UserEnums.MEMBER)) {
fileOwnerDTO.setOwnerId(authUser.getId());
@@ -55,7 +59,7 @@ public class FileController {
fileOwnerDTO.setOwnerId(authUser.getStoreId());
}
fileOwnerDTO.setUserEnums(authUser.getRole().name());
return ResultUtil.data(fileService.customerPageOwner(fileOwnerDTO, file, searchVO, pageVo));
return ResultUtil.data(fileService.customerPageOwner(fileOwnerDTO));
}
@ApiOperation(value = "文件重命名")

View File

@@ -0,0 +1,70 @@
package cn.lili.controller.common;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.file.entity.FileDirectory;
import cn.lili.modules.file.entity.dto.FileDirectoryDTO;
import cn.lili.modules.file.service.FileDirectoryService;
import cn.lili.modules.file.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 文件目录管理接口
*
* @author Chopper
* @since 2020/11/26 15:41
*/
@RestController
@Api(tags = "文件目录管理接口")
@RequestMapping("/common/resource/fileDirectory")
@RequiredArgsConstructor
public class FileDirectoryController {
private final FileDirectoryService fileDirectoryService;
private final FileService fileService;
@ApiOperation(value = "获取文件目录列表")
@GetMapping
public ResultMessage<List<FileDirectoryDTO>> getSceneFileList() {
return ResultUtil.data(fileDirectoryService.getFileDirectoryList(UserContext.getCurrentUser().getId()));
}
@ApiOperation(value = "添加文件目录")
@PostMapping
public ResultMessage<FileDirectory> addSceneFileList(@RequestBody @Valid FileDirectory fileDirectory) {
fileDirectory.setDirectoryType(UserContext.getCurrentUser().getRole().name());
fileDirectory.setOwnerId(UserContext.getCurrentUser().getId());
fileDirectoryService.save(fileDirectory);
return ResultUtil.data(fileDirectory);
}
@ApiOperation(value = "修改文件目录")
@PutMapping
public ResultMessage<FileDirectory> editSceneFileList(@RequestBody @Valid FileDirectory fileDirectory) {
fileDirectory.setDirectoryType(UserContext.getCurrentUser().getRole().name());
fileDirectory.setOwnerId(UserContext.getCurrentUser().getId());
fileDirectoryService.updateById(fileDirectory);
return ResultUtil.data(fileDirectory);
}
@ApiOperation(value = "删除文件目录")
@DeleteMapping("/{id}")
public ResultMessage<Object> deleteSceneFileList(@PathVariable String id) {
//检测文件夹下是否包含图片
if(fileService.countByDirectory(id)){
return ResultUtil.error(ResultCode.FILE_DIRECTORY_NOT_EMPTY);
}
//删除目录
fileDirectoryService.removeById(id);
return ResultUtil.success();
}
}

View File

@@ -1,6 +1,7 @@
package cn.lili.controller.common;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.lili.cache.Cache;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
@@ -21,10 +22,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
@@ -55,8 +53,11 @@ public class UploadController {
@PostMapping(value = "/file")
public ResultMessage<Object> upload(MultipartFile file,
String base64,
@RequestHeader String accessToken) {
@RequestHeader String accessToken, @RequestParam String directoryPath) {
if(StrUtil.isBlank(directoryPath)){
directoryPath = "default";
}
AuthUser authUser = UserContext.getAuthUser(cache, accessToken);
//如果用户未登录,则无法上传图片
@@ -89,6 +90,12 @@ public class UploadController {
try {
InputStream inputStream = file.getInputStream();
//上传至第三方云服务或服务器
String scene = UserContext.getCurrentUser().getRole().name();
if (StrUtil.equalsAny(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name(), UserEnums.STORE.name(), UserEnums.SEAT.name())) {
scene = scene + "/" + authUser.getId();
}
fileKey = scene + "/" + directoryPath + "/" + fileKey;
//上传至第三方云服务或服务器
result = filePluginFactory.filePlugin().inputStreamUpload(inputStream, fileKey);
//保存数据信息至数据库
newFile.setName(file.getOriginalFilename());
@@ -101,8 +108,19 @@ public class UploadController {
//如果是店铺则记录店铺id
if (authUser.getRole().equals(UserEnums.STORE)) {
newFile.setOwnerId(authUser.getStoreId());
newFile.setOwnerName(authUser.getStoreName());
} else {
newFile.setOwnerId(authUser.getId());
newFile.setOwnerName(authUser.getNickName());
}
//存储文件目录
if (StrUtil.isNotEmpty(directoryPath)) {
if (directoryPath.indexOf("/") > 0) {
newFile.setFileDirectoryId(directoryPath.substring(directoryPath.lastIndexOf("/") + 1));
} else {
newFile.setFileDirectoryId(directoryPath);
}
}
fileService.save(newFile);
} catch (Exception e) {

View File

@@ -190,18 +190,6 @@ lili:
interfereNum: 0
#允许误差像素
faultTolerant: 3
#短信模版配置
sms:
#登录
LOGIN: SMS_205755300
#注册
REGISTER: SMS_205755298
#找回密码
FIND_USER: SMS_205755301
#设置密码
UPDATE_PASSWORD: SMS_205755297
#支付密码
WALLET_PASSWORD: SMS_205755301
system:
isTestModel: true
statistics:

View File

@@ -20,17 +20,30 @@ management:
exposure:
include: '*'
spring:
mail:
host: smtp.qq.com
port: 465
username: lifenlong@foxmail.com
password: dirpxpqgfvysbefh
protocol: smtps
properties:
mail:
smtp:
auth: true
starttls:
enable: true
# 要在其中注册的Spring Boot Admin Server的URL。
boot:
admin:
client:
url: http://192.168.0.108:8000
url: http://127.0.0.1:8000
cache:
type: redis
# Redis
redis:
host: 192.168.31.108
port: 30379
host: 127.0.0.1
port: 6379
password: lilishop
lettuce:
pool:
@@ -60,7 +73,7 @@ spring:
default-datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.108:30306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: lilishop
maxActive: 50
@@ -209,25 +222,12 @@ lili:
interfereNum: 1
#允许误差像素
faultTolerant: 3
#短信模版配置
sms:
#登录
LOGIN: SMS_205755300
#注册
REGISTER: SMS_205755298
#找回密码
FIND_USER: SMS_205755301
#设置密码
UPDATE_PASSWORD: SMS_205755297
#支付密码
WALLET_PASSWORD: SMS_205755301
system:
isDemoSite: false
isTestModel: true
# 脱敏级别:
# 0不做脱敏处理
# 1管理端用户手机号等信息脱敏
# 2商家端信息脱敏为2时表示管理端商家端同时脱敏
# 脱敏级别:
# 0不做脱敏处理
# 1管理端用户手机号等信息脱敏
# 2商家端信息脱敏为2时表示管理端商家端同时脱敏
sensitiveLevel: 1
statistics:
@@ -241,16 +241,16 @@ lili:
sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6
#域名
domain:
pc: http://192.168.0.108:8888
wap: http://192.168.0.108:8888
seller: http://192.168.0.108:8888
admin: http://192.168.0.108:8888
pc: http://127.0.0.1:8888
wap: http://127.0.0.1:8888
seller: http://127.0.0.1:8888
admin: http://127.0.0.1:8888
#api地址
api:
buyer: https://z171l91606.51mypc.cn
base: http://192.168.0.108:8888
manager: http://192.168.0.108:8888
seller: http://192.168.0.108:8888
base: http://127.0.0.1:8888
manager: http://127.0.0.1:8888
seller: http://127.0.0.1:8888
# jwt 细节设定
jwt-setting:
@@ -269,7 +269,7 @@ lili:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.31.108:30920
cluster-nodes: 127.0.0.1:9200
index:
number-of-replicas: 0
number-of-shards: 3
@@ -301,7 +301,7 @@ lili:
after-sale-topic: shop_lili_after_sale_topic
after-sale-group: shop_lili_after_sale_group
rocketmq:
name-server: 192.168.31.108:30876
name-server: 127.0.0.1:9876
isVIPChannel: false
producer:
group: lili_group
@@ -310,7 +310,7 @@ rocketmq:
xxl:
job:
admin:
addresses: http://192.168.31.108:30001/xxl-job-admin
addresses: http://127.0.0.1:9001/xxl-job-admin
executor:
appname: xxl-job-executor-lilishop
address:

View File

@@ -5,9 +5,7 @@ import cn.hutool.core.date.DateTime;
import cn.hutool.json.JSONUtil;
import cn.lili.event.AfterSaleStatusChangeEvent;
import cn.lili.event.OrderStatusChangeEvent;
import cn.lili.modules.distribution.entity.dos.DistributionOrder;
import cn.lili.modules.distribution.entity.enums.DistributionOrderStatusEnum;
import cn.lili.modules.distribution.mapper.DistributionOrderMapper;
import cn.lili.modules.distribution.service.DistributionOrderService;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.order.entity.dto.OrderMessage;
@@ -17,13 +15,10 @@ import cn.lili.modules.system.entity.dto.DistributionSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.timetask.handler.EveryDayExecute;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 分销订单入库
*
@@ -32,7 +27,7 @@ import javax.annotation.Resource;
*/
@Slf4j
@Service
public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDayExecute, AfterSaleStatusChangeEvent {
public class DistributionOrderExecute implements OrderStatusChangeEvent, AfterSaleStatusChangeEvent {
/**
* 分销订单
@@ -40,10 +35,6 @@ public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDa
@Autowired
private DistributionOrderService distributionOrderService;
@Autowired
private SettingService settingService;
@Override
public void orderChange(OrderMessage orderMessage) {
@@ -67,25 +58,10 @@ public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDa
}
}
@Override
public void execute() {
log.info("分销订单定时开始执行");
//设置结算天数(解冻日期)
Setting setting = settingService.get(SettingEnum.DISTRIBUTION_SETTING.name());
DistributionSetting distributionSetting = JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class);
//解冻时间
DateTime dateTime = new DateTime();
//当前时间-结算天数=最终结算时间
dateTime = dateTime.offsetNew(DateField.DAY_OF_MONTH, -distributionSetting.getCashDay());
//分销人员订单结算
distributionOrderService.updateRebate(dateTime,DistributionOrderStatusEnum.WAIT_BILL.name());
}
@Override
public void afterSaleStatusChange(AfterSale afterSale) {
if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.COMPLETE.name())) {
distributionOrderService.refundOrder(afterSale.getSn());
distributionOrderService.refundOrder(afterSale);
}
}

View File

@@ -104,7 +104,7 @@ public class FullDiscountExecute implements TradeEvent, OrderStatusChangeEvent {
searchParams.setOrderPromotionType(OrderPromotionTypeEnum.GIFT.name());
List<Order> orders = orderService.queryListByParams(searchParams);
if (orders != null && !orders.isEmpty()) {
orderService.systemCancel(orders.get(0).getSn(),"主订单取消,赠送订单字段自动取消");
orderService.systemCancel(orders.get(0).getSn(),"主订单取消,赠送订单字段自动取消",true);
}
}
}

View File

@@ -7,6 +7,7 @@ import cn.lili.event.GoodsCommentCompleteEvent;
import cn.lili.event.StoreSettingChangeEvent;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.service.GoodsService;
import cn.lili.modules.goods.service.GoodsSkuService;
import cn.lili.modules.member.entity.dos.MemberEvaluation;
import cn.lili.modules.store.entity.dos.Store;
@@ -31,12 +32,15 @@ public class GoodsSkuExecute implements GoodsCommentCompleteEvent, StoreSettingC
@Autowired
private GoodsSkuService goodsSkuService;
@Autowired
private GoodsService goodsService;
@Autowired
private Cache cache;
@Override
public void goodsComment(MemberEvaluation memberEvaluation) {
goodsSkuService.updateGoodsSkuCommentNum(memberEvaluation.getSkuId());
goodsService.updateGoodsCommentNum(memberEvaluation.getGoodsId(), memberEvaluation.getSkuId());
}
@Override

View File

@@ -15,6 +15,7 @@ import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dto.OrderMessage;
import cn.lili.modules.order.order.entity.enums.OrderPromotionTypeEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum;
@@ -134,12 +135,16 @@ public class MemberPointExecute implements MemberRegisterEvent, GoodsCommentComp
@Override
public void afterSaleStatusChange(AfterSale afterSale) {
if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.COMPLETE.name())) {
Order order = orderService.getBySn(afterSale.getOrderSn());
//获取积分设置
PointSetting pointSetting = getPointSetting();
if (pointSetting.getConsumer() == 0 || !OrderStatusEnum.COMPLETED.name().equals(order.getOrderStatus())) {
return;
}
//计算扣除积分数量
Double point = CurrencyUtil.mul(pointSetting.getMoney(), afterSale.getActualRefundPrice(), 0);
Double point = CurrencyUtil.mul(pointSetting.getConsumer(), afterSale.getActualRefundPrice(), 0);
//扣除会员积分
memberService.updateMemberPoint(point.longValue(), PointTypeEnum.REDUCE.name(), afterSale.getMemberId(), "会员退款,回退积分" + point + "");
memberService.updateMemberPoint(point.longValue(), PointTypeEnum.REDUCE.name(), afterSale.getMemberId(), "会员退款,回退消费赠送积分" + point + "");
}
}

View File

@@ -1,12 +1,25 @@
package cn.lili.event.impl;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.event.AfterSaleStatusChangeEvent;
import cn.lili.event.TradeEvent;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.service.AfterSaleService;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.enums.RefundStatusEnum;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.order.order.service.TradeService;
import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 订单状态处理类
*
@@ -14,11 +27,17 @@ import org.springframework.stereotype.Service;
* @since 2020-07-03 11:20
**/
@Service
public class OrderStatusHandlerExecute implements TradeEvent {
public class OrderStatusHandlerExecute implements TradeEvent, AfterSaleStatusChangeEvent {
@Autowired
private TradeService tradeService;
@Autowired
private OrderItemService orderItemService;
@Autowired
private OrderService orderService;
@Autowired
private AfterSaleService afterSaleService;
@Override
public void orderCreate(TradeDTO tradeDTO) {
@@ -28,4 +47,41 @@ public class OrderStatusHandlerExecute implements TradeEvent {
}
}
@Override
public void afterSaleStatusChange(AfterSale afterSale) {
Order order = orderService.getBySn(afterSale.getOrderSn());
OrderItem orderItem = orderItemService.getBySn(afterSale.getOrderItemSn());
if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.COMPLETE.name())) {
if (orderItem.getReturnGoodsNumber().equals(orderItem.getNum())) {
orderItem.setIsRefund(RefundStatusEnum.ALL_REFUND.name());
} else {
orderItem.setIsRefund(RefundStatusEnum.PART_REFUND.name());
}
orderItem.setRefundPrice(CurrencyUtil.add(afterSale.getActualRefundPrice(), orderItem.getRefundPrice()));
orderItemService.updateByAfterSale(orderItem);
//循环订单货物,判断是否已经全部售后
List<OrderItem> orderItems = orderItemService.getByOrderSn(afterSale.getOrderSn());
// 总退货数量
int returnCount = 0;
// 总购买数量
int deliverCount = 0;
//获取订单货物已完成售后的数量
AfterSaleSearchParams saleSearchParams = new AfterSaleSearchParams();
saleSearchParams.setOrderSn(afterSale.getOrderSn());
saleSearchParams.setServiceStatus(AfterSaleStatusEnum.COMPLETE.name());
List<AfterSale> afterSales = afterSaleService.exportAfterSaleOrder(saleSearchParams);
for (AfterSale sale : afterSales) {
returnCount += sale.getNum();
}
//订单货物购买总数
deliverCount = order.getGoodsNum();
if (returnCount == deliverCount) {
orderService.systemCancel(afterSale.getOrderSn(),"订单货物全部退款",false);
}
}
}
}

View File

@@ -8,6 +8,7 @@ import cn.lili.modules.order.order.entity.dto.OrderMessage;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.order.order.service.StoreFlowService;
import cn.lili.modules.payment.entity.RefundLog;
import cn.lili.modules.payment.kit.Payment;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
@@ -30,6 +31,8 @@ public class PaymentExecute implements OrderStatusChangeEvent {
*/
@Autowired
private OrderService orderService;
@Autowired
private StoreFlowService storeFlowService;
@Override
public void orderChange(OrderMessage orderMessage) {
@@ -60,6 +63,8 @@ public class PaymentExecute implements OrderStatusChangeEvent {
.refundReason("订单取消")
.build();
payment.refund(refundLog);
}
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -95,7 +96,7 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
keys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId()));
int i = -orderItem.getNum();
values.add(Integer.toString(i));
setPromotionStock(keys, values, orderItem);
setPromotionStock(keys, values, orderItem, true);
}
List<Integer> stocks = cache.multiGet(keys);
@@ -133,6 +134,7 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
keys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId()));
int i = orderItem.getNum();
values.add(Integer.toString(i));
setPromotionStock(keys, values, orderItem, false);
}
//批量脚本执行库存回退
Boolean skuResult = stringRedisTemplate.execute(quantityScript, keys, values.toArray());
@@ -189,28 +191,33 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
//如果促销类型需要库存判定,则做对应处理
orderItems.forEach(orderItem -> {
if (orderItem.getPromotionType() != null) {
//如果此促销有库存概念,则计入
if (PromotionTypeEnum.haveStock(orderItem.getPromotionType())) {
String[] skuPromotions = orderItem.getPromotionType().split(",");
for (int i = 0; i < skuPromotions.length; i++) {
int currentIndex = i;
//如果此促销有库存概念,则计入
Arrays.stream(PromotionTypeEnum.haveStockPromotion).filter(promotionTypeEnum -> promotionTypeEnum.name().equals(skuPromotions[currentIndex]))
.findFirst()
.ifPresent(promotionTypeEnum -> {
String promotionId = orderItem.getPromotionId().split(",")[currentIndex];
String cacheKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, promotionId, orderItem.getSkuId());
PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(orderItem.getPromotionType());
String cacheKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId());
switch (promotionTypeEnum) {
case KANJIA:
cache.put(cacheKey, kanjiaActivityGoodsService.getKanjiaGoodsBySkuId(orderItem.getSkuId()).getStock());
return;
case POINTS_GOODS:
cache.put(cacheKey, pointsGoodsService.getPointsGoodsDetailBySkuId(orderItem.getSkuId()).getActiveStock());
return;
case SECKILL:
case PINTUAN:
cache.put(cacheKey, promotionGoodsService.getPromotionGoodsStock(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId()));
return;
default:
break;
}
switch (promotionTypeEnum) {
case KANJIA:
cache.put(cacheKey, kanjiaActivityGoodsService.getKanjiaGoodsBySkuId(orderItem.getSkuId()).getStock());
return;
case POINTS_GOODS:
cache.put(cacheKey, pointsGoodsService.getPointsGoodsDetailBySkuId(orderItem.getSkuId()).getActiveStock());
return;
case SECKILL:
case PINTUAN:
cache.put(cacheKey, promotionGoodsService.getPromotionGoodsStock(promotionTypeEnum, promotionId, orderItem.getSkuId()));
return;
default:
break;
}
});
}
}
});
}
@@ -222,7 +229,7 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
* @param orderSn 失败入库订单信息
*/
private void errorOrder(String orderSn) {
orderService.systemCancel(orderSn, outOfStockMessage);
orderService.systemCancel(orderSn, outOfStockMessage, true);
}
@@ -233,16 +240,20 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
* @param values 缓存value值
* @param sku 购物车信息
*/
private void setPromotionStock(List<String> keys, List<String> values, OrderItem sku) {
private void setPromotionStock(List<String> keys, List<String> values, OrderItem sku, boolean deduction) {
if (sku.getPromotionType() != null) {
//如果此促销有库存概念,则计入
if (!PromotionTypeEnum.haveStock(sku.getPromotionType())) {
return;
String[] skuPromotions = sku.getPromotionType().split(",");
for (int i = 0; i < skuPromotions.length; i++) {
int currentIndex = i;
Arrays.stream(PromotionTypeEnum.haveStockPromotion).filter(promotionTypeEnum -> promotionTypeEnum.name().equals(skuPromotions[currentIndex]))
.findFirst()
.ifPresent(promotionTypeEnum -> {
keys.add(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, sku.getPromotionId().split(",")[currentIndex], sku.getSkuId()));
int num = deduction ? -sku.getNum() : sku.getNum();
values.add(Integer.toString(num));
});
}
PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(sku.getPromotionType());
keys.add(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, sku.getPromotionId(), sku.getSkuId()));
int i = -sku.getNum();
values.add(Integer.toString(i));
}
}
@@ -277,41 +288,51 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
goodsSku.setId(orderItem.getSkuId());
goodsSku.setGoodsId(orderItem.getGoodsId());
//如果有促销信息
if (null != orderItem.getPromotionType() && null != orderItem.getPromotionId() && PromotionTypeEnum.haveStock(orderItem.getPromotionType())) {
if (null != orderItem.getPromotionType() && null != orderItem.getPromotionId()) {
//如果促销有库存信息
PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(orderItem.getPromotionType());
String[] skuPromotions = orderItem.getPromotionType().split(",");
for (int i = 0; i < skuPromotions.length; i++) {
int currentIndex = i;
Arrays.stream(PromotionTypeEnum.haveStockPromotion).filter(promotionTypeEnum -> promotionTypeEnum.name().equals(skuPromotions[currentIndex]))
.findFirst()
.ifPresent(promotionTypeEnum -> {
//修改砍价商品库存
String promotionId = orderItem.getPromotionId().split(",")[currentIndex];
//修改砍价商品库存
if (promotionTypeEnum.equals(PromotionTypeEnum.KANJIA)) {
KanjiaActivity kanjiaActivity = kanjiaActivityService.getById(orderItem.getPromotionId());
KanjiaActivityGoodsDTO kanjiaActivityGoodsDTO = kanjiaActivityGoodsService.getKanjiaGoodsDetail(kanjiaActivity.getKanjiaActivityGoodsId());
//修改砍价商品库存
if (promotionTypeEnum.equals(PromotionTypeEnum.KANJIA)) {
KanjiaActivity kanjiaActivity = kanjiaActivityService.getById(promotionId);
KanjiaActivityGoodsDTO kanjiaActivityGoodsDTO = kanjiaActivityGoodsService.getKanjiaGoodsDetail(kanjiaActivity.getKanjiaActivityGoodsId());
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId())).toString());
kanjiaActivityGoodsDTO.setStock(stock);
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, promotionId, orderItem.getSkuId())).toString());
kanjiaActivityGoodsDTO.setStock(stock);
kanjiaActivityGoodsService.updateById(kanjiaActivityGoodsDTO);
//修改积分商品库存
} else if (promotionTypeEnum.equals(PromotionTypeEnum.POINTS_GOODS)) {
PointsGoodsVO pointsGoodsVO = pointsGoodsService.getPointsGoodsDetail(orderItem.getPromotionId());
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId())).toString());
pointsGoodsVO.setActiveStock(stock);
pointsGoodsService.updateById(pointsGoodsVO);
} else {
PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams();
searchParams.setPromotionType(promotionTypeEnum.name());
searchParams.setPromotionId(orderItem.getPromotionId());
searchParams.setSkuId(orderItem.getSkuId());
PromotionGoods pGoods = promotionGoodsService.getPromotionsGoods(searchParams);
//记录需要更新的促销库存信息
promotionKey.add(
PromotionGoodsService.getPromotionGoodsStockCacheKey(
promotionTypeEnum,
orderItem.getPromotionId(), orderItem.getSkuId())
);
if (pGoods != null) {
promotionGoods.add(pGoods);
}
kanjiaActivityGoodsService.updateById(kanjiaActivityGoodsDTO);
//修改积分商品库存
} else if (promotionTypeEnum.equals(PromotionTypeEnum.POINTS_GOODS)) {
PointsGoodsVO pointsGoodsVO = pointsGoodsService.getPointsGoodsDetail(promotionId);
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, promotionId, orderItem.getSkuId())).toString());
pointsGoodsVO.setActiveStock(stock);
pointsGoodsService.updateById(pointsGoodsVO);
} else {
PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams();
searchParams.setPromotionType(promotionTypeEnum.name());
searchParams.setPromotionId(promotionId);
searchParams.setSkuId(orderItem.getSkuId());
PromotionGoods pGoods = promotionGoodsService.getPromotionsGoods(searchParams);
//记录需要更新的促销库存信息
promotionKey.add(
PromotionGoodsService.getPromotionGoodsStockCacheKey(
promotionTypeEnum,
promotionId, orderItem.getSkuId())
);
if (pGoods != null) {
promotionGoods.add(pGoods);
}
}
});
}
}
goodsSkus.add(goodsSku);
}
@@ -322,8 +343,6 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
for (int i = 0; i < skuStocks.size(); i++) {
goodsSkus.get(i).setQuantity(Convert.toInt(skuStocks.get(i).toString()));
}
//批量修改商品库存
goodsSkuService.updateBatchById(goodsSkus);
//促销库存处理
if (!promotionKey.isEmpty()) {
@@ -336,7 +355,7 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
promotionGoodsService.updatePromotionGoodsStock(promotionGoods);
}
//商品库存包含sku库存集合批量更新商品库存相关
goodsSkuService.updateGoodsStuck(goodsSkus);
goodsSkuService.updateGoodsStock(goodsSkus);
log.info("订单确认,库存同步:商品信息--{};促销信息---{}", goodsSkus, promotionGoods);
@@ -353,6 +372,69 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
List<GoodsSku> goodsSkus = new ArrayList<>();
//sku库存key 集合
List<String> skuKeys = new ArrayList<>();
//促销商品
List<PromotionGoods> promotionGoods = new ArrayList<>();
//促销库存key 集合
List<String> promotionKey = new ArrayList<>();
//循环订单
for (OrderItem orderItem : order.getOrderItems()) {
skuKeys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId()));
GoodsSku goodsSku = new GoodsSku();
goodsSku.setId(orderItem.getSkuId());
goodsSku.setGoodsId(orderItem.getGoodsId());
//如果有促销信息
if (null != orderItem.getPromotionType() && null != orderItem.getPromotionId()) {
//如果促销有库存信息
String[] skuPromotions = orderItem.getPromotionType().split(",");
for (int i = 0; i < skuPromotions.length; i++) {
int currentIndex = i;
Arrays.stream(PromotionTypeEnum.haveStockPromotion).filter(promotionTypeEnum -> promotionTypeEnum.name().equals(skuPromotions[currentIndex]))
.findFirst()
.ifPresent(promotionTypeEnum -> {
//修改砍价商品库存
String promotionId = orderItem.getPromotionId().split(",")[currentIndex];
//修改砍价商品库存
if (promotionTypeEnum.equals(PromotionTypeEnum.KANJIA)) {
KanjiaActivity kanjiaActivity = kanjiaActivityService.getById(promotionId);
KanjiaActivityGoodsDTO kanjiaActivityGoodsDTO = kanjiaActivityGoodsService.getKanjiaGoodsDetail(kanjiaActivity.getKanjiaActivityGoodsId());
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, promotionId, orderItem.getSkuId())).toString());
kanjiaActivityGoodsDTO.setStock(stock);
kanjiaActivityGoodsService.updateById(kanjiaActivityGoodsDTO);
//修改积分商品库存
} else if (promotionTypeEnum.equals(PromotionTypeEnum.POINTS_GOODS)) {
PointsGoodsVO pointsGoodsVO = pointsGoodsService.getPointsGoodsDetail(promotionId);
Integer stock = Integer.parseInt(cache.get(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, promotionId, orderItem.getSkuId())).toString());
pointsGoodsVO.setActiveStock(stock);
pointsGoodsService.updateById(pointsGoodsVO);
} else {
PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams();
searchParams.setPromotionType(promotionTypeEnum.name());
searchParams.setPromotionId(promotionId);
searchParams.setSkuId(orderItem.getSkuId());
PromotionGoods pGoods = promotionGoodsService.getPromotionsGoods(searchParams);
//记录需要更新的促销库存信息
promotionKey.add(
PromotionGoodsService.getPromotionGoodsStockCacheKey(
promotionTypeEnum,
promotionId, orderItem.getSkuId())
);
if (pGoods != null) {
promotionGoods.add(pGoods);
}
}
});
}
}
goodsSkus.add(goodsSku);
}
//循环订单
for (OrderItem orderItem : order.getOrderItems()) {
skuKeys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId()));
@@ -367,10 +449,19 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
for (int i = 0; i < skuStocks.size(); i++) {
goodsSkus.get(i).setQuantity(Convert.toInt(skuStocks.get(i).toString()));
}
//促销库存处理
if (!promotionKey.isEmpty()) {
List promotionStocks = cache.multiGet(promotionKey);
for (int i = 0; i < promotionKey.size(); i++) {
promotionGoods.get(i).setQuantity(Convert.toInt(promotionStocks.get(i).toString()));
Integer num = promotionGoods.get(i).getNum();
promotionGoods.get(i).setNum((num != null ? num : 0) + order.getOrder().getGoodsNum());
}
promotionGoodsService.updatePromotionGoodsStock(promotionGoods);
}
log.info("订单取消,库存还原:{}", goodsSkus);
//批量修改商品库存
goodsSkuService.updateBatchById(goodsSkus);
goodsSkuService.updateGoodsStuck(goodsSkus);
goodsSkuService.updateGoodsStock(goodsSkus);
}
}

View File

@@ -43,7 +43,7 @@ public class VerificationOrderExecute implements OrderStatusChangeEvent {
//修改虚拟订单货物可以进行售后、投诉
orderItemService.update(new LambdaUpdateWrapper<OrderItem>().eq(OrderItem::getOrderSn, orderMessage.getOrderSn())
.set(OrderItem::getAfterSaleStatus, OrderItemAfterSaleStatusEnum.NOT_APPLIED)
.set(OrderItem::getCommentStatus, OrderComplaintStatusEnum.NO_APPLY));
.set(OrderItem::getComplainStatus, OrderComplaintStatusEnum.NO_APPLY));
}
}

View File

@@ -0,0 +1,48 @@
package cn.lili.event.impl;
import cn.lili.event.OrderStatusChangeEvent;
import cn.lili.modules.order.order.entity.dto.OrderMessage;
import cn.lili.modules.wechat.service.WechatMPService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service ;
/**
* 微信小程序执行器
*
* @author Chopper
* @version v1.0 2021-04-19 14:25
*/
@Slf4j
@Service
public class WechatMPExecute implements OrderStatusChangeEvent {
@Autowired
private WechatMPService wechatMPService;
/**
* 订单已发货、待提货、待核验状态 如果是微信小程序的订单则进行 订单发货信息录入
*
* @param orderMessage 订单消息
*/
@Override
public void orderChange(OrderMessage orderMessage) {
switch (orderMessage.getNewStatus()) {
case TAKE:
case STAY_PICKED_UP:
case DELIVERED:
try {
wechatMPService.uploadShippingInfo(orderMessage.getOrderSn());
} catch (Exception e) {
log.error("发货信息录入失败", e);
}
break;
default:
break;
}
}
}

View File

@@ -292,6 +292,9 @@ public class GoodsMessageListener implements RocketMQListener<MessageExt> {
searchParams.setCategoryPath(promotions.getScopeId());
searchParams.setPageNumber(i);
searchParams.setPageSize(BATCH_SIZE);
if (CharSequenceUtil.isNotEmpty(promotions.getStoreId()) && !"0".equals(promotions.getStoreId())){
searchParams.setStoreId(promotions.getStoreId());
}
IPage<GoodsSku> goodsSkuByPage = this.goodsSkuService.getGoodsSkuByPage(searchParams);
if (goodsSkuByPage == null || goodsSkuByPage.getRecords().isEmpty()) {
break;
@@ -492,7 +495,7 @@ public class GoodsMessageListener implements RocketMQListener<MessageExt> {
}
int buyCount = goodsSku.getBuyCount() + goodsCompleteMessage.getBuyNum();
goodsSku.setBuyCount(buyCount);
goodsSkuService.update(goodsSku);
goodsSkuService.updateGoodsSkuBuyCount(goodsSku.getId(), buyCount);
this.goodsIndexService.updateIndex(MapUtil.builder(new HashMap<String, Object>()).put("id", goodsCompleteMessage.getSkuId()).build(), MapUtil.builder(new HashMap<String, Object>()).put("buyCount", buyCount).build());

View File

@@ -1,60 +0,0 @@
package cn.lili.timetask.handler.impl.bill;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.lili.modules.store.entity.dto.StoreSettlementDay;
import cn.lili.modules.store.service.BillService;
import cn.lili.modules.store.service.StoreDetailService;
import cn.lili.timetask.handler.EveryDayExecute;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 店铺结算执行
*
* @author Bulbasaur
* @since 2021/2/18 3:45 下午
*/
@Component
public class BillExecute implements EveryDayExecute {
/**
* 结算单
*/
@Autowired
private BillService billService;
/**
* 店铺详情
*/
@Autowired
private StoreDetailService storeDetailService;
/**
* 1.查询今日待结算的商家
* 2.查询商家上次结算日期,生成本次结算单
* 3.记录商家结算日
*/
@Override
public void execute() {
//获取当前天数
int day = DateUtil.date().dayOfMonth();
//获取待结算商家列表
List<StoreSettlementDay> storeList = storeDetailService.getSettlementStore(day);
//获取当前时间
DateTime endTime = DateUtil.date();
//批量商家结算
for (StoreSettlementDay storeSettlementDay : storeList) {
//生成结算单
billService.createBill(storeSettlementDay.getStoreId(), storeSettlementDay.getSettlementDay(), endTime);
//修改店铺结算时间
storeDetailService.updateSettlementDay(storeSettlementDay.getStoreId(), endTime);
}
}
}

View File

@@ -54,7 +54,7 @@ public class CancelOrderTaskExecute implements EveryMinuteExecute {
List<Order> list = orderService.list(queryWrapper);
List<String> cancelSnList = list.stream().map(Order::getSn).collect(Collectors.toList());
for (String sn : cancelSnList) {
orderService.systemCancel(sn, "超时未支付自动取消");
orderService.systemCancel(sn, "超时未支付自动取消",false);
}
}
}

View File

@@ -18,6 +18,10 @@ import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.order.order.service.StoreFlowService;
import cn.lili.modules.store.entity.dto.StoreSettlementDay;
import cn.lili.modules.store.service.BillService;
import cn.lili.modules.store.service.StoreDetailService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.OrderSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
@@ -28,6 +32,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@@ -65,6 +70,20 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
@Autowired
private DistributionOrderService distributionOrderService;
@Autowired
private StoreFlowService storeFlowService;
/**
* 结算单
*/
@Autowired
private BillService billService;
/**
* 店铺详情
*/
@Autowired
private StoreDetailService storeDetailService;
/**
* 执行每日任务
*/
@@ -92,7 +111,7 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
}
try {
//关闭允许售后申请
closeAfterSale(orderSetting);
this.closeAfterSale(orderSetting);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
@@ -102,6 +121,22 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
} catch (Exception e) {
log.error(e.getMessage(), e);
}
//修改分账状态
try {
storeFlowService.updateProfitSharingStatus();
} catch (Exception e) {
log.error("修改分账状态失败", e);
}
//生成店铺结算单
try {
createBill();
} catch (Exception e) {
log.error("生成店铺结算单", e);
}
}
/**
@@ -111,7 +146,6 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
*/
private void completedOrder(OrderSetting orderSetting) {
//订单自动收货时间 = 当前时间 - 自动收货时间天数
DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getAutoReceive());
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
@@ -144,7 +178,9 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getAutoEvaluation());
//订单完成时间 <= 订单自动好评时间
OrderItemOperationDTO orderItemOperationDTO = OrderItemOperationDTO.builder().receiveTime(receiveTime).commentStatus(CommentStatusEnum.UNFINISHED.name()).build();
OrderItemOperationDTO orderItemOperationDTO =
OrderItemOperationDTO.builder().receiveTime(receiveTime).commentStatus(CommentStatusEnum.UNFINISHED.name())
.build();
List<OrderItem> orderItems = orderItemService.waitOperationOrderItem(orderItemOperationDTO);
//判断是否有符合条件的订单,进行自动评价处理
@@ -162,53 +198,30 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
try {
memberEvaluationService.addMemberEvaluation(memberEvaluationDTO, false);
} catch (Exception e) {
// 修改订单货物评价状态为已评价避免无限调用评价异常
orderItemService.updateCommentStatus(orderItem.getSn(), CommentStatusEnum.FINISHED);
log.error(e.getMessage(), e);
}
}
}
}
/**
* 关闭允许售后申请
*
* @param orderSetting 订单设置
*/
private void closeAfterSale(OrderSetting orderSetting) {
//为0则不限制
if (orderSetting.getCloseAfterSale() == null || orderSetting.getCloseAfterSale() == 0) {
return;
}
@Transactional(rollbackFor = Exception.class)
public void closeAfterSale(OrderSetting orderSetting) {
//订单关闭售后申请时间 = 当前时间 - 自动关闭售后申请天数
DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getCloseAfterSale());
// OrderItemOperationDTO build = OrderItemOperationDTO.builder().receiveTime(receiveTime)
// .afterSaleStatus(OrderItemAfterSaleStatusEnum.NOT_APPLIED.name()).build();
// List<OrderItem> orderItems = orderItemService.waitOperationOrderItem(build);
//关闭售后订单=未售后订单+小于订单关闭售后申请时间
OrderItemOperationDTO build = OrderItemOperationDTO.builder().receiveTime(receiveTime).afterSaleStatus(OrderItemAfterSaleStatusEnum.NOT_APPLIED.name()).build();
List<OrderItem> orderItems = orderItemService.waitOperationOrderItem(build);
//判断是否有符合条件的订单,关闭允许售后申请处理
if (!orderItems.isEmpty()) {
//获取订单货物ID
List<String> orderItemIdList = orderItems.stream().map(OrderItem::getId).collect(Collectors.toList());
//修改订单售后状态
LambdaUpdateWrapper<OrderItem> lambdaUpdateWrapper = new LambdaUpdateWrapper<OrderItem>()
.set(OrderItem::getAfterSaleStatus, OrderItemAfterSaleStatusEnum.EXPIRED.name())
.in(OrderItem::getId, orderItemIdList);
orderItemService.update(lambdaUpdateWrapper);
//修改订售后状态
List<OrderItem> orderItemsList = orderItems.stream()
.map((orderItem) -> {
orderItem.setAfterSaleStatus(OrderItemAfterSaleStatusEnum.EXPIRED.name());
return orderItem;
})
.collect(Collectors.toList());
//修改对应分销订单状态
distributionOrderService.updateDistributionOrderStatus(orderItemsList);
}
orderItemService.expiredAfterSaleStatus(receiveTime);
}
@@ -227,7 +240,8 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getCloseComplaint());
//关闭售后订单=未售后订单+小于订单关闭售后申请时间
OrderItemOperationDTO build = OrderItemOperationDTO.builder().receiveTime(receiveTime).complainStatus(OrderComplaintStatusEnum.NO_APPLY.name()).build();
OrderItemOperationDTO build = OrderItemOperationDTO.builder().receiveTime(receiveTime)
.complainStatus(OrderComplaintStatusEnum.NO_APPLY.name()).build();
List<OrderItem> orderItems = orderItemService.waitOperationOrderItem(build);
//判断是否有符合条件的订单,关闭允许售后申请处理
@@ -237,12 +251,37 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
List<String> orderItemIdList = orderItems.stream().map(OrderItem::getId).collect(Collectors.toList());
//修改订单投诉状态
LambdaUpdateWrapper<OrderItem> lambdaUpdateWrapper = new LambdaUpdateWrapper<OrderItem>()
.set(OrderItem::getComplainStatus, OrderItemAfterSaleStatusEnum.EXPIRED.name())
.in(OrderItem::getId, orderItemIdList);
LambdaUpdateWrapper<OrderItem> lambdaUpdateWrapper =
new LambdaUpdateWrapper<OrderItem>().set(OrderItem::getComplainStatus,
OrderItemAfterSaleStatusEnum.EXPIRED.name()).in(OrderItem::getId, orderItemIdList);
orderItemService.update(lambdaUpdateWrapper);
}
}
/**
* 1.查询今日待结算的商家
* 2.查询商家上次结算日期,生成本次结算单
* 3.记录商家结算日
*/
private void createBill() {
//获取当前天数
int day = DateUtil.date().dayOfMonth();
//获取待结算商家列表
List<StoreSettlementDay> storeList = storeDetailService.getSettlementStore(day);
//获取当前时间
DateTime endTime = DateUtil.date();
//批量商家结算
for (StoreSettlementDay storeSettlementDay : storeList) {
//生成结算单
billService.createBill(storeSettlementDay.getStoreId(), storeSettlementDay.getSettlementDay(), endTime);
//修改店铺结算时间
storeDetailService.updateSettlementDay(storeSettlementDay.getStoreId(), endTime);
}
}
}

View File

@@ -72,6 +72,7 @@ public class PromotionEverydayExecute implements EveryDayExecute {
log.info("生成秒杀活动设置:{}", seckillSetting);
for (int i = 1; i <= SeckillService.PRE_CREATION; i++) {
Seckill seckill = new Seckill(i, seckillSetting.getHours(), seckillSetting.getSeckillRule());
seckill.setApplyEndTime(null);
//如果已经存在促销,则不再次保存
if (seckillService.list(

View File

@@ -0,0 +1,31 @@
package cn.lili.trigger.executor;
import cn.hutool.json.JSONUtil;
import cn.lili.modules.promotion.service.CouponActivityService;
import cn.lili.trigger.TimeTriggerExecutor;
import cn.lili.trigger.message.CouponActivityMessage;
import cn.lili.trigger.model.TimeExecuteConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 优惠券活动精准发圈延时触发
*
* @author Bulbasaur
* @since 2021/6/1 5:02 下午
*/
@Slf4j
@Component(TimeExecuteConstant.COUPON_ACTIVITY_EXECUTOR)
public class CouponActivityTriggerExecutor implements TimeTriggerExecutor {
@Autowired
private CouponActivityService couponActivityService;
@Override
public void execute(Object object) {
CouponActivityMessage couponActivityMessage = JSONUtil.toBean(JSONUtil.parseObj(object), CouponActivityMessage.class);
couponActivityService.specifyCoupon(couponActivityMessage.getCouponActivityId());
}
}

View File

@@ -1,64 +0,0 @@
package cn.lili.buyer.test.cart;
import cn.lili.common.utils.DateUtil;
import cn.lili.timetask.handler.impl.statistics.OnlineMemberStatistics;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Calendar;
import java.util.Random;
/**
* 订单库存扣减
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest
class OnlineTest {
@Autowired
private OnlineMemberStatistics onlineMemberStatistics;
//订单支付,库存扣减单元测试
@Test
void everyHour() {
onlineMemberStatistics.execute();
}
//订单支付,库存扣减单元测试
@Test
void customSetting() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 48 );
//循环填充数据
for (int i = 0; i < 48; i++) {
calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1);
System.out.println(DateUtil.toString(calendar.getTime(),""));
Random random = new Random();
onlineMemberStatistics.execute(calendar.getTime(), random.nextInt(1000000));
}
}
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 48 - 1);
//循环填充数据
for (int i = 0; i < 48; i++) {
calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1);
System.out.println(calendar.getTime().getTime());
}
}
}

View File

@@ -15,15 +15,6 @@
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
@@ -468,6 +459,11 @@
<scope>runtime</scope>
<classifier>osx-aarch_64</classifier>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.15</version>
</dependency>
</dependencies>

View File

@@ -1,6 +1,11 @@
package cn.lili.common.enums;
import cn.lili.common.utils.StringUtils;
import java.util.Arrays;
import java.util.EnumSet;
/**
* 促销分类枚举
*
@@ -23,7 +28,12 @@ public enum PromotionTypeEnum {
/**
* 有促销库存的活动类型
*/
static final PromotionTypeEnum[] haveStockPromotion = new PromotionTypeEnum[]{PINTUAN, SECKILL, KANJIA, POINTS_GOODS};
public static final PromotionTypeEnum[] haveStockPromotion = new PromotionTypeEnum[]{PINTUAN, SECKILL, KANJIA, POINTS_GOODS};
/**
* 有独立促销库存的活动类型
*/
public static final PromotionTypeEnum[] haveIndependanceStockPromotion = new PromotionTypeEnum[]{SECKILL};
private final String description;
@@ -47,4 +57,28 @@ public enum PromotionTypeEnum {
return description;
}
/**
* 判断促销类型是否有效
* @param typeEnumValue
* @return
*/
public static boolean isValid(String typeEnumValue) {
if (StringUtils.isBlank(typeEnumValue)) {
return false;
}
return Arrays.stream(PromotionTypeEnum.values()).anyMatch(c -> c.name().equals(typeEnumValue));
}
/**
* 判断订单类型是否可售后
* POINTS\KANJIA 两种促销类型的订单不可进行售后
* @return true 可售后 false 不可售后
*/
public static boolean isCanAfterSale(String promotionType) {
if (!isValid(promotionType)) {
return true;
}
EnumSet<PromotionTypeEnum> noAfterSale = EnumSet.of(PromotionTypeEnum.KANJIA, PromotionTypeEnum.POINTS_GOODS);
return !noAfterSale.contains(PromotionTypeEnum.valueOf(promotionType));
}
}

View File

@@ -42,6 +42,7 @@ public enum ResultCode {
PLATFORM_NOT_SUPPORTED_IM(1006, "平台未开启IM"),
STORE_NOT_SUPPORTED_IM(1007, "店铺未开启IM"),
UNINITIALIZED_PASSWORD(1008, "非初始化密码,无法进行初始化设置"),
FILE_DIRECTORY_NOT_EMPTY(1012, "文件夹下有文件,请删除所有文件后删除文件夹!"),
/**
* 分类
*/
@@ -89,6 +90,7 @@ public enum ResultCode {
VIRTUAL_GOODS_NOT_NEED_TEMP(11015, "虚拟商品无需选择配送模板"),
GOODS_NOT_EXIST_STORE(11017, "当前用户无权操作此商品"),
GOODS_TYPE_ERROR(11016, "需选择商品类型"),
GOODS_STOCK_IMPORT_ERROR(11018, "导入商品库存失败,请检查表格数据"),
/**
* 参数
@@ -150,6 +152,7 @@ public enum ResultCode {
CLERK_DISABLED_ERROR(20031, "店员已禁用"),
CLERK_CURRENT_SUPPER(20032, "无法删除当前登录店员"),
CANT_EDIT_CLERK_SHOPKEEPER(20033, "无法在店员管理编辑店员信息"),
USER_MOBILE_REPEATABLE_ERROR(20034, "该手机号已存在"),
/**
* 权限
*/
@@ -173,6 +176,7 @@ public enum ResultCode {
DISTRIBUTION_RETREAT_ERROR(22004, "分销员清退失败"),
DISTRIBUTION_CASH_NOT_EXIST(22005, "分销员提现记录不存在"),
DISTRIBUTION_GOODS_DOUBLE(22006, "不能重复添加分销商品"),
DISTRIBUTION_EDIT_ERROR(22007, "修改分销员失败"),
/**
* 购物车
@@ -205,6 +209,7 @@ public enum ResultCode {
POINT_NOT_ENOUGH(31015, "当前会员积分不足购买当前积分商品!"),
ORDER_LABEL_ORDER_ERROR(31016, "订单不能打印电子面单"),
ORDER_PRICE_ERROR(31017,"订单金额不能小于等于0"),
ORDER_PACKAGE_NOT_EXIST(31017, "当前订单包裹不存在!"),
/**
@@ -418,7 +423,7 @@ public enum ResultCode {
STORE_CLOSE_ERROR(50006, "店铺关闭,请联系管理员"),
STORE_DELIVER_GOODS_ADDRESS(50007,"请填写商家发货地址"),
FREIGHT_TEMPLATE_NOT_EXIST(50010, "当前模版不存在"),
STORE_STATUS_ERROR(50011, "店铺状态异常,无法申请"),
STORE_STATUS_ERROR(50011, "店铺在申请中或审核中,请勿重复操作"),
STORE_DELIVER_ADDRESS_EXIST(50012,"请填写发货地址"),
/**
@@ -495,6 +500,9 @@ public enum ResultCode {
WECHAT_MP_MESSAGE_TMPL_ERROR(80306, "未能获取到微信模版消息id"),
WECHAT_ERROR(80307, "微信接口异常"),
APP_VERSION_EXIST(80307, "APP版本已存在"),
APP_VERSION_PARAM_ERROR(80308, "添加APP版本参数异常"),
// 未选择APP类型
APP_VERSION_TYPE_ERROR(80308, "请选择有效的APP类型"),
/**
* IM

View File

@@ -131,4 +131,13 @@ public class ResultUtil<T> {
return this.resultMessage;
}
/**
* 返回失败
*
* @return 消息
*/
public static <T> ResultMessage<T> error() {
return new ResultUtil<T>().setErrorMsg(ResultCode.ERROR);
}
}

View File

@@ -1,5 +1,6 @@
package cn.lili.common.exception;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.vo.ResultMessage;
@@ -42,26 +43,21 @@ public class GlobalControllerExceptionHandler {
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ResultMessage<Object> handleServiceException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
//如果是自定义异常,则获取异常,返回自定义错误消息
if (e instanceof ServiceException) {
ServiceException serviceException = ((ServiceException) e);
ResultCode resultCode = serviceException.getResultCode();
Integer code = null;
String message = null;
Integer code = resultCode.code();
String message = resultCode.message();
if (resultCode != null) {
code = resultCode.code();
message = resultCode.message();
}
//如果有扩展消息,则输出异常中,跟随补充异常
if (!serviceException.getMsg().equals(ServiceException.DEFAULT_MESSAGE)) {
message += ":" + serviceException.getMsg();
if (message != null) {
message = appendErrorMessage(message, serviceException.getMsg());
}
// 对一些特殊异常处理不再打印error级别的日志
assert serviceException.getResultCode() != null;
if (serviceException.getResultCode().equals(ResultCode.DEMO_SITE_EXCEPTION)) {
log.debug("[DEMO_SITE_EXCEPTION]:{}", serviceException.getResultCode().message(), e);
return ResultUtil.error(code, message);
@@ -103,7 +99,30 @@ public class GlobalControllerExceptionHandler {
log.error("全局异常[RuntimeException]:", e);
return ResultUtil.error(ResultCode.ERROR);
// 检查异常链是否包含 ServiceException
ServiceException serviceException = findServiceException(e);
if (serviceException != null) {
ResultCode resultCode = serviceException.getResultCode();
Integer code = resultCode.code();
String message = resultCode.message();
if (message != null) {
message = appendErrorMessage(message, serviceException.getMsg());
}
return ResultUtil.error(code, message);
}
return ResultUtil.error();
}
// 遍历异常链,查找 ServiceException
private ServiceException findServiceException(Throwable ex) {
while (ex != null) {
if (ex instanceof ServiceException) {
return (ServiceException) ex;
}
ex = ex.getCause();
}
return null;
}
// /**
@@ -145,7 +164,8 @@ public class GlobalControllerExceptionHandler {
if (!fieldErrors.isEmpty()) {
return ResultUtil.error(ResultCode.PARAMS_ERROR.code(),
fieldErrors.stream()
.map(FieldError::getDefaultMessage) // 获取每个对象的名称字段
// 获取每个对象的名称字段
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", ")));
}
return ResultUtil.error(ResultCode.PARAMS_ERROR);
@@ -168,4 +188,42 @@ public class GlobalControllerExceptionHandler {
ConstraintViolationException exception = (ConstraintViolationException) e;
return ResultUtil.error(ResultCode.PARAMS_ERROR.code(), exception.getMessage());
}
/**
* 拼接错误消息
*
* @param message 原始消息
* @param appendMessage 需要拼接的消息
* @return 拼接后的消息
*/
private String appendErrorMessage(String message, String appendMessage) {
//这里的代码看起来有点乱,简单解释一下
//场景1服务A服务B=》
// 服务A调用服务B=》
// 服务B抛出异常{扩展消息},拼接后成为{默认消息}{扩展消息}
// 异常被服务A捕获=》
// 最终消息拼接过程中当前方法体参数message是{默认消息}参数appendMessage是服务A给的{默认消息}+{扩展消息},最终会形成{默认消息}+{默认消息}+{扩展消息}
//场景2只有服务A=》
// 服务A抛出异常{扩展消息}=》
// 当前方法体拼接{默认消息}{扩展消息} 并输出返回。
//
//总的来说由于消息拼接是一个流式传递由服务间传递所以这里的消息可能存在A包含B也可能出现B包含A
// 所以这里需要双重判定A包含B=》返回AB包含A=》返回B否则返回拼接后的消息
if (message.contains(appendMessage)) {
return message;
}
if (appendMessage.contains(message)) {
return appendMessage;
}
//忽略默认错误信息,如果有其他错误消息体就不再返回默认的错误消息
if (message.equals(ResultCode.ERROR.message())) {
return appendMessage;
}
if (appendMessage.equals(ResultCode.ERROR.message())) {
return message;
}
return CharSequenceUtil.format("{}-{}", message, appendMessage);
}
}

View File

@@ -15,12 +15,11 @@ public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 3447728300174142127L;
public static final String DEFAULT_MESSAGE = "网络错误,请稍后重试!";
/**
* 异常消息
*/
private String msg = DEFAULT_MESSAGE;
private String msg = ResultCode.ERROR.message();
/**
* 错误码

View File

@@ -14,23 +14,23 @@ import org.springframework.context.annotation.Configuration;
public class ApiProperties {
/**
* 买家api
*/
private String buyer;
// /**
// * 买家api
// */
// private String buyer;
/**
* 管理端域名
*/
private String store;
/**
* 管理端域名
*/
private String manager;
/**
* 管理端域名
*/
private String common;
// /**
// * 管理端域名
// */
// private String store;
//
// /**
// * 管理端域名
// */
// private String manager;
//
// /**
// * 管理端域名
// */
// private String common;
}

View File

@@ -92,8 +92,9 @@ public class UserContext {
try {
//获取token的信息
Claims claims
= Jwts.parser()
= Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(accessToken).getBody();
//获取存储在claims中的用户信息
String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString();

View File

@@ -144,7 +144,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public ServletInputStream getInputStream() throws IOException {
BufferedReader bufferedReader = null;
InputStreamReader reader = null;
//获取输入流
@@ -163,47 +162,55 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
//继续读取下一行流直到line为空
line = bufferedReader.readLine();
}
if (CharSequenceUtil.isNotEmpty(body) && Boolean.TRUE.equals(JSONUtil.isJsonObj(body.toString()))) {
//将body转换为map
Map<String, Object> map = JSONUtil.parseObj(body.toString());
//创建空的map用于存储结果
Map<String, Object> resultMap = new HashMap<>(map.size());
//遍历数组
for (Map.Entry<String, Object> entry : map.entrySet()) {
//如果map.get(key)获取到的是字符串就需要进行处理如果不是直接存储resultMap
if (map.get(entry.getKey()) instanceof String) {
resultMap.put(entry.getKey(), filterXss(entry.getKey(), entry.getValue().toString()));
} else {
resultMap.put(entry.getKey(), entry.getValue());
}
// 兼容替换:不再使用过时的 JSONUtil.isJsonObj(String),改为尝试解析并捕获异常
if (CharSequenceUtil.isNotEmpty(body)) {
Map<String, Object> map = null;
try {
map = JSONUtil.parseObj(body.toString());
} catch (Exception ignore) {
map = null;
}
//将resultMap转换为json字符串
String resultStr = JSONUtil.toJsonStr(resultMap);
//将json字符串转换为字节
final ByteArrayInputStream resultBIS = new ByteArrayInputStream(resultStr.getBytes(StandardCharsets.UTF_8));
//实现接口
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
if (map != null) {
//创建空的map用于存储结果
Map<String, Object> resultMap = new HashMap<>(map.size());
//遍历数组
for (Map.Entry<String, Object> entry : map.entrySet()) {
//如果map.get(key)获取到的是字符串就需要进行处理如果不是直接存储resultMap
if (map.get(entry.getKey()) instanceof String) {
resultMap.put(entry.getKey(), filterXss(entry.getKey(), entry.getValue().toString()));
} else {
resultMap.put(entry.getKey(), entry.getValue());
}
}
@Override
public boolean isReady() {
return false;
}
//将resultMap转换为json字符串
String resultStr = JSONUtil.toJsonStr(resultMap);
//将json字符串转换为字节
final ByteArrayInputStream resultBIS = new ByteArrayInputStream(resultStr.getBytes(StandardCharsets.UTF_8));
@Override
public void setReadListener(ReadListener readListener) {
}
//实现接口
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public int read() {
return resultBIS.read();
}
};
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return resultBIS.read();
}
};
}
}
//将json字符串转换为字节

View File

@@ -65,8 +65,9 @@ public class TokenUtil {
Claims claims;
try {
claims = Jwts.parser()
claims = Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(oldRefreshToken).getBody();
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException |
IllegalArgumentException e) {

View File

@@ -34,4 +34,11 @@ public class CommonUtil {
return sb.toString();
}
/**
* 获取特定字符 + 6位随机数
* @return
*/
public static String getSpecialStr(String value) {
return value + getRandomNum();
}
}

View File

@@ -125,13 +125,21 @@ public final class CurrencyUtil {
return (int) price;
}
public static Long getFenLong(Double money) {
BigDecimal bigDecimalValue = BigDecimal.valueOf(money);
// 乘以 100 并四舍五入到最接近的整数
BigDecimal fenValue = bigDecimalValue.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP);
return fenValue.longValue();
}
/**
* 金额转分
*
* @param money 金额
* @return double类型分
*/
public static double reversalFen(Double money) {
public static double reversalFen(Integer money) {
return div(money, 100);
}
}

View File

@@ -23,7 +23,8 @@ public class ThreadPoolUtil {
* 线程缓冲队列
*/
private static final BlockingQueue<Runnable> BQUEUE = new ArrayBlockingQueue<Runnable>(100);
private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, BQUEUE, new ThreadPoolExecutor.CallerRunsPolicy());
private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, BQUEUE,
new ThreadPoolExecutor.CallerRunsPolicy());
/**
* volatile禁止指令重排
*/
@@ -34,7 +35,7 @@ public class ThreadPoolUtil {
}
/**
* 无返回值直接执行, 管他娘的
* 执行方法
*
* @param runnable
*/
@@ -43,7 +44,7 @@ public class ThreadPoolUtil {
}
/**
* 返回值直接执行, 管他娘的
* 提交返回值
*
* @param callable
*/

View File

@@ -22,7 +22,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {PhoneValidator.class})
public @interface Phone {
String regexp() default "1[3|4|5|7|8]\\d{9}";
String regexp() default "1[3|4|5|7|8|9]\\d{9}";
String message() default "手机号码格式不正确";

View File

@@ -90,7 +90,8 @@ public abstract class BaseElasticsearchService {
request.settings(Settings.builder()
.put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards())
.put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas())
.put("index.max_result_window", 100000) //最大查询结果数
//最大查询结果数
.put("index.max_result_window", 100000)
.put("index.mapping.total_fields.limit", 2000));
//创建索引

View File

@@ -66,14 +66,16 @@ public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
.setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder
.setConnectTimeout(1000)
.setSocketTimeout(12 * 1000));
.setSocketTimeout(12 * 1000)
.setConnectionRequestTimeout(1000));
}
private HttpAsyncClientBuilder configureHttpClientBuilder(HttpAsyncClientBuilder httpClientBuilder,
CredentialsProvider credentialsProvider) {
httpClientBuilder
.setKeepAliveStrategy(getConnectionKeepAliveStrategy())
.setMaxConnPerRoute(10)
.setMaxConnPerRoute(50)
.setMaxConnTotal(200)
.setDefaultIOReactorConfig(
IOReactorConfig
.custom()

View File

@@ -55,7 +55,7 @@ public class BaseAuthWeChatPCRequest extends BaseAuthRequest {
return ConnectAuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.username(object.getString("unionid"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)

View File

@@ -57,7 +57,7 @@ public class BaseAuthWeChatRequest extends BaseAuthRequest {
return ConnectAuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.username(object.getString("unionid"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)

View File

@@ -5,7 +5,6 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ClientTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
@@ -125,24 +124,18 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
@Transactional
public Token miniProgramAutoLogin(WechatMPLoginParams params) {
Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid());
Map<String, String> map = new HashMap<>(3);
if (cacheData == null) {
//得到微信小程序联合登陆信息
JSONObject json = this.getConnect(params.getCode());
//存储session key 后续登录用得到
String sessionKey = json.getStr("session_key");
String unionId = json.getStr("unionid");
String openId = json.getStr("openid");
map.put("sessionKey", sessionKey);
map.put("unionId", unionId);
map.put("openId", openId);
cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid(), map, 900L);
} else {
map = (Map<String, String>) cacheData;
}
//微信联合登陆参数
//得到微信小程序联合登陆信息
JSONObject json = this.getConnect(params.getCode());
//存储session key 后续登录用得到
String sessionKey = json.getStr("session_key");
String unionId = json.getStr("unionid");
String openId = json.getStr("openid");
map.put("sessionKey", sessionKey);
map.put("unionId", unionId);
map.put("openId", openId);
//微信联合登陆参数
return phoneMpBindAndLogin(map.get("sessionKey"), params, map.get("openId"), map.get("unionId"));
}
@@ -217,7 +210,7 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
connectQueryDTO.getUnionType())
.eq(CharSequenceUtil.isNotEmpty(connectQueryDTO.getUnionId()), Connect::getUnionId,
connectQueryDTO.getUnionId());
return this.getOne(queryWrapper,false);
return this.getOne(queryWrapper, false);
}
@Override
@@ -267,13 +260,13 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
private Token unionLoginCallback(ConnectAuthUser authUser, boolean longTerm) {
try {
Member member =null;
Member member = null;
//判断是否传递手机号,如果传递手机号则使用手机号登录
if(StrUtil.isNotBlank(authUser.getPhone())){
if (StrUtil.isNotBlank(authUser.getPhone())) {
member = memberService.findByMobile(authUser.getPhone());
}
//如果未查到手机号的会员则使用第三方登录
if(member==null){
if (member == null) {
LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<Connect>();
//使用UnionId登录
if (authUser.getToken() != null && StrUtil.isNotBlank(authUser.getToken().getUnionId())) {

View File

@@ -23,6 +23,7 @@ import cn.lili.modules.connect.request.BaseAuthWeChatPCRequest;
import cn.lili.modules.connect.request.BaseAuthWeChatRequest;
import cn.lili.modules.connect.service.ConnectService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.connect.ConnectSetting;
import cn.lili.modules.system.entity.dto.connect.QQConnectSetting;
import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting;
import cn.lili.modules.system.entity.dto.connect.dto.QQConnectSettingItem;
@@ -70,8 +71,8 @@ public class ConnectUtil {
* @param connectAuthEnum 用户枚举
* @return 回调地址
*/
String getRedirectUri(ConnectAuthEnum connectAuthEnum) {
return apiProperties.getBuyer() + prefix + connectAuthEnum.getName();
String getRedirectUri(ConnectAuthEnum connectAuthEnum,String callbackUrl) {
return callbackUrl + prefix + connectAuthEnum.getName();
}
/**
@@ -106,10 +107,13 @@ public class ConnectUtil {
//缓存写入登录结果300秒有效
cache.put(CachePrefix.CONNECT_RESULT.getPrefix() + callback.getCode(), resultMessage, 300L);
//登录设置
ConnectSetting connectSetting = JSONUtil.toBean(settingService.get(SettingEnum.CONNECT_SETTING.name()).getSettingValue(), ConnectSetting.class);
//跳转地址
String url = this.check(httpServletRequest.getHeader("user-agent")) ?
domainProperties.getWap() + "/pages/passport/login?state=" + callback.getCode() :
domainProperties.getPc() + "/login?state=" + callback.getCode();
connectSetting.getWap() + "/pages/passport/login?state=" + callback.getCode() :
connectSetting.getPc() + "/login?state=" + callback.getCode();
try {
httpServletResponse.sendRedirect(url);
@@ -151,13 +155,15 @@ public class ConnectUtil {
//寻找配置
Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name());
WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class);
//登录设置
ConnectSetting connectSetting = JSONUtil.toBean(settingService.get(SettingEnum.CONNECT_SETTING.name()).getSettingValue(), ConnectSetting.class);
for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) {
if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.H5.name())) {
authRequest = new BaseAuthWeChatRequest(AuthConfig.builder()
.clientId(wechatConnectSettingItem.getAppId())
.clientSecret(wechatConnectSettingItem.getAppSecret())
.redirectUri(getRedirectUri(authInterface))
.redirectUri(getRedirectUri(authInterface, connectSetting.getCallbackUrl()))
.build(), cache);
}
}
@@ -167,13 +173,14 @@ public class ConnectUtil {
//寻找配置
Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name());
WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class);
//登录设置
ConnectSetting connectSetting = JSONUtil.toBean(settingService.get(SettingEnum.CONNECT_SETTING.name()).getSettingValue(), ConnectSetting.class);
for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) {
if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.PC.name())) {
authRequest = new BaseAuthWeChatPCRequest(AuthConfig.builder()
.clientId(wechatConnectSettingItem.getAppId())
.clientSecret(wechatConnectSettingItem.getAppSecret())
.redirectUri(getRedirectUri(authInterface))
.redirectUri(getRedirectUri(authInterface, connectSetting.getCallbackUrl()))
.build(), cache);
}
}
@@ -184,13 +191,14 @@ public class ConnectUtil {
//寻找配置
Setting setting = settingService.get(SettingEnum.QQ_CONNECT.name());
QQConnectSetting qqConnectSetting = JSONUtil.toBean(setting.getSettingValue(), QQConnectSetting.class);
//登录设置
ConnectSetting connectSetting = JSONUtil.toBean(settingService.get(SettingEnum.CONNECT_SETTING.name()).getSettingValue(), ConnectSetting.class);
for (QQConnectSettingItem qqConnectSettingItem : qqConnectSetting.getQqConnectSettingItemList()) {
if (qqConnectSettingItem.getClientType().equals(ClientTypeEnum.PC.name())) {
authRequest = new BaseAuthQQRequest(AuthConfig.builder()
.clientId(qqConnectSettingItem.getAppId())
.clientSecret(qqConnectSettingItem.getAppKey())
.redirectUri(getRedirectUri(authInterface))
.redirectUri(getRedirectUri(authInterface, connectSetting.getCallbackUrl()))
//这里qq获取unionid 需要配置为true详情可以查阅属性说明内部有帮助文档
.unionId(true)
.build(), cache);

View File

@@ -36,6 +36,7 @@ public class Distribution extends BaseEntity {
this.rebateTotal=0D;
this.canRebate=0D;
this.commissionFrozen=0D;
this.distributionOrderPrice=0D;
this.distributionStatus = DistributionStatusEnum.APPLY.name();
BeanUtil.copyProperties(distributionApplyDTO, this);
}
@@ -85,4 +86,7 @@ public class Distribution extends BaseEntity {
@ApiModelProperty(value = "结算银行开户支行名称")
private String settlementBankBranchName;
@ApiModelProperty(value = "分销订单金额")
private Double distributionOrderPrice;
}

View File

@@ -9,9 +9,11 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
@@ -51,11 +53,30 @@ public class DistributionCash extends BaseEntity {
@ApiModelProperty(value = "状态")
private String distributionCashStatus;
public DistributionCash(String sn, String distributionId, Double price, String memberName) {
@ApiModelProperty(value = "会员姓名")
private String name;
@ApiModelProperty(value = "身份证号")
private String idNumber;
@ApiModelProperty(value = "结算银行开户行名称")
private String settlementBankAccountName;
@ApiModelProperty(value = "结算银行开户账号")
private String settlementBankAccountNum;
@ApiModelProperty(value = "结算银行开户支行名称")
private String settlementBankBranchName;
public DistributionCash(String sn,Double price, Distribution distribution) {
this.sn = sn;
this.distributionId = distributionId;
this.distributionId = distribution.getId();
this.price = price;
this.distributionCashStatus = WithdrawStatusEnum.APPLY.name();
this.distributionName = memberName;
this.distributionName = distribution.getMemberName();
this.name = distribution.getName();
this.idNumber = distribution.getIdNumber();
this.settlementBankAccountName = distribution.getSettlementBankAccountName();
this.settlementBankAccountNum = distribution.getSettlementBankAccountNum();
this.settlementBankBranchName = distribution.getSettlementBankBranchName();
}
}

View File

@@ -79,6 +79,8 @@ public class DistributionOrder extends BaseIdEntity {
private String image;
@ApiModelProperty(value = "商品数量")
private Integer num;
@ApiModelProperty(value = "退款商品数量")
private Integer refundNum;
public DistributionOrder(StoreFlow storeFlow) {
distributionOrderStatus = DistributionOrderStatusEnum.NO_COMPLETED.name();
@@ -95,6 +97,8 @@ public class DistributionOrder extends BaseIdEntity {
specs = storeFlow.getSpecs();
image = storeFlow.getImage();
num = storeFlow.getNum();
refundNum=0;
sellBackRebate=0D;
}
}

View File

@@ -6,16 +6,10 @@ package cn.lili.modules.distribution.entity.enums;
* @author pikachu
*/
public enum DistributionOrderStatusEnum {
//未完成
NO_COMPLETED("未完成"),
//待结算(冻结)
WAIT_BILL("待结算"),
//待提现
WAIT_CASH("待提现"),
//已提现
COMPLETE_CASH("提现完成"),
//订单取消
CANCEL("订单取消"),
//完成
COMPLETE("完成"),
//订单取消
REFUND("退款");

View File

@@ -13,25 +13,32 @@ import org.apache.ibatis.annotations.Update;
*/
public interface DistributionMapper extends BaseMapper<Distribution> {
/**
* 修改分销员可提现金额
*
* @param commissionFrozen 分销金额
* @param distributionId 分销员ID
*/
@Update("UPDATE li_distribution set commission_frozen = (IFNULL(commission_frozen,0)+#{commissionFrozen}) " +
", rebate_total=(IFNULL(rebate_total,0)+#{commissionFrozen}) WHERE id = #{distributionId}")
void subCanRebate(Double commissionFrozen, String distributionId);
@Update("UPDATE li_distribution set commission_frozen = (IFNULL(commission_frozen,0) - #{commissionFrozen}) " +
", rebate_total=(IFNULL(rebate_total,0) - #{commissionFrozen}) " +
", distribution_order_count=(IFNULL(distribution_order_count,0)-1) " +
" WHERE id = #{distributionId}")
void subRebate(Double commissionFrozen, String distributionId, Double distributionOrderPrice);
/**
* 添加分销金额
*
* @param commissionFrozen 分销金额
* @param distributionId 分销员ID
*/
@Update("UPDATE li_distribution set commission_frozen = (IFNULL(commission_frozen,0)+#{commissionFrozen}) " +
", rebate_total=(IFNULL(rebate_total,0)+#{commissionFrozen}) " +
", distribution_order_count=(IFNULL(distribution_order_count,0)+1) WHERE id = #{distributionId}")
void addCanRebate(Double commissionFrozen, String distributionId);
", distribution_order_price=(IFNULL(distribution_order_price,0)+#{distributionOrderPrice}) " +
", distribution_order_count=(IFNULL(distribution_order_count,0)+1) " +
" WHERE id = #{distributionId}")
void addRebate(Double commissionFrozen, String distributionId, Double distributionOrderPrice);
@Update("UPDATE li_distribution SET commission_frozen = (IFNULL(commission_frozen,0) - #{rebate}) " +
",can_rebate=(IFNULL(can_rebate,0) + #{rebate}) " +
" WHERE id = #{distributionId}")
void addCanRebate(Double rebate, String distributionId);
@Update("UPDATE li_distribution SET can_rebate=(IFNULL(can_rebate,0) - #{rebate}),cash_rebate=(IFNULL(cash_rebate,0) + #{rebate}) " +
" WHERE id = #{distributionId}")
void addCashRebate(Double rebate, String distributionId);
@Update("UPDATE li_distribution SET cash_rebate=(IFNULL(cash_rebate,0) - #{rebate}) " +
" WHERE id = #{distributionId}")
void subCashRebate(Double rebate, String distributionId);
}

View File

@@ -3,6 +3,9 @@ package cn.lili.modules.distribution.mapper;
import cn.hutool.core.date.DateTime;
import cn.lili.modules.distribution.entity.dos.DistributionOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
/**
@@ -24,4 +27,15 @@ public interface DistributionOrderMapper extends BaseMapper<DistributionOrder> {
",d.commission_frozen =(ifnull(d.commission_frozen,0) -(SELECT SUM( dorder.rebate ) FROM li_distribution_order AS dorder WHERE dorder.distribution_order_status = #{distributionOrderStatus} AND dorder.settle_cycle< #{settleCycle} AND dorder.distribution_id = d.id ) )")
void rebate(String distributionOrderStatus, DateTime settleCycle);
/**
* 查询待结算的分销订单
* @return 待结算的分销订单
*/
@Select("UPDATE li_distribution_order distribution_order"
+ " INNER JOIN li_order_item oi ON oi.sn = distribution_order.order_item_sn"
+ " SET distribution_order.distribution_order_status = 'WAIT_BILL',"
+ " distribution_order.settle_cycle = #{settleCycle} "
+ " WHERE distribution_order.distribution_order_status = 'NO_COMPLETED'"
+ " AND oi.after_sale_status = 'EXPIRED';")
List<DistributionOrder> distributionSettlementOrder(Date settleCycle);
}

View File

@@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletResponse;
/**
* 分销佣金业务层
*
@@ -40,6 +42,9 @@ public interface DistributionCashService extends IService<DistributionCash> {
*/
IPage<DistributionCash> getDistributionCash(DistributionCashSearchParams distributionCashSearchParams);
void queryExport(HttpServletResponse response,DistributionCashSearchParams distributionCashSearchParams);
/**
* 审核分销提现申请
*

View File

@@ -3,7 +3,9 @@ package cn.lili.modules.distribution.service;
import cn.hutool.core.date.DateTime;
import cn.lili.modules.distribution.entity.dos.DistributionOrder;
import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.dos.StoreFlow;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -44,22 +46,12 @@ public interface DistributionOrderService extends IService<DistributionOrder> {
/**
* 订单退款
* 记录分销订单
*
* @param afterSaleSn 售后单号
*/
void refundOrder(String afterSaleSn);
void refundOrder(AfterSale afterSale);
/**
* 分销订单状态修改
*
* @param orderItems
* 分销订单完成
*/
void updateDistributionOrderStatus(List<OrderItem> orderItems);
void completeOrder(StoreFlow storeFlow);
/**
* 分销订单结算
* @param dateTime
* @param distributionOrderStatus
*/
void updateRebate(DateTime dateTime, String distributionOrderStatus);
}

View File

@@ -78,18 +78,42 @@ public interface DistributionService extends IService<Distribution> {
void checkDistributionSetting();
/**
* 修改可提现金额
*
* @param canRebate 修改金额
* @param distributionId 分销员ID
*/
void subCanRebate(Double canRebate, String distributionId);
/**
* 添加分销金额
* 添加分销冻结金额
* 创建分销订单时进行调用
*
* @param rebate 金额
* @param distributionId 分销员ID
* @param distributionOrderPrice 分销订单金额
*/
void addRebate(Double rebate, String distributionId);
void addRebate(Double rebate, String distributionId, Double distributionOrderPrice);
/**
* 扣减分销冻结金额
* 订单取消/退款时进行调用
*
* @param rebate 佣金
* @param distributionId 分销员ID
*/
void subRebate(Double rebate, String distributionId, Double distributionOrderPrice);
/**
* 添加分销可提现金额
* 订单完成时进行调用
* @param rebate 佣金
* @param distributionId 分销员ID
*/
void addCanRebate(Double rebate, String distributionId);
/**
* 添加提现金额
* @param rebate
* @param distributionId
*/
void addCashRebate(Double rebate, String distributionId);
/**
* 扣减提现金额
* @param rebate
* @param distributionId
*/
void subCashRebate(Double rebate, String distributionId);
}

View File

@@ -1,5 +1,12 @@
package cn.lili.modules.distribution.serviceimpl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.context.ThreadContextHolder;
import cn.lili.common.enums.ClientTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.RocketmqCustomProperties;
@@ -13,6 +20,14 @@ import cn.lili.modules.distribution.entity.vos.DistributionCashSearchParams;
import cn.lili.modules.distribution.mapper.DistributionCashMapper;
import cn.lili.modules.distribution.service.DistributionCashService;
import cn.lili.modules.distribution.service.DistributionService;
import cn.lili.modules.order.order.entity.dto.OrderExportDTO;
import cn.lili.modules.order.order.entity.dto.OrderExportDetailDTO;
import cn.lili.modules.order.order.entity.dto.OrderSearchParams;
import cn.lili.modules.order.order.entity.dto.PriceDetailDTO;
import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderTypeEnum;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.wallet.entity.dto.MemberWalletUpdateDTO;
import cn.lili.modules.wallet.entity.dto.MemberWithdrawalMessage;
import cn.lili.modules.wallet.entity.enums.DepositServiceTypeEnum;
@@ -25,12 +40,23 @@ import cn.lili.rocketmq.tags.MemberTagsEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@@ -76,7 +102,7 @@ public class DistributionCashServiceImpl extends ServiceImpl<DistributionCashMap
distribution.setCommissionFrozen(CurrencyUtil.add(distribution.getCommissionFrozen(), applyMoney));
distributionService.updateById(distribution);
//提现申请记录
DistributionCash distributionCash = new DistributionCash("D" + SnowFlake.getId(), distribution.getId(), applyMoney, distribution.getMemberName());
DistributionCash distributionCash = new DistributionCash("D" + SnowFlake.getId(),applyMoney, distribution);
boolean result = this.save(distributionCash);
if (result) {
//发送提现消息
@@ -109,6 +135,28 @@ public class DistributionCashServiceImpl extends ServiceImpl<DistributionCashMap
return this.page(PageUtil.initPage(distributionCashSearchParams), distributionCashSearchParams.queryWrapper());
}
@Override
public void queryExport(HttpServletResponse response, DistributionCashSearchParams distributionCashSearchParams) {
XSSFWorkbook workbook = initExportData(this.list(distributionCashSearchParams.queryWrapper()));
try {
// 设置响应头
String fileName = URLEncoder.encode("分销提现列表", "UTF-8");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public DistributionCash audit(String id, String result) {
@@ -123,32 +171,21 @@ public class DistributionCashServiceImpl extends ServiceImpl<DistributionCashMap
//获取分销员
Distribution distribution = distributionService.getById(distributorCash.getDistributionId());
if (distribution != null && distribution.getDistributionStatus().equals(DistributionStatusEnum.PASS.name())) {
MemberWithdrawalMessage memberWithdrawalMessage = new MemberWithdrawalMessage();
//审核通过
if (result.equals(WithdrawStatusEnum.VIA_AUDITING.name())) {
memberWithdrawalMessage.setStatus(WithdrawStatusEnum.VIA_AUDITING.name());
//分销记录操作
distributorCash.setDistributionCashStatus(WithdrawStatusEnum.VIA_AUDITING.name());
distributorCash.setPayTime(new Date());
//提现到余额
memberWalletService.increase(new MemberWalletUpdateDTO(distributorCash.getPrice(), distribution.getMemberId(), "分销[" + distributorCash.getSn() + "]佣金提现到余额[" + distributorCash.getPrice() + "]", DepositServiceTypeEnum.WALLET_COMMISSION.name()));
} else {
memberWithdrawalMessage.setStatus(WithdrawStatusEnum.FAIL_AUDITING.name());
//分销员可提现金额退回
distribution.setCanRebate(CurrencyUtil.add(distribution.getCanRebate(), distributorCash.getPrice()));
distributorCash.setDistributionCashStatus(WithdrawStatusEnum.FAIL_AUDITING.name());
}
distribution.setCommissionFrozen(CurrencyUtil.sub(distribution.getCommissionFrozen(), distributorCash.getPrice()));
//分销员金额相关处理
distributionService.updateById(distribution);
//修改分销提现申请
boolean bool = this.updateById(distributorCash);
if (bool) {
//组织会员提现审核消息
memberWithdrawalMessage.setMemberId(distribution.getMemberId());
memberWithdrawalMessage.setPrice(distributorCash.getPrice());
String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_WITHDRAWAL.name();
rocketMQTemplate.asyncSend(destination, memberWithdrawalMessage, RocketmqSendCallbackBuilder.commonCallback());
}
this.updateById(distributorCash);
return distributorCash;
}
throw new ServiceException(ResultCode.DISTRIBUTION_NOT_EXIST);
@@ -156,4 +193,38 @@ public class DistributionCashServiceImpl extends ServiceImpl<DistributionCashMap
throw new ServiceException(ResultCode.DISTRIBUTION_CASH_NOT_EXIST);
}
/**
* 初始化填充导出数据
*
* @param distributionCashList 导出的数据
* @return 填充导出数据
*/
private XSSFWorkbook initExportData(List<DistributionCash> distributionCashList) {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("订单列表");
// 创建表头
Row header = sheet.createRow(0);
String[] headers = {"编号", "姓名", "身份证号", "结算银行开户行名称", "结算银行开户账号", "结算银行开户支行名称"};
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
}
// 填充数据
for (int i = 0; i < distributionCashList.size(); i++) {
DistributionCash distributionCash = distributionCashList.get(i);
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(distributionCash.getSn());
row.createCell(1).setCellValue(distributionCash.getName());
row.createCell(2).setCellValue(distributionCash.getIdNumber());
row.createCell(3).setCellValue(distributionCash.getSettlementBankAccountName());
row.createCell(4).setCellValue(distributionCash.getSettlementBankAccountNum());
row.createCell(5).setCellValue(distributionCash.getSettlementBankBranchName());
}
return workbook;
}
}

View File

@@ -1,32 +1,23 @@
package cn.lili.modules.distribution.serviceimpl;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.SpringContextUtil;
import cn.lili.modules.distribution.entity.dos.Distribution;
import cn.lili.modules.distribution.entity.dos.DistributionOrder;
import cn.lili.modules.distribution.entity.enums.DistributionOrderStatusEnum;
import cn.lili.modules.distribution.entity.enums.DistributionStatusEnum;
import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams;
import cn.lili.modules.distribution.mapper.DistributionOrderMapper;
import cn.lili.modules.distribution.service.DistributionOrderService;
import cn.lili.modules.distribution.service.DistributionService;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dos.OrderItem;
import cn.lili.modules.order.order.entity.dos.StoreFlow;
import cn.lili.modules.order.order.entity.dto.StoreFlowProfitSharingDTO;
import cn.lili.modules.order.order.entity.dto.StoreFlowQueryDTO;
import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum;
import cn.lili.modules.order.order.entity.enums.FlowTypeEnum;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.order.service.OrderService;
import cn.lili.modules.order.order.service.StoreFlowService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.DistributionSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -38,12 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 分销订单接口实现
@@ -53,7 +40,8 @@ import java.util.stream.Collectors;
*/
@Slf4j
@Service
public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderMapper, DistributionOrder> implements DistributionOrderService {
public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderMapper, DistributionOrder>
implements DistributionOrderService {
/**
* 订单
@@ -70,15 +58,12 @@ public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderM
*/
@Autowired
private DistributionService distributionService;
/**
* 系统设置
*/
@Autowired
private SettingService settingService;
@Override
public IPage<DistributionOrder> getDistributionOrderPage(DistributionOrderSearchParams distributionOrderSearchParams) {
return this.page(PageUtil.initPage(distributionOrderSearchParams), distributionOrderSearchParams.queryWrapper());
public IPage<DistributionOrder> getDistributionOrderPage(
DistributionOrderSearchParams distributionOrderSearchParams) {
return this.page(PageUtil.initPage(distributionOrderSearchParams),
distributionOrderSearchParams.queryWrapper());
}
@@ -99,8 +84,8 @@ public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderM
//判断是否为分销订单,如果为分销订单则获取分销佣金
if (order.getDistributionId() != null) {
//根据订单编号获取有分销金额的店铺流水记录
List<StoreFlow> storeFlowList = storeFlowService
.listStoreFlow(StoreFlowQueryDTO.builder().justDistribution(true).orderSn(orderSn).build());
List<StoreFlow> storeFlowList = storeFlowService.listStoreFlow(
StoreFlowQueryDTO.builder().justDistribution(true).orderSn(orderSn).build());
double rebate = 0.0;
//循环店铺流水记录判断是否包含分销商品
//包含分销商品则进行记录分销订单、计算分销总额
@@ -115,39 +100,21 @@ public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderM
Distribution distribution = distributionService.getById(order.getDistributionId());
distributionOrder.setDistributionName(distribution.getMemberName());
//设置结算天数(解冻日期)
Setting setting = settingService.get(SettingEnum.DISTRIBUTION_SETTING.name());
DistributionSetting distributionSetting = JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class);
//添加分销订单
this.save(distributionOrder);
//记录会员的分销总额
if (rebate != 0.0) {
distributionService.addRebate(rebate, order.getDistributionId());
//如果天数写0则立即进行结算
if (distributionSetting.getCashDay().equals(0)) {
DateTime dateTime = new DateTime();
dateTime = dateTime.offsetNew(DateField.DAY_OF_MONTH, -distributionSetting.getCashDay());
//防止事务失效采用上下文获取bean
DistributionOrderService bean = SpringContextUtil.getBean(DistributionOrderService.class);
//分销订单结算
bean.updateRebate(dateTime, DistributionOrderStatusEnum.WAIT_BILL.name());
}
distributionService.addRebate(rebate, order.getDistributionId(), storeFlow.getFinalPrice());
}
}
}
}
/**
* 1.获取订单判断是否为已付款的分销订单
* 2.查看店铺流水记录分销佣金
* 3.修改分销员的分销总金额、可提现金额
* 1.获取订单判断是否为已付款的分销订单 2.查看店铺流水记录分销佣金 3.修改分销员的分销总金额、可提现金额
*
* @param orderSn 订单编号
*/
@@ -161,181 +128,77 @@ public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderM
if (order.getDistributionId() != null && order.getPayStatus().equals(PayStatusEnum.PAID.name())) {
//根据订单编号获取有分销金额的店铺流水记录
List<DistributionOrder> distributionOrderList = this.list(new LambdaQueryWrapper<DistributionOrder>()
.eq(DistributionOrder::getOrderSn, orderSn));
List<DistributionOrder> distributionOrderList =
this.list(new LambdaQueryWrapper<DistributionOrder>().eq(DistributionOrder::getOrderSn, orderSn));
//如果没有分销定单,则直接返回
if (distributionOrderList.isEmpty()) {
return;
}
//分销金额
double rebate = 0.0;
//包含分销商品则进行记录分销订单、计算分销总额
for (DistributionOrder distributionOrder : distributionOrderList) {
rebate = CurrencyUtil.add(rebate, distributionOrder.getRebate());
}
//如果包含分销商品则记录会员的分销总额
if (rebate != 0.0) {
distributionService.subCanRebate(CurrencyUtil.sub(0, rebate), order.getDistributionId());
distributionService.subRebate(distributionOrder.getRebate(), order.getDistributionId(), distributionOrder.getSellBackRebate());
}
}
//修改分销订单的状态
this.update(new LambdaUpdateWrapper<DistributionOrder>().eq(DistributionOrder::getOrderSn, orderSn)
.set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.CANCEL.name()));
.set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.REFUND.name()));
}
@Override
public void refundOrder(String afterSaleSn) {
public void refundOrder(AfterSale afterSale) {
//判断是否为分销订单
StoreFlow storeFlow = storeFlowService.queryOne(StoreFlowQueryDTO.builder().justDistribution(true).refundSn(afterSaleSn).build());
if (storeFlow != null) {
StoreFlow refundStoreFlow = storeFlowService.queryOne(
StoreFlowQueryDTO.builder().justDistribution(true).refundSn(afterSale.getSn()).build());
if (refundStoreFlow != null) {
//获取收款分销订单
DistributionOrder distributionOrder = this.getOne(new LambdaQueryWrapper<DistributionOrder>()
.eq(DistributionOrder::getOrderItemSn, storeFlow.getOrderItemSn()));
DistributionOrder distributionOrder = this.getOne(
new LambdaQueryWrapper<DistributionOrder>().eq(DistributionOrder::getOrderItemSn,
afterSale.getOrderItemSn()));
//分销订单不存在,则直接返回
if (distributionOrder == null) {
return;
}
if (distributionOrder.getDistributionOrderStatus().equals(DistributionOrderStatusEnum.WAIT_BILL.name())) {
this.update(new LambdaUpdateWrapper<DistributionOrder>()
.eq(DistributionOrder::getOrderItemSn, storeFlow.getOrderItemSn())
.set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.CANCEL.name()));
if (distributionOrder.getSellBackRebate() == null) {
distributionOrder.setSellBackRebate(refundStoreFlow.getDistributionRebate());
} else {
distributionOrder.setSellBackRebate(
CurrencyUtil.add(distributionOrder.getSellBackRebate(), refundStoreFlow.getDistributionRebate()));
}
//如果已结算则创建退款分销订单
else {
//修改分销员提成金额
distributionService.subCanRebate(CurrencyUtil.sub(0, storeFlow.getDistributionRebate()), distributionOrder.getDistributionId());
distributionOrder.setRebate(CurrencyUtil.sub(distributionOrder.getRebate(), refundStoreFlow.getDistributionRebate()));
if (distributionOrder.getRebate() == 0) {
distributionOrder.setDistributionOrderStatus(DistributionOrderStatusEnum.REFUND.name());
}
distributionOrder.setRefundNum(distributionOrder.getRefundNum() + afterSale.getNum());
this.updateById(distributionOrder);
// 修改分销员提成金额
if (refundStoreFlow.getDistributionRebate() != 0.0) {
distributionService.subRebate(refundStoreFlow.getDistributionRebate(), distributionOrder.getDistributionId(), refundStoreFlow.getFinalPrice());
}
}
}
@Override
public void updateDistributionOrderStatus(List<OrderItem> orderItems) {
if (orderItems.isEmpty()) {
return;
}
//获取未完成分销订单
List<DistributionOrder> distributionOrderList = this.list(new LambdaQueryWrapper<DistributionOrder>()
.eq(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.NO_COMPLETED.name()));
if (distributionOrderList.isEmpty()) {
return;
}
List<DistributionOrder> list = ListUtil.list(false);
orderItems.stream().forEach(orderItem -> {
//订单售后状态为已失效并且投诉状态为已失效
if (StrUtil.equals(OrderItemAfterSaleStatusEnum.EXPIRED.name(), orderItem.getAfterSaleStatus())) {
List<DistributionOrder> collect = distributionOrderList.stream()
.filter(distributionOrder -> StrUtil.equals(distributionOrder.getOrderItemSn(), orderItem.getSn()))
.map((distributionOrder) -> {
distributionOrder.setDistributionOrderStatus(DistributionOrderStatusEnum.WAIT_BILL.name());
distributionOrder.setSettleCycle(new Date());
return distributionOrder;
})
.collect(Collectors.toList());
list.addAll(collect);
}
});
if (!list.isEmpty()) {
//批量修改分销订单结算状态
this.updateBatchById(list);
public void completeOrder(StoreFlow storeFlow) {
if (storeFlow.getFlowType().equals(FlowTypeEnum.PAY.name())
&&storeFlow.getDistributionRebate() != null
&& storeFlow.getDistributionRebate() != 0) {
//获取分账内容
StoreFlowProfitSharingDTO storeFlowProfitSharingDTO = JSONUtil.toBean(storeFlow.getProfitSharing(), StoreFlowProfitSharingDTO.class);
//获取分销订单
DistributionOrder distributionOrder = this.getOne(new LambdaQueryWrapper<DistributionOrder>().eq(DistributionOrder::getOrderItemSn, storeFlow.getOrderItemSn()));
//解冻分销金额
distributionService.addCanRebate(storeFlowProfitSharingDTO.getDistributionPrice(), distributionOrder.getDistributionId());
// 订单完成
this.update(new LambdaUpdateWrapper<DistributionOrder>()
.eq(DistributionOrder::getId, distributionOrder.getId())
.set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.COMPLETE.name()));
}
}
@Override
public void updateRebate(DateTime dateTime, String distributionOrderStatus) {
//结算时间延后五分钟
dateTime = dateTime.offsetNew(DateField.MINUTE, 5);
//获取待结算订单
List<DistributionOrder> distributionOrderList = this.list(new LambdaQueryWrapper<DistributionOrder>()
.eq(DistributionOrder::getDistributionOrderStatus, distributionOrderStatus)
.isNotNull(DistributionOrder::getSettleCycle)
.le(DistributionOrder::getSettleCycle, dateTime));
//校验待结算订单
if (ObjectUtil.isNotNull(distributionOrderList) && distributionOrderList.size() > 0) {
//结算分销人员信息列表
List<Distribution> distributionUpdateList = new ArrayList<>();
//获取分销员信息
List<Distribution> distributionList = distributionService.list(new LambdaQueryWrapper<Distribution>()
.eq(Distribution::getDistributionStatus, DistributionStatusEnum.PASS.name()));
//根据销人员获取对应分销订单
Map<String, List<DistributionOrder>> distributionOrderList1 = distributionOrderList.stream()
.collect(Collectors.groupingBy(DistributionOrder::getDistributionId));
//校验分销订单不为空
if (ObjectUtil.isNotNull(distributionOrderList1) && distributionOrderList1.size() > 0) {
//遍历分销订单map
distributionOrderList1.forEach((key, value) -> {
//计算分销结算金额
distributionUpdateList.add(checkDistribution(key, value, distributionList));
});
}
//校验分销信息列表不为空
if (ObjectUtil.isNotNull(distributionUpdateList) && !distributionUpdateList.isEmpty()) {
//修改分销员收益
distributionService.updateBatchById(distributionUpdateList);
distributionOrderList.stream().forEach(distributionOrder -> {
//修改分销订单状态为待提现
distributionOrder.setDistributionOrderStatus(DistributionOrderStatusEnum.WAIT_CASH.name());
});
}
//修改分销订单状态
this.updateBatchById(distributionOrderList);
}
}
/**
* 计算分销结算金额
*
* @param distributionId 分销ID
* @param list 分销订单
* @param distributionList 分销列表
* @return
*/
public Distribution checkDistribution(String distributionId, List<DistributionOrder> list, List<Distribution> distributionList) {
//获取所有待结算订单分销人员信息
Distribution distribution = distributionList.parallelStream().filter(a -> StrUtil.equals(a.getId(), distributionId)).collect(Collectors.toList()).get(0);
//获取分销订单总金额
double rebate = list.stream().mapToDouble(DistributionOrder::getRebate).sum();
//检验单分销人员冻结金额为负数时.扣除负数冻结金额后再结算
if (distribution.getCommissionFrozen() < 0) {
rebate = CurrencyUtil.add(distribution.getCommissionFrozen() == null ? 0.0 : distribution.getCommissionFrozen(), rebate);
}
//结算订单总金额+分销可提现金额
Double canRebate = CurrencyUtil.add(rebate, distribution.getCanRebate() == null ? 0.0 : distribution.getCanRebate());
//结算金额小于0
if (canRebate < 0) {
//结算订单总金额+分销可提现金额
distribution.setCanRebate(0.0);
//冻结金额
distribution.setCommissionFrozen(canRebate);
} else {
//结算订单总金额+分销可提现金额
distribution.setCanRebate(canRebate);
//冻结金额
distribution.setCommissionFrozen(0.0);
}
return distribution;
}
}

View File

@@ -80,23 +80,25 @@ public class DistributionServiceImpl extends ServiceImpl<DistributionMapper, Dis
//如果分销员非空并未审核则提示用户请等待,如果分销员为拒绝状态则重新提交申请
if (Optional.ofNullable(distribution).isPresent()) {
if (distribution.getDistributionStatus().equals(DistributionStatusEnum.APPLY.name())) {
throw new ServiceException(ResultCode.DISTRIBUTION_IS_APPLY);
} else if (distribution.getDistributionStatus().equals(DistributionStatusEnum.REFUSE.name())) {
distribution.setDistributionStatus(DistributionStatusEnum.APPLY.name());
BeanUtil.copyProperties(distributionApplyDTO, distribution);
this.updateById(distribution);
return distribution;
switch (DistributionStatusEnum.valueOf(distribution.getDistributionStatus())) {
case REFUSE:
case RETREAT:
distribution.setDistributionStatus(DistributionStatusEnum.APPLY.name());
BeanUtil.copyProperties(distributionApplyDTO, distribution);
this.updateById(distribution);
return distribution;
default:
throw new ServiceException(ResultCode.DISTRIBUTION_IS_APPLY);
}
} else {
//如果未申请分销员则新增进行申请
//获取当前登录用户
Member member = memberService.getUserInfo();
//新建分销员
distribution = new Distribution(member.getId(), member.getNickName(), distributionApplyDTO);
//添加分销员
this.save(distribution);
}
//如果未申请分销员则新增进行申请
//获取当前登录用户
Member member = memberService.getUserInfo();
//新建分销员
distribution = new Distribution(member.getId(), member.getNickName(), distributionApplyDTO);
//添加分销员
this.save(distribution);
return distribution;
}
@@ -179,14 +181,30 @@ public class DistributionServiceImpl extends ServiceImpl<DistributionMapper, Dis
}
}
@Override
public void subCanRebate(Double canRebate, String distributionId) {
this.baseMapper.subCanRebate(canRebate, distributionId);
public void subRebate(Double canRebate, String distributionId, Double distributionOrderPrice) {
this.baseMapper.subRebate(canRebate, distributionId, distributionOrderPrice);
}
@Override
public void addRebate(Double rebate, String distributionId) {
public void addRebate(Double rebate, String distributionId, Double distributionOrderPrice) {
this.baseMapper.addRebate(rebate, distributionId, distributionOrderPrice);
}
@Override
public void addCanRebate(Double rebate, String distributionId) {
this.baseMapper.addCanRebate(rebate, distributionId);
}
@Override
public void addCashRebate(Double rebate, String distributionId) {
this.baseMapper.addCashRebate(rebate, distributionId);
}
@Override
public void subCashRebate(Double rebate, String distributionId) {
this.baseMapper.subCashRebate(rebate, distributionId);
}
}

View File

@@ -41,6 +41,12 @@ public class File extends BaseEntity {
@ApiModelProperty(value = "拥有者id")
private String ownerId;
@ApiModelProperty(value = "拥有者名称")
private String ownerName;
@ApiModelProperty(value = "用户类型")
private String userEnums;
@ApiModelProperty(value = "文件夹ID")
private String fileDirectoryId;
}

View File

@@ -0,0 +1,36 @@
package cn.lili.modules.file.entity;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.mybatis.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Data
@TableName("li_file_directory")
@ApiModel(value = "文件目录")
public class FileDirectory extends BaseEntity {
/**
* @see UserEnums
*/
@ApiModelProperty(value = "文件目录类型")
private String directoryType;
@ApiModelProperty(value = "拥有者名称")
private String directoryName;
@ApiModelProperty(value = "拥有者id")
private String ownerId;
@ApiModelProperty(value = "父分类ID")
private String parentId;
@ApiModelProperty(value = "层级")
@NotNull(message = "层级不能为空")
@Min(value = 0, message = "层级最小为0")
@Max(value = 2, message = "层级最大为2")
private Integer level;
}

View File

@@ -0,0 +1,22 @@
package cn.lili.modules.file.entity.dto;
import cn.lili.common.utils.BeanUtil;
import cn.lili.modules.file.entity.FileDirectory;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class FileDirectoryDTO extends FileDirectory{
@ApiModelProperty(value = "文件目录列表")
private List<FileDirectory> children= new ArrayList<>();
public FileDirectoryDTO(FileDirectory fileDirectory){
BeanUtil.copyProperties(fileDirectory, this);
}
}

View File

@@ -1,10 +1,16 @@
package cn.lili.modules.file.entity.dto;
import cn.lili.common.utils.DateUtil;
import cn.lili.common.vo.PageVO;
import com.alipay.api.internal.util.StringUtils;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Calendar;
import java.util.Date;
/**
* 文件查询所属者参数对象
*
@@ -14,12 +20,53 @@ import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileOwnerDTO {
public class FileOwnerDTO extends PageVO {
@ApiModelProperty(value = "拥有者id")
private String ownerId;
@ApiModelProperty(value = "拥有者名称")
private String ownerName;
@ApiModelProperty(value = "用户类型")
private String userEnums;
@ApiModelProperty(value = "原文件名")
private String name;
@ApiModelProperty(value = "存储文件名")
private String fileKey;
@ApiModelProperty(value = "文件类型")
private String fileType;
@ApiModelProperty(value = "文件夹ID")
private String fileDirectoryId;
@ApiModelProperty(value = "起始日期")
private String startDate;
@ApiModelProperty(value = "结束日期")
private String endDate;
public Date getConvertStartDate() {
if (StringUtils.isEmpty(startDate)) {
return null;
}
return DateUtil.toDate(startDate, DateUtil.STANDARD_DATE_FORMAT);
}
public Date getConvertEndDate() {
if (StringUtils.isEmpty(endDate)) {
return null;
}
//结束时间等于结束日期+1天 -1秒
Date date = DateUtil.toDate(endDate, DateUtil.STANDARD_DATE_FORMAT);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + 1);
calendar.set(Calendar.SECOND, -1);
return calendar.getTime();
}
}

View File

@@ -0,0 +1,18 @@
package cn.lili.modules.file.entity.dto;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.SearchVO;
import cn.lili.modules.file.entity.File;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class FileSearchParams extends PageVO {
@ApiModelProperty(value = "文件")
private File file;
@ApiModelProperty(value = "搜索VO")
private SearchVO searchVO;
@ApiModelProperty(value = "文件夹ID")
private String fileDirectoryId;
}

View File

@@ -0,0 +1,14 @@
package cn.lili.modules.file.mapper;
import cn.lili.modules.file.entity.FileDirectory;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 文件管理数据处理层
*
* @author Chopper
* @since 2021-02-22 17:20
*/
public interface FileDirectoryMapper extends BaseMapper<FileDirectory> {
}

View File

@@ -65,7 +65,7 @@ public class TencentFilePlugin implements FilePlugin {
* @return
*/
private String getUrlPrefix() {
return "https://" + ossSetting.getTencentCOSBucket() + ".cos" + ossSetting.getTencentCOSRegion() + ".myqcloud.com/";
return "https://" + ossSetting.getTencentCOSBucket() + ".cos." + ossSetting.getTencentCOSRegion() + ".myqcloud.com/";
}
@Override

View File

@@ -0,0 +1,33 @@
package cn.lili.modules.file.service;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.modules.file.entity.FileDirectory;
import cn.lili.modules.file.entity.dto.FileDirectoryDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* 文件管理业务层
*
* @author Chopper
*/
public interface FileDirectoryService extends IService<FileDirectory> {
/**
* 添加目录
*
* @param userEnum
* @param id
* @param ownerName
*/
void addFileDirectory(UserEnums userEnum, String id, String ownerName);
/**
* 获取文件目录
*
* @param ownerId 拥有者
* @return
*/
List<FileDirectoryDTO> getFileDirectoryList(String ownerId);
}

View File

@@ -24,6 +24,19 @@ public interface FileService extends IService<File> {
* @param ids
*/
void batchDelete(List<String> ids);
/**
* 根据文件夹ID批量删除
*
* @param directoryId 文件夹ID
*/
void batchDeleteByDirectory(String directoryId);
/**
* 根据文件夹ID查看是否有文件
* @param directoryId 文件夹ID
* @return
*/
Boolean countByDirectory(String directoryId);
/**
* 所有者批量删除
@@ -37,22 +50,19 @@ public interface FileService extends IService<File> {
/**
* 自定义搜索分页
*
* @param file
* @param searchVO
* @param pageVo
* @param fileOwnerDTO 文件查询
* @return
*/
IPage<File> customerPage(File file, SearchVO searchVO, PageVO pageVo);
IPage<File> customerPage(FileOwnerDTO fileOwnerDTO);
/**
* 所属文件数据查询
*
* @param file
* @param searchVO
* @param pageVo
* @param ownerDTO
* @param ownerDTO 文件查询
* @return
*/
IPage<File> customerPageOwner(FileOwnerDTO ownerDTO, File file, SearchVO searchVO, PageVO pageVo);
IPage<File> customerPageOwner(FileOwnerDTO ownerDTO);
}

View File

@@ -0,0 +1,69 @@
package cn.lili.modules.file.serviceimpl;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.modules.distribution.entity.dos.DistributionOrder;
import cn.lili.modules.file.entity.FileDirectory;
import cn.lili.modules.file.entity.dto.FileDirectoryDTO;
import cn.lili.modules.file.mapper.FileDirectoryMapper;
import cn.lili.modules.file.service.FileDirectoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 文件管理业务层实现
*
* @author Chopper
* @since 2020/11/26 17:50
*/
@Service
@RequiredArgsConstructor
public class FileDirectoryServiceImpl extends ServiceImpl<FileDirectoryMapper, FileDirectory> implements FileDirectoryService {
@Override
public void addFileDirectory(UserEnums userEnum, String id, String ownerName) {
FileDirectory fileDirectory = new FileDirectory();
fileDirectory.setOwnerId(id);
fileDirectory.setDirectoryName(ownerName);
fileDirectory.setDirectoryType(userEnum.name());
this.save(fileDirectory);
}
@Override
public List<FileDirectoryDTO> getFileDirectoryList(String scene) {
List<FileDirectory> fileDirectoryList = this.list(new LambdaQueryWrapper<FileDirectory>().eq(FileDirectory::getOwnerId, scene));
List<FileDirectoryDTO> fileDirectoryDTOList = new ArrayList<>();
fileDirectoryList.forEach(item -> {
if (item.getLevel() == 0) {
FileDirectoryDTO fileDirectoryDTO = new FileDirectoryDTO(item);
initChild(fileDirectoryDTO, fileDirectoryList);
fileDirectoryDTOList.add(fileDirectoryDTO);
}
});
return fileDirectoryDTOList;
}
/**
* 递归初始化子树
*/
private void initChild(FileDirectoryDTO fileDirectoryDTO, List<FileDirectory> fileDirectoryList) {
if (fileDirectoryList == null) {
return;
}
fileDirectoryList.stream()
.filter(item -> (item.getParentId().equals(fileDirectoryDTO.getId())))
.forEach(child -> {
FileDirectoryDTO childTree = new FileDirectoryDTO(child);
initChild(childTree, fileDirectoryList);
fileDirectoryDTO.getChildren().add(childTree);
});
}
}

View File

@@ -4,23 +4,19 @@ import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.SearchVO;
import cn.lili.modules.file.entity.File;
import cn.lili.modules.file.entity.dto.FileOwnerDTO;
import cn.lili.modules.file.mapper.FileMapper;
import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.file.plugin.FilePluginFactory;
import cn.lili.modules.file.service.FileService;
import cn.lili.mybatis.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 文件管理业务层实现
@@ -43,10 +39,31 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
List<File> files = this.list(queryWrapper);
List<String> keys = new ArrayList<>();
files.forEach(item -> keys.add(item.getFileKey()));
filePluginFactory.filePlugin().deleteFile(keys);
if (!keys.isEmpty()) {
filePluginFactory.filePlugin().deleteFile(keys);
}
this.remove(queryWrapper);
}
@Override
public void batchDeleteByDirectory(String directoryId) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(File::getFileDirectoryId, directoryId);
List<File> files = this.list(queryWrapper);
List<String> keys = new ArrayList<>();
files.forEach(item -> keys.add(item.getFileKey()));
if (!keys.isEmpty()) {
filePluginFactory.filePlugin().deleteFile(keys);
}
this.remove(queryWrapper);
}
@Override
public Boolean countByDirectory(String directoryId) {
return this.count(new LambdaQueryWrapper<File>().eq(File::getFileDirectoryId, directoryId))>0;
}
@Override
public void batchDelete(List<String> ids, AuthUser authUser) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
@@ -74,26 +91,36 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
}
@Override
public IPage<File> customerPage(File file, SearchVO searchVO, PageVO pageVo) {
public IPage<File> customerPage(FileOwnerDTO fileOwnerDTO) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(CharSequenceUtil.isNotEmpty(file.getName()), File::getName, file.getName())
.like(CharSequenceUtil.isNotEmpty(file.getFileKey()), File::getFileKey, file.getFileKey())
.like(CharSequenceUtil.isNotEmpty(file.getFileType()), File::getFileType, file.getFileType())
.between(CharSequenceUtil.isNotEmpty(searchVO.getStartDate()) && CharSequenceUtil.isNotEmpty(searchVO.getEndDate()),
File::getCreateTime, searchVO.getStartDate(), searchVO.getEndDate());
return this.page(PageUtil.initPage(pageVo), queryWrapper);
queryWrapper.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getName()), File::getName, fileOwnerDTO.getName())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getOwnerName()), File::getOwnerName,
fileOwnerDTO.getOwnerName())
.eq(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileDirectoryId()), File::getFileDirectoryId,
fileOwnerDTO.getFileDirectoryId())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileKey()), File::getFileKey, fileOwnerDTO.getFileKey())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileType()), File::getFileType,
fileOwnerDTO.getFileType()).between(
CharSequenceUtil.isNotEmpty(fileOwnerDTO.getStartDate()) && CharSequenceUtil.isNotEmpty(
fileOwnerDTO.getEndDate()), File::getCreateTime, fileOwnerDTO.getStartDate(),
fileOwnerDTO.getEndDate());
return this.page(PageUtil.initPage(fileOwnerDTO), queryWrapper);
}
@Override
public IPage<File> customerPageOwner(FileOwnerDTO ownerDTO, File file, SearchVO searchVO, PageVO pageVo) {
public IPage<File> customerPageOwner(FileOwnerDTO fileOwnerDTO) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CharSequenceUtil.isNotEmpty(ownerDTO.getOwnerId()), File::getOwnerId, ownerDTO.getOwnerId())
.eq(File::getUserEnums, ownerDTO.getUserEnums())
.like(CharSequenceUtil.isNotEmpty(file.getName()), File::getName, file.getName())
.like(CharSequenceUtil.isNotEmpty(file.getFileKey()), File::getFileKey, file.getFileKey())
.like(CharSequenceUtil.isNotEmpty(file.getFileType()), File::getFileType, file.getFileType())
.between(CharSequenceUtil.isNotEmpty(searchVO.getStartDate()) && CharSequenceUtil.isNotEmpty(searchVO.getEndDate()),
File::getCreateTime, searchVO.getStartDate(), searchVO.getEndDate());
return this.page(PageUtil.initPage(pageVo), queryWrapper);
queryWrapper.eq(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getOwnerId()), File::getOwnerId,
fileOwnerDTO.getOwnerId()).eq(File::getUserEnums, fileOwnerDTO.getUserEnums())
.eq(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileDirectoryId()), File::getFileDirectoryId,
fileOwnerDTO.getFileDirectoryId())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getName()), File::getName, fileOwnerDTO.getName())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileKey()), File::getFileKey, fileOwnerDTO.getFileKey())
.like(CharSequenceUtil.isNotEmpty(fileOwnerDTO.getFileType()), File::getFileType,
fileOwnerDTO.getFileType()).between(
CharSequenceUtil.isNotEmpty(fileOwnerDTO.getStartDate()) && CharSequenceUtil.isNotEmpty(
fileOwnerDTO.getEndDate()), File::getCreateTime, fileOwnerDTO.getStartDate(),
fileOwnerDTO.getEndDate());
return this.page(PageUtil.initPage(fileOwnerDTO), queryWrapper);
}
}

View File

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.models.auth.In;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@@ -163,6 +164,9 @@ public class GoodsSku extends BaseEntity {
@ApiModelProperty(value = "商品类型", required = true)
private String goodsType;
@ApiModelProperty(value = "预警数量")
private Integer alertQuantity;
public Double getWeight() {
if (weight == null) {
return 0d;
@@ -170,6 +174,13 @@ public class GoodsSku extends BaseEntity {
return weight;
}
public Integer getAlertQuantity() {
if(alertQuantity == null){
return 0;
}
return alertQuantity;
}
@Override
public Date getCreateTime() {
if (super.getCreateTime() == null) {
@@ -199,6 +210,7 @@ public class GoodsSku extends BaseEntity {
this.mobileIntro = goods.getMobileIntro();
this.goodsUnit = goods.getGoodsUnit();
this.grade = 100D;
this.alertQuantity = 0;
//商品状态
this.authFlag = goods.getAuthFlag();
this.salesModel = goods.getSalesModel();

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.goods.entity.dto;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -32,4 +33,18 @@ public class CategorySearchParams {
@ApiModelProperty(value = "父节点名称")
private String parentTitle;
@ApiModelProperty(value = "是否禁用")
private Boolean deleteFlag;
public <T > QueryWrapper<T> queryWrapper() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper.like(name != null, "name", name);
queryWrapper.like(parentTitle != null, "parent_title", parentTitle);
queryWrapper.eq(parentId != null, "parent_id", parentId);
queryWrapper.eq(level != null, "level", level);
queryWrapper.eq(sortOrder != null, "sort_order", sortOrder);
queryWrapper.eq(commissionRate != null, "commission_rate", commissionRate);
queryWrapper.eq(deleteFlag != null, "delete_flag", deleteFlag);
return queryWrapper;
}
}

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* 商品导入DTO
@@ -36,7 +35,7 @@ public class GoodsImportDTO {
private Boolean release;
@ApiModelProperty(value = "商品图片")
private List<Map<String, String>> images;
private List<String> images;
private List<String> goodsGalleryList;
@ApiModelProperty(value = "成本价")

View File

@@ -2,7 +2,6 @@ package cn.lili.modules.goods.entity.dto;
import cn.lili.common.validation.EnumValue;
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
import cn.lili.modules.goods.entity.enums.GoodsTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
@@ -96,7 +95,8 @@ public class GoodsOperationDTO implements Serializable {
@ApiModelProperty(value = "是否有规格", hidden = true)
private String haveSpec;
@ApiModelProperty(value = "销售模式", required = true)
@ApiModelProperty(value = "商品单位", required = true)
@NotEmpty(message = "商品单位不能为空")
private String goodsUnit;
@ApiModelProperty(value = "商品描述")

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.goods.entity.dto;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
@@ -9,6 +10,8 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 商品查询条件
@@ -25,6 +28,7 @@ public class GoodsSearchParams extends PageVO {
private static final long serialVersionUID = 2544015852728566887L;
@ApiModelProperty(value = "商品编号")
private String goodsId;
@@ -34,6 +38,9 @@ public class GoodsSearchParams extends PageVO {
@ApiModelProperty(value = "商品编号")
private String id;
@ApiModelProperty(value = "商品编号")
private List<String> ids;
@ApiModelProperty(value = "商家ID")
private String storeId;
@@ -64,6 +71,9 @@ public class GoodsSearchParams extends PageVO {
@ApiModelProperty(value = "审核状态")
private String authFlag;
@ApiModelProperty(value = "商品状态")
private String goodsStatus;
@ApiModelProperty(value = "库存数量")
private Integer leQuantity;
@@ -85,8 +95,13 @@ public class GoodsSearchParams extends PageVO {
@ApiModelProperty(value = "销售模式", required = true)
private String salesModel;
@ApiModelProperty(value = "预警库存")
private Boolean alertQuantity;
public <T> QueryWrapper<T> queryWrapper() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 统一使用 CharSequenceUtil.isNotEmpty() 处理字符串
if (CharSequenceUtil.isNotEmpty(goodsId)) {
queryWrapper.eq("goods_id", goodsId);
}
@@ -96,6 +111,12 @@ public class GoodsSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(id)) {
queryWrapper.in("id", Arrays.asList(id.split(",")));
}
// 统一使用 CollUtil.isNotEmpty() 处理集合
if (CollUtil.isNotEmpty(ids)) {
queryWrapper.in("id", ids);
}
if (CharSequenceUtil.isNotEmpty(storeId)) {
queryWrapper.eq("store_id", storeId);
}
@@ -108,24 +129,42 @@ public class GoodsSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(storeCategoryPath)) {
queryWrapper.like("store_category_path", storeCategoryPath);
}
if (selfOperated != null) {
if (CharSequenceUtil.isNotEmpty(goodsStatus)) {
if(goodsStatus.equals(GoodsStatusEnum.UPPER.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.PASS.name());
queryWrapper.eq("market_enable", GoodsStatusEnum.UPPER.name());
}else if(goodsStatus.equals(GoodsStatusEnum.DOWN.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.PASS.name());
queryWrapper.eq("market_enable", GoodsStatusEnum.DOWN.name());
}else if(goodsStatus.equals(GoodsAuthEnum.TOBEAUDITED.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.TOBEAUDITED.name());
}else if(goodsStatus.equals(GoodsAuthEnum.REFUSE.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.REFUSE.name());
}
}
// 统一使用 Objects.nonNull() 处理对象非空判断
if (Objects.nonNull(selfOperated)) {
queryWrapper.eq("self_operated", selfOperated);
}
if (CharSequenceUtil.isNotEmpty(marketEnable)) {
queryWrapper.eq("market_enable", marketEnable);
}
if (CharSequenceUtil.isNotEmpty(authFlag)) {
queryWrapper.eq("auth_flag", authFlag);
}
if (leQuantity != null) {
if (Objects.nonNull(leQuantity)) {
queryWrapper.le("quantity", leQuantity);
}
if (geQuantity != null) {
queryWrapper.ge("quantity", geQuantity);
if (Objects.nonNull(geQuantity)) {
queryWrapper.gt("quantity", geQuantity);
}
if (recommend != null) {
if (Objects.nonNull(recommend)) {
queryWrapper.le("recommend", recommend);
}
if (CharSequenceUtil.isNotEmpty(goodsType)) {
queryWrapper.eq("goods_type", goodsType);
}
@@ -133,6 +172,12 @@ public class GoodsSearchParams extends PageVO {
queryWrapper.eq("sales_model", salesModel);
}
if(Objects.nonNull(alertQuantity) && alertQuantity){
queryWrapper.apply("quantity <= alert_quantity");
queryWrapper.ge("alert_quantity", 0);
}
queryWrapper.in(CollUtil.isNotEmpty(ids), "id", ids);
queryWrapper.eq("delete_flag", false);
this.betweenWrapper(queryWrapper);
return queryWrapper;

View File

@@ -12,11 +12,30 @@ import lombok.Data;
@Data
public class GoodsSkuStockDTO {
@ApiModelProperty(value = "商品id")
private String goodsId;
@ApiModelProperty(value = "商品skuId")
private String skuId;
@ApiModelProperty(value = "库存")
private Integer quantity;
@ApiModelProperty(value = "预警库存")
private Integer alertQuantity;
@ApiModelProperty(value = "规格信息")
private String simpleSpecs;
@ApiModelProperty(value = "商品编号")
private String sn;
@ApiModelProperty(value = "商品名称")
private String goodsName;
/**
* @see cn.lili.modules.goods.entity.enums.GoodsStockTypeEnum
*/
@ApiModelProperty(value = "类型")
private String type;
}

View File

@@ -0,0 +1,32 @@
package cn.lili.modules.goods.entity.enums;
/**
* 库存操作类型
*/
public enum GoodsStockTypeEnum {
SUB(""),
ADD("");
private final String description;
GoodsStockTypeEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
// 根据描述获取枚举实例
public static GoodsStockTypeEnum fromDescription(String description) {
for (GoodsStockTypeEnum type : GoodsStockTypeEnum.values()) {
if (type.getDescription().equals(description)) {
return type;
}
}
throw new IllegalArgumentException("No matching enum constant for description: " + description);
}
}

View File

@@ -0,0 +1,17 @@
package cn.lili.modules.goods.entity.vos;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class GoodsNumVO {
@ApiModelProperty(value = "出售中的商品数量")
private Integer upperGoodsNum;
@ApiModelProperty(value = "仓库中的商品数量")
private Integer downGoodsNum;
@ApiModelProperty(value = "待审核商品数量")
private Integer auditGoodsNum;
@ApiModelProperty(value = "审核未通过数量")
private Integer refuseGoodsNum;
}

View File

@@ -32,18 +32,5 @@ public class SpecValueVO implements Serializable {
* 规格图片
*/
@ApiModelProperty(value = "规格的图片")
private List<SpecImages> specImage;
@Data
public static class SpecImages implements Serializable {
private static final long serialVersionUID = 1816357809660916086L;
private String url;
private String name;
private String status;
}
private List<String> specImage;
}

View File

@@ -2,6 +2,9 @@ package cn.lili.modules.goods.mapper;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.entity.dto.GoodsSkuDTO;
import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO;
import cn.lili.modules.order.order.entity.dto.OrderExportDTO;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -114,4 +117,7 @@ public interface GoodsSkuMapper extends BaseMapper<GoodsSku> {
@Select("SELECT *,g.params as params FROM li_goods_sku gs inner join li_goods g on gs.goods_id = g.id ${ew.customSqlSegment}")
IPage<GoodsSkuDTO> queryByParams(IPage<GoodsSkuDTO> page, @Param(Constants.WRAPPER) Wrapper<GoodsSkuDTO> queryWrapper);
@Select("SELECT id as sku_id, quantity, goods_id,simple_specs,sn,goods_name FROM li_goods_sku ${ew.customSqlSegment}")
List<GoodsSkuStockDTO> queryStocks(@Param(Constants.WRAPPER) Wrapper<GoodsSku> queryWrapper);
}

View File

@@ -2,6 +2,7 @@ package cn.lili.modules.goods.service;
import cn.lili.modules.goods.entity.dos.Category;
import cn.lili.modules.goods.entity.dto.CategorySearchParams;
import cn.lili.modules.goods.entity.vos.CategoryVO;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -69,9 +70,10 @@ public interface CategoryService extends IService<Category> {
* 查询所有的分类,父子关系
* 数据库获取
*
* @param categorySearchParams 查询参数
* @return 所有的分类,父子关系
*/
List<CategoryVO> listAllChildren();
List<CategoryVO> listAllChildren(CategorySearchParams categorySearchParams);
/**
* 获取指定分类的分类名称

View File

@@ -5,6 +5,7 @@ import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
import cn.lili.modules.goods.entity.vos.GoodsNumVO;
import cn.lili.modules.goods.entity.vos.GoodsVO;
import cn.lili.modules.store.entity.dos.Store;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -83,6 +84,14 @@ public interface GoodsService extends IService<Goods> {
*/
IPage<Goods> queryByParams(GoodsSearchParams goodsSearchParams);
/**
* 获取商品数量
*
* @param goodsSearchParams 查询参数
* @return 商品数量
*/
GoodsNumVO getGoodsNumVO(GoodsSearchParams goodsSearchParams);
/**
* 商品查询
@@ -153,16 +162,16 @@ public interface GoodsService extends IService<Goods> {
* 修改商品库存数量
*
* @param goodsId 商品ID
* @param quantity 库存数量
*/
void updateStock(String goodsId, Integer quantity);
void updateStock(String goodsId);
/**
* 更新商品评价数量
*
* @param goodsId 商品ID
* @param skuId skuID
*/
void updateGoodsCommentNum(String goodsId);
void updateGoodsCommentNum(String goodsId, String skuId);
/**
* 更新商品的购买数量
@@ -191,6 +200,11 @@ public interface GoodsService extends IService<Goods> {
*/
void categoryGoodsName(String categoryId);
/**
* 添加商品评价数量
*
* @param commentNum 评价数量
* @param goodsId 商品ID
*/
void addGoodsCommentNum(Integer commentNum, String goodsId);
}

View File

@@ -12,7 +12,9 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
@@ -146,7 +148,20 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @return 商品sku信息
*/
IPage<GoodsSku> getGoodsSkuByPage(GoodsSearchParams searchParams);
/**
* 查询导出商品库存
*
* @param searchParams 查询参数
* @return 导出商品库存
*/
void queryExportStock(HttpServletResponse response, GoodsSearchParams searchParams);
/**
* 导入商品库存
* @param storeId 店铺ID
* @param files 文件
*/
void importStock(String storeId, MultipartFile files);
/**
* 分页查询商品sku信息
@@ -187,6 +202,19 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @param goodsSkuStockDTOS sku库存修改实体
*/
void updateStocks(List<GoodsSkuStockDTO> goodsSkuStockDTOS);
void updateStocksByType(List<GoodsSkuStockDTO> goodsSkuStockDTOS);
/**
* 更新SKU预警库存
* @param goodsSkuStockDTOS sku库存修改实体
*/
void batchUpdateAlertQuantity(List<GoodsSkuStockDTO> goodsSkuStockDTOS);
/**
* 更新SKU预警库存
* @param goodsSkuStockDTO sku库存修改实体
*/
void updateAlertQuantity(GoodsSkuStockDTO goodsSkuStockDTO);
/**
* 更新SKU库存
@@ -195,6 +223,7 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @param quantity 设置的库存数量
*/
void updateStock(String skuId, Integer quantity);
void updateStock(String skuId, Integer quantity,String type);
/**
* 获取商品sku库存
@@ -209,14 +238,7 @@ public interface GoodsSkuService extends IService<GoodsSku> {
*
* @param goodsSkus
*/
void updateGoodsStuck(List<GoodsSku> goodsSkus);
/**
* 更新SKU评价数量
*
* @param skuId SKUId
*/
void updateGoodsSkuCommentNum(String skuId);
void updateGoodsStock(List<GoodsSku> goodsSkus);
/**
* 根据商品id获取全部skuId的集合
@@ -249,4 +271,38 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @param goodsOperationDTO 商品操作信息
*/
void renderGoodsSkuList(List<GoodsSku> goodsSkuList, GoodsOperationDTO goodsOperationDTO);
/**
* 更新商品sku购买数量
*
* @param skuId skuId
* @param buyCount 购买数量
*/
void updateGoodsSkuBuyCount(String skuId, int buyCount);
/**
* 更新商品sku评分
*
* @param goodsId goodsId
* @param grade 评分
* @param commentNum 评论数量
*/
void updateGoodsSkuGrade(String goodsId, double grade,int commentNum);
/**
* 获取最新商品库存
*
* @param goodsId 商品ID
* @return 库存数量
*/
Integer getGoodsStock(String goodsId);
/**
* 更新sku运费模版
*
* @param goodsId 商品id
* @param templateId 运费模版id
* @return 操作结果
*/
Boolean freight(List<String> goodsId, String templateId);
}

View File

@@ -8,6 +8,7 @@ import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.modules.goods.entity.dos.Category;
import cn.lili.modules.goods.entity.dto.CategorySearchParams;
import cn.lili.modules.goods.entity.vos.CategoryVO;
import cn.lili.modules.goods.mapper.CategoryMapper;
import cn.lili.modules.goods.service.CategoryBrandService;
@@ -106,10 +107,10 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> i
@Override
public List<CategoryVO> categoryTree() {
// List<CategoryVO> categoryVOList = (List<CategoryVO>) cache.get(CachePrefix.CATEGORY.getPrefix());
// if (categoryVOList != null) {
// return categoryVOList;
// }
List<CategoryVO> categoryVOList = (List<CategoryVO>) cache.get(CachePrefix.CATEGORY.getPrefix());
if (categoryVOList != null) {
return categoryVOList;
}
//获取全部分类
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
@@ -117,7 +118,7 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> i
List<Category> list = this.list(queryWrapper);
//构造分类树
List<CategoryVO> categoryVOList = new ArrayList<>();
categoryVOList = new ArrayList<>();
for (Category category : list) {
if ("0".equals(category.getParentId())) {
CategoryVO categoryVO = new CategoryVO(category);
@@ -165,10 +166,10 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> i
}
@Override
public List<CategoryVO> listAllChildren() {
public List<CategoryVO> listAllChildren(CategorySearchParams categorySearchParams) {
//获取全部分类
List<Category> list = this.list();
List<Category> list = this.list(categorySearchParams.queryWrapper());
//构造分类树
List<CategoryVO> categoryVOList = new ArrayList<>();

View File

@@ -101,7 +101,7 @@ public class GoodsGalleryServiceImpl extends ServiceImpl<GoodsGalleryMapper, Goo
switch (OssEnum.valueOf(ossSetting.getType())) {
case MINIO:
//缩略图全路径
return url + "?x-oss-process=style/" + width + "X" + height;
return url;
case ALI_OSS:
//缩略图全路径
return url + "?x-oss-process=style/" + width + "X" + height;

View File

@@ -33,7 +33,9 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
@@ -203,15 +205,11 @@ public class GoodsImportServiceImpl implements GoodsImportService {
goodsImportDTO.setCategory(category);
goodsImportDTO.setTemplate(templateId);
goodsImportDTO.setGoodsUnit(objects.get(4).toString().substring(objects.get(4).toString().indexOf("-") + 1));
goodsImportDTO.setRelease(objects.get(5).toString().equals("上架") ? true : false);
goodsImportDTO.setRelease("上架".equals(objects.get(5).toString()));
List<Map<String, String>> images = new ArrayList<>();
List<String> goodsGalleryList = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("url", objects.get(6).toString());
images.add(map);
goodsGalleryList.add(objects.get(6).toString());
goodsImportDTO.setImages(images);
goodsImportDTO.setImages(goodsGalleryList);
goodsImportDTO.setGoodsGalleryList(goodsGalleryList);
goodsImportDTO.setCost(Convert.toDouble(objects.get(7)));
@@ -241,7 +239,7 @@ public class GoodsImportServiceImpl implements GoodsImportService {
//获取父
Category parentCategory = categoryService.getCategoryById(goodsImportDTO.getCategory().getParentId());
goodsOperationDTO.setCategoryPath(parentCategory.getParentId() + "," + parentCategory.getId() + "," + goodsImportDTO.getCategory().getParentId());
goodsOperationDTO.setCategoryPath(parentCategory.getParentId() + "," + parentCategory.getId() + "," + goodsImportDTO.getCategory().getId());
//添加商品
goodsService.addGoods(goodsOperationDTO);
}

View File

@@ -1,6 +1,7 @@
package cn.lili.modules.goods.serviceimpl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
@@ -13,27 +14,27 @@ import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.modules.goods.entity.dos.Category;
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.GoodsGallery;
import cn.lili.modules.goods.entity.dos.Wholesale;
import cn.lili.modules.goods.entity.dos.*;
import cn.lili.modules.goods.entity.dto.GoodsOperationDTO;
import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
import cn.lili.modules.goods.entity.vos.GoodsNumVO;
import cn.lili.modules.goods.entity.vos.GoodsSkuVO;
import cn.lili.modules.goods.entity.vos.GoodsVO;
import cn.lili.modules.goods.mapper.GoodsMapper;
import cn.lili.modules.goods.service.*;
import cn.lili.modules.member.entity.dos.MemberEvaluation;
import cn.lili.modules.member.entity.dto.EvaluationQueryParams;
import cn.lili.modules.member.entity.enums.EvaluationGradeEnum;
import cn.lili.modules.member.service.MemberEvaluationService;
import cn.lili.modules.search.utils.EsIndexUtil;
import cn.lili.modules.store.entity.dos.FreightTemplate;
import cn.lili.modules.store.entity.dos.Store;
import cn.lili.modules.store.entity.vos.StoreVO;
import cn.lili.modules.store.service.FreightTemplateService;
import cn.lili.modules.store.service.StoreService;
import cn.lili.modules.system.aspect.annotation.SystemLogPoint;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.GoodsSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
@@ -56,6 +57,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 商品业务层实现
@@ -165,6 +167,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
@Override
@Transactional(rollbackFor = Exception.class)
@SystemLogPoint(description = "添加商品", customerLog = "'新增商品名称:['+#goodsOperationDTO.goodsName+']'")
public void addGoods(GoodsOperationDTO goodsOperationDTO) {
Goods goods = new Goods(goodsOperationDTO);
//检查商品
@@ -192,6 +195,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
@Override
@Transactional(rollbackFor = Exception.class)
@SystemLogPoint(description = "修改商品", customerLog = "'操作的商品ID:['+#goodsId+']'")
public void editGoods(GoodsOperationDTO goodsOperationDTO, String goodsId) {
Goods goods = new Goods(goodsOperationDTO);
goods.setId(goodsId);
@@ -269,7 +273,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
goodsVO.setWholesaleList(wholesaleList);
}
cache.put(CachePrefix.GOODS.getPrefix() + goodsId, goodsVO);
cache.put(CachePrefix.GOODS.getPrefix() + goodsId, goodsVO,7200L);
return goodsVO;
}
@@ -278,6 +282,40 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
return this.page(PageUtil.initPage(goodsSearchParams), goodsSearchParams.queryWrapper());
}
@Override
public GoodsNumVO getGoodsNumVO(GoodsSearchParams goodsSearchParams) {
GoodsNumVO goodsNumVO = new GoodsNumVO();
// 获取基础查询条件
QueryWrapper<Goods> baseWrapper = goodsSearchParams.queryWrapper();
// 使用聚合查询一次性获取所有状态的商品数量
List<Map<String, Object>> results = this.baseMapper.selectMaps(
baseWrapper.select(
"COUNT(CASE WHEN auth_flag = 'PASS' AND market_enable = 'UPPER' THEN 1 END) as upperGoodsNum",
"COUNT(CASE WHEN auth_flag = 'PASS' AND market_enable = 'DOWN' THEN 1 END) as downGoodsNum",
"COUNT(CASE WHEN auth_flag = 'TOBEAUDITED' THEN 1 END) as auditGoodsNum",
"COUNT(CASE WHEN auth_flag = 'REFUSE' THEN 1 END) as refuseGoodsNum"
)
);
if (!results.isEmpty()) {
Map<String, Object> result = results.get(0);
goodsNumVO.setUpperGoodsNum(((Number) result.get("upperGoodsNum")).intValue());
goodsNumVO.setDownGoodsNum(((Number) result.get("downGoodsNum")).intValue());
goodsNumVO.setAuditGoodsNum(((Number) result.get("auditGoodsNum")).intValue());
goodsNumVO.setRefuseGoodsNum(((Number) result.get("refuseGoodsNum")).intValue());
} else {
// 如果没有结果设置默认值为0
goodsNumVO.setUpperGoodsNum(0);
goodsNumVO.setDownGoodsNum(0);
goodsNumVO.setAuditGoodsNum(0);
goodsNumVO.setRefuseGoodsNum(0);
}
return goodsNumVO;
}
/**
* 商品查询
*
@@ -290,6 +328,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
}
@Override
@SystemLogPoint(description = "审核商品", customerLog = "'操作的商品ID:['+#goodsIds+'],操作后商品状态:['+#goodsAuthEnum+']'")
@Transactional(rollbackFor = Exception.class)
public boolean auditGoods(List<String> goodsIds, GoodsAuthEnum goodsAuthEnum) {
List<String> goodsCacheKeys = new ArrayList<>();
@@ -312,6 +351,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
@Override
@Transactional(rollbackFor = Exception.class)
@SystemLogPoint(description = "商品状态操作", customerLog = "'操作类型:['+#goodsStatusEnum+'],操作对象:['+#goodsIds+'],操作原因:['+#underReason+']'")
public Boolean updateGoodsMarketAble(List<String> goodsIds, GoodsStatusEnum goodsStatusEnum, String underReason) {
boolean result;
@@ -343,6 +383,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
* @return 更新结果
*/
@Override
@SystemLogPoint(description = "店铺关闭下架商品", customerLog = "'操作类型:['+#goodsStatusEnum+'],操作对象:['+#storeId+'],操作原因:['+#underReason+']'")
public Boolean updateGoodsMarketAbleByStoreId(String storeId, GoodsStatusEnum goodsStatusEnum, String underReason) {
@@ -358,7 +399,9 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
}
@Override
@SystemLogPoint(description = "管理员关闭下架商品", customerLog = "'操作类型:['+#goodsStatusEnum+'],操作对象:['+#goodsIds+'],操作原因:['+#underReason+']'")
@Transactional(rollbackFor = Exception.class)
public Boolean managerUpdateGoodsMarketAble(List<String> goodsIds, GoodsStatusEnum goodsStatusEnum, String underReason) {
boolean result;
@@ -386,6 +429,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
@Override
@Transactional(rollbackFor = Exception.class)
@SystemLogPoint(description = "删除商品", customerLog = "'操作对象:['+#goodsIds+']'")
public Boolean deleteGoods(List<String> goodsIds) {
LambdaUpdateWrapper<Goods> updateWrapper = this.getUpdateWrapperByStoreAuthority();
@@ -426,37 +470,61 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
LambdaUpdateWrapper<Goods> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.set(Goods::getTemplateId, templateId);
lambdaUpdateWrapper.in(Goods::getId, goodsIds);
cache.multiDel(goodsIds);
List<String> goodsCache = goodsIds.stream().map(item -> CachePrefix.GOODS.getPrefix() + item).collect(Collectors.toList());
cache.multiDel(goodsCache);
goodsSkuService.freight(goodsIds, templateId);
return this.update(lambdaUpdateWrapper);
}
@Override
public void updateStock(String goodsId, Integer quantity) {
@SystemLogPoint(description = "同步商品库存", customerLog = "'同步商品商品ID的库存:['+#goodsId+']'")
public void updateStock(String goodsId) {
LambdaUpdateWrapper<Goods> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.set(Goods::getQuantity, quantity);
Integer stock = goodsSkuService.getGoodsStock(goodsId);
lambdaUpdateWrapper.set(Goods::getQuantity, stock);
lambdaUpdateWrapper.eq(Goods::getId, goodsId);
cache.remove(CachePrefix.GOODS.getPrefix() + goodsId);
this.update(lambdaUpdateWrapper);
}
@Override
public void updateGoodsCommentNum(String goodsId) {
@Transactional(rollbackFor = Exception.class)
public void updateGoodsCommentNum(String goodsId, String skuId) {
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(skuId);
if (goodsSku == null) {
return;
}
//获取商品信息
Goods goods = this.getById(goodsId);
//修改商品评价数量
goods.setCommentNum(goods.getCommentNum() + 1);
//修改商品好评率
LambdaQueryWrapper<MemberEvaluation> goodEvaluationQueryWrapper = new LambdaQueryWrapper<>();
goodEvaluationQueryWrapper.eq(MemberEvaluation::getId, goodsId);
goodEvaluationQueryWrapper.eq(MemberEvaluation::getGrade, EvaluationGradeEnum.GOOD.name());
if (goods == null) {
return;
}
//修改商品评价数量
long commentNum = memberEvaluationService.getEvaluationCount(EvaluationQueryParams.builder().goodsId(goodsId).status("OPEN").build());
goods.setCommentNum((int) (commentNum));
//好评数量
long highPraiseNum = memberEvaluationService.count(goodEvaluationQueryWrapper);
long highPraiseNum = memberEvaluationService.getEvaluationCount(EvaluationQueryParams.builder().goodsId(goodsId).status("OPEN").grade(EvaluationGradeEnum.GOOD.name()).build());
//好评率
double grade = NumberUtil.mul(NumberUtil.div(highPraiseNum, goods.getCommentNum().doubleValue(), 2), 100);
goods.setGrade(grade);
this.updateById(goods);
LambdaUpdateWrapper<Goods> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Goods::getId, goodsId);
updateWrapper.set(Goods::getCommentNum, goods.getCommentNum());
updateWrapper.set(Goods::getGrade, goods.getGrade());
this.update(updateWrapper);
cache.remove(CachePrefix.GOODS.getPrefix() + goodsId);
// 修改商品sku评价数量
this.goodsSkuService.updateGoodsSkuGrade(goodsId, grade, goods.getCommentNum());
Map<String, Object> updateIndexFieldsMap = EsIndexUtil.getUpdateIndexFieldsMap(MapUtil.builder(new HashMap<String, Object>()).put("goodsId", goodsId).build(), MapUtil.builder(new HashMap<String, Object>()).put("commentNum", goods.getCommentNum()).put("highPraiseNum", highPraiseNum).put("grade", grade).build());
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品索引信息", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.UPDATE_GOODS_INDEX_FIELD.name(), JSONUtil.toJsonStr(updateIndexFieldsMap)));
}
/**
@@ -548,7 +616,8 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
if (!GoodsStatusEnum.UPPER.name().equals(goods.getMarketEnable()) || !GoodsAuthEnum.PASS.name().equals(goods.getAuthFlag())) {
return;
}
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("生成商品", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.GENERATOR_GOODS_INDEX.name(), goods.getId()));
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("生成商品", rocketmqCustomProperties.getGoodsTopic(),
GoodsTagsEnum.GENERATOR_GOODS_INDEX.name(), goods.getId()));
}
/**
@@ -558,7 +627,8 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
*/
@Transactional
public void updateEsGoods(List<String> goodsIds) {
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.UPDATE_GOODS_INDEX.name(), goodsIds));
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品", rocketmqCustomProperties.getGoodsTopic(),
GoodsTagsEnum.UPDATE_GOODS_INDEX.name(), goodsIds));
}
/**
@@ -568,7 +638,8 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
*/
@Transactional
public void deleteEsGoods(List<String> goodsIds) {
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("删除商品", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.GOODS_DELETE.name(), JSONUtil.toJsonStr(goodsIds)));
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("删除商品", rocketmqCustomProperties.getGoodsTopic(),
GoodsTagsEnum.GOODS_DELETE.name(), JSONUtil.toJsonStr(goodsIds)));
}
/**

View File

@@ -1,9 +1,8 @@
package cn.lili.modules.goods.serviceimpl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
@@ -26,6 +25,7 @@ import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
import cn.lili.modules.goods.entity.enums.GoodsStatusEnum;
import cn.lili.modules.goods.entity.enums.GoodsStockTypeEnum;
import cn.lili.modules.goods.entity.vos.GoodsSkuSpecVO;
import cn.lili.modules.goods.entity.vos.GoodsSkuVO;
import cn.lili.modules.goods.entity.vos.GoodsVO;
@@ -38,9 +38,6 @@ import cn.lili.modules.goods.service.WholesaleService;
import cn.lili.modules.goods.sku.GoodsSkuBuilder;
import cn.lili.modules.goods.sku.render.SalesModelRender;
import cn.lili.modules.member.entity.dos.FootPrint;
import cn.lili.modules.member.entity.dto.EvaluationQueryParams;
import cn.lili.modules.member.entity.enums.EvaluationGradeEnum;
import cn.lili.modules.member.service.MemberEvaluationService;
import cn.lili.modules.promotion.entity.dos.Coupon;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.dto.search.PromotionGoodsSearchParams;
@@ -50,7 +47,6 @@ import cn.lili.modules.promotion.service.MemberCouponService;
import cn.lili.modules.promotion.service.PromotionGoodsService;
import cn.lili.modules.search.entity.dos.EsGoodsIndex;
import cn.lili.modules.search.service.EsGoodsIndexService;
import cn.lili.modules.search.utils.EsIndexUtil;
import cn.lili.mybatis.BaseEntity;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
@@ -61,12 +57,22 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
@@ -104,11 +110,6 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
*/
@Autowired
private RocketmqCustomProperties rocketmqCustomProperties;
/**
* 会员评价
*/
@Autowired
private MemberEvaluationService memberEvaluationService;
/**
* 商品
*/
@@ -148,7 +149,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
if (!goodsSkus.isEmpty()) {
this.saveOrUpdateBatch(goodsSkus);
this.updateStock(goodsSkus);
this.updateGoodsStock(goodsSkus);
}
}
@@ -199,7 +200,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
skuList.stream().map(BaseEntity::getId).collect(Collectors.toList()));
this.remove(unnecessarySkuIdsQuery);
this.saveOrUpdateBatch(skuList);
this.updateStock(skuList);
this.updateGoodsStock(skuList);
}
}
@@ -340,6 +341,11 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
}
}
if (goodsSkuDetail.getGoodsGalleryList() == null || goodsSkuDetail.getGoodsGalleryList().isEmpty()) {
goodsSkuDetail.setGoodsGalleryList(goodsVO.getGoodsGalleryList());
} else {
goodsSkuDetail.getGoodsGalleryList().addAll(goodsVO.getGoodsGalleryList());
}
map.put("data", goodsSkuDetail);
//获取分类
@@ -385,7 +391,9 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
List<GoodsSku> goodsSkus = this.getGoodsSkuListByGoodsId(goods.getId());
for (GoodsSku sku : goodsSkus) {
cache.remove(GoodsSkuService.getCacheKeys(sku.getId()));
cache.put(GoodsSkuService.getCacheKeys(sku.getId()), sku);
if (GoodsStatusEnum.UPPER.name().equals(goods.getMarketEnable()) && GoodsAuthEnum.PASS.name().equals(goods.getAuthFlag())) {
cache.put(GoodsSkuService.getCacheKeys(sku.getId()), sku);
}
}
}
}
@@ -478,13 +486,10 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
SpecValueVO specValueVO = new SpecValueVO();
if ("images".equals(entry.getKey())) {
specValueVO.setSpecName(entry.getKey());
if (entry.getValue().toString().contains("url")) {
List<SpecValueVO.SpecImages> specImages = JSONUtil.toList(JSONUtil.parseArray(entry.getValue()),
SpecValueVO.SpecImages.class);
specValueVO.setSpecImage(specImages);
goodsGalleryList =
specImages.stream().map(SpecValueVO.SpecImages::getUrl).collect(Collectors.toList());
}
List<String> specImages = JSONUtil.toList(JSONUtil.parseArray(entry.getValue()),
String.class);
specValueVO.setSpecImage(specImages);
goodsGalleryList = new ArrayList<>(specImages);
} else {
specValueVO.setSpecName(entry.getKey());
specValueVO.setSpecValue(entry.getValue().toString());
@@ -501,6 +506,116 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
return this.page(PageUtil.initPage(searchParams), searchParams.queryWrapper());
}
@Override
public void queryExportStock(HttpServletResponse response, GoodsSearchParams searchParams) {
List<GoodsSkuStockDTO> goodsSkuStockDTOList = this.baseMapper.queryStocks(searchParams.queryWrapper());
XSSFWorkbook workbook = initStockExportData(goodsSkuStockDTOList);
try {
// 设置响应头
String fileName = URLEncoder.encode("商品库存", "UTF-8");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void importStock(String storeId, MultipartFile file) {
List<GoodsSkuStockDTO> goodsSkuStockDTOList = new ArrayList<>();
try (InputStream inputStream = file.getInputStream()) {
// 使用 WorkbookFactory.create 方法读取 Excel 文件
Workbook workbook = WorkbookFactory.create(inputStream);
// 我们只读取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 检查第一个sheet的行数是否超过10002行
if (sheet.getPhysicalNumberOfRows() > 10002) {
throw new ServiceException(ResultCode.GOODS_STOCK_IMPORT_ERROR, "Excel行数超过10002行");
}
// 遍历行和单元格
Iterator<Row> rowIterator = sheet.rowIterator();
int rowIndex = 0;
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
rowIndex++;
// 跳过表头
if (rowIndex < 3) {
continue;
}
List<Object> objects = new ArrayList<>();
for (int i = 0; i < 4; i++) {
objects.add(getCellValue(row.getCell(i)));
}
log.error(getCellValue(row.getCell(2)));
log.error(getCellValue(row.getCell(3)));
// 判断数据格式
if (!"".equals(getCellValue(row.getCell(2))) && !"".equals(getCellValue(row.getCell(2)))) {
throw new ServiceException(ResultCode.GOODS_STOCK_IMPORT_ERROR, "库存修改方向列数据必须是“增”或“减”");
} else if (!NumberUtil.isInteger(getCellValue(row.getCell(3))) || Integer.parseInt(getCellValue(row.getCell(3))) < 0) {
throw new ServiceException(ResultCode.GOODS_STOCK_IMPORT_ERROR, "库存必须是正整数");
} else if (this.count(new LambdaQueryWrapper<GoodsSku>()
.eq(GoodsSku::getGoodsId, getCellValue(row.getCell(0)))
.eq(GoodsSku::getId, getCellValue(row.getCell(1)))
.eq(GoodsSku::getStoreId, storeId)) == 0) {
throw new ServiceException(ResultCode.GOODS_STOCK_IMPORT_ERROR, "" + rowIndex + "行商品不存在");
}
GoodsSkuStockDTO goodsSkuStockDTO = new GoodsSkuStockDTO();
goodsSkuStockDTO.setGoodsId(getCellValue(row.getCell(0)));
goodsSkuStockDTO.setSkuId(getCellValue(row.getCell(1)));
goodsSkuStockDTO.setType(GoodsStockTypeEnum.fromDescription(getCellValue(row.getCell(2))).name());
goodsSkuStockDTO.setQuantity(Integer.parseInt(getCellValue(row.getCell(3))));
goodsSkuStockDTOList.add(goodsSkuStockDTO);
}
} catch (IOException e) {
log.error("IOException occurred while processing the Excel file.", e);
throw new ServiceException(ResultCode.GOODS_STOCK_IMPORT_ERROR, e.getMessage());
}
// 批量修改商品库存
this.updateStocksByType(goodsSkuStockDTOList);
}
private String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue().toString();
} else {
// 将数值转换为整数以去掉小数点
double numericValue = cell.getNumericCellValue();
if (numericValue == (long) numericValue) {
return String.valueOf((long) numericValue);
} else {
return String.valueOf(numericValue);
}
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
default:
return "";
}
}
@Override
public IPage<GoodsSkuDTO> getGoodsSkuDTOByPage(Page<GoodsSkuDTO> page, Wrapper<GoodsSkuDTO> queryWrapper) {
return this.baseMapper.queryByParams(page, queryWrapper);
@@ -520,42 +635,140 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStocks(List<GoodsSkuStockDTO> goodsSkuStockDTOS) {
for (GoodsSkuStockDTO goodsSkuStockDTO : goodsSkuStockDTOS) {
this.updateStock(goodsSkuStockDTO.getSkuId(), goodsSkuStockDTO.getQuantity());
List<String> skuIds = goodsSkuStockDTOS.stream().map(GoodsSkuStockDTO::getSkuId).collect(Collectors.toList());
List<GoodsSkuStockDTO> goodsSkuStockList = this.baseMapper.queryStocks(GoodsSearchParams.builder().ids(skuIds).build().queryWrapper());
Map<String, List<GoodsSkuStockDTO>> groupByGoodsIds = goodsSkuStockList.stream().collect(Collectors.groupingBy(GoodsSkuStockDTO::getGoodsId));
//统计每个商品的库存
for (Map.Entry<String, List<GoodsSkuStockDTO>> entry : groupByGoodsIds.entrySet()) {
//库存
for (GoodsSkuStockDTO goodsSku : entry.getValue()) {
goodsSkuStockDTOS.stream().filter(i -> i.getSkuId().equals(goodsSku.getSkuId())).findFirst().ifPresent(i -> goodsSku.setQuantity(i.getQuantity()));
this.updateStock(goodsSku.getSkuId(), goodsSku.getQuantity());
}
//保存商品库存结果
goodsService.updateStock(entry.getKey());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStocksByType(List<GoodsSkuStockDTO> goodsSkuStockDTOS) {
// 获取所有的goodsID并去除相同的goodsID
List<String> goodsIds = goodsSkuStockDTOS.stream()
.map(GoodsSkuStockDTO::getGoodsId)
.distinct()
.collect(Collectors.toList());
//更新SKU库存
for (GoodsSkuStockDTO goodsSkuStockDTO : goodsSkuStockDTOS) {
this.updateStock(goodsSkuStockDTO.getSkuId(), goodsSkuStockDTO.getQuantity(), goodsSkuStockDTO.getType());
}
//更新SPU库存
for (String goodsId : goodsIds) {
goodsService.updateStock(goodsId);
}
}
@Override
public void updateAlertQuantity(GoodsSkuStockDTO goodsSkuStockDTO) {
GoodsSku goodsSku = this.getById(goodsSkuStockDTO.getSkuId());
goodsSku.setAlertQuantity(goodsSkuStockDTO.getAlertQuantity());
//清除缓存,防止修改预警后直接修改商品导致预警数值错误
cache.remove(CachePrefix.GOODS.getPrefix() + goodsSku.getGoodsId());
this.update(goodsSku);
}
@Override
public void batchUpdateAlertQuantity(List<GoodsSkuStockDTO> goodsSkuStockDTOS) {
List<GoodsSku> goodsSkuList = new ArrayList<>();
List<String> skuIds = goodsSkuStockDTOS.stream().map(GoodsSkuStockDTO::getSkuId).collect(Collectors.toList());
List<GoodsSkuStockDTO> goodsSkuStockList = this.baseMapper.queryStocks(GoodsSearchParams.builder().ids(skuIds).build().queryWrapper());
List<String> goodsIdList = goodsSkuStockList.stream().map(GoodsSkuStockDTO::getGoodsId).collect(Collectors.toList());
HashSet<String> uniqueSet = new HashSet<>(goodsIdList);
// 将去重后的元素转回列表
List<String> uniqueGoodsIdList = new ArrayList<>(uniqueSet);
for (String goodsId : uniqueGoodsIdList) {
cache.remove(CachePrefix.GOODS.getPrefix() + goodsId);
}
//修改预警库存
for (GoodsSkuStockDTO goodsSkuStockDTO : goodsSkuStockDTOS) {
GoodsSku goodsSku = this.getById(goodsSkuStockDTO.getSkuId());
goodsSku.setAlertQuantity(goodsSkuStockDTO.getAlertQuantity());
goodsSkuList.add(goodsSku);
}
this.updateBatchById(goodsSkuList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStock(String skuId, Integer quantity) {
GoodsSku goodsSku = getGoodsSkuByIdFromCache(skuId);
if (goodsSku != null) {
//判断商品sku是否已经下架(修改商品库存为0时 会自动下架商品),再次更新商品库存时 需更新商品索引
Boolean isFlag = goodsSku.getQuantity()<= 0;
boolean isFlag = goodsSku.getQuantity() <= 0;
goodsSku.setQuantity(quantity);
boolean update =
this.update(new LambdaUpdateWrapper<GoodsSku>().eq(GoodsSku::getId, skuId).set(GoodsSku::getQuantity, quantity));
boolean update = this.update(new LambdaUpdateWrapper<GoodsSku>().eq(GoodsSku::getId, skuId).set(GoodsSku::getQuantity, quantity));
if (update) {
cache.remove(CachePrefix.GOODS.getPrefix() + goodsSku.getGoodsId());
}
cache.put(GoodsSkuService.getCacheKeys(skuId), goodsSku);
cache.put(GoodsSkuService.getStockCacheKey(skuId), quantity);
//更新商品库存
List<GoodsSku> goodsSkus = new ArrayList<>();
goodsSkus.add(goodsSku);
this.updateGoodsStuck(goodsSkus);
this.promotionGoodsService.updatePromotionGoodsStock(goodsSku.getId(), quantity);
//商品库存为0是删除商品索引
if (quantity <= 0) {
goodsIndexService.deleteIndexById(goodsSku.getId());
}
//商品SKU库存为0并且商品sku状态为上架时更新商品库存
if(isFlag && StrUtil.equals(goodsSku.getMarketEnable(),GoodsStatusEnum.UPPER.name())) {
if (isFlag && CharSequenceUtil.equals(goodsSku.getMarketEnable(), GoodsStatusEnum.UPPER.name())) {
List<String> goodsIds = new ArrayList<>();
goodsIds.add(goodsSku.getGoodsId());
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.UPDATE_GOODS_INDEX.name(), goodsIds));
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品", rocketmqCustomProperties.getGoodsTopic(),
GoodsTagsEnum.UPDATE_GOODS_INDEX.name(), goodsIds));
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStock(String skuId, Integer quantity, String type) {
GoodsSku goodsSku = getGoodsSkuByIdFromCache(skuId);
if (goodsSku != null) {
//计算修改库存
if (type.equals(GoodsStockTypeEnum.ADD.name())) {
quantity = Convert.toInt(NumberUtil.add(goodsSku.getQuantity(), quantity));
} else {
quantity = Convert.toInt(NumberUtil.sub(goodsSku.getQuantity(), quantity));
}
goodsSku.setQuantity(quantity);
//判断商品sku是否已经下架(修改商品库存为0时 会自动下架商品),再次更新商品库存时 需更新商品索引
boolean isFlag = goodsSku.getQuantity() <= 0;
boolean update = this.update(new LambdaUpdateWrapper<GoodsSku>().eq(GoodsSku::getId, skuId).set(GoodsSku::getQuantity, quantity));
if (update) {
cache.remove(CachePrefix.GOODS.getPrefix() + goodsSku.getGoodsId());
}
cache.put(GoodsSkuService.getCacheKeys(skuId), goodsSku);
cache.put(GoodsSkuService.getStockCacheKey(skuId), quantity);
this.promotionGoodsService.updatePromotionGoodsStock(goodsSku.getId(), quantity);
//商品库存为0是删除商品索引
if (quantity <= 0) {
goodsIndexService.deleteIndexById(goodsSku.getId());
}
//商品SKU库存为0并且商品sku状态为上架时更新商品库存
if (isFlag && CharSequenceUtil.equals(goodsSku.getMarketEnable(), GoodsStatusEnum.UPPER.name())) {
List<String> goodsIds = new ArrayList<>();
goodsIds.add(goodsSku.getGoodsId());
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("更新商品", rocketmqCustomProperties.getGoodsTopic(),
GoodsTagsEnum.UPDATE_GOODS_INDEX.name(), goodsIds));
}
}
}
@@ -575,66 +788,26 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
@Override
@Transactional(rollbackFor = Exception.class)
public void updateGoodsStuck(List<GoodsSku> goodsSkus) {
Map<String, List<GoodsSku>> groupByGoodsIds =
goodsSkus.stream().collect(Collectors.groupingBy(GoodsSku::getGoodsId));
//获取相关的sku集合
LambdaQueryWrapper<GoodsSku> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(GoodsSku::getGoodsId, groupByGoodsIds.keySet());
List<GoodsSku> goodsSkuList = this.list(lambdaQueryWrapper);
public void updateGoodsStock(List<GoodsSku> goodsSkus) {
Map<String, List<GoodsSku>> groupByGoodsIds = goodsSkus.stream().collect(Collectors.groupingBy(GoodsSku::getGoodsId));
//统计每个商品的库存
for (String goodsId : groupByGoodsIds.keySet()) {
//库存
Integer quantity = 0;
for (GoodsSku goodsSku : goodsSkuList) {
for (GoodsSku goodsSku : goodsSkus) {
if (goodsId.equals(goodsSku.getGoodsId())) {
quantity += goodsSku.getQuantity();
}
this.updateStock(goodsSku.getId(), goodsSku.getQuantity());
}
//保存商品库存结果
goodsService.updateStock(goodsId, quantity);
goodsService.updateStock(goodsId);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateGoodsSkuCommentNum(String skuId) {
//获取商品信息
GoodsSku goodsSku = this.getGoodsSkuByIdFromCache(skuId);
EvaluationQueryParams queryParams = new EvaluationQueryParams();
queryParams.setGrade(EvaluationGradeEnum.GOOD.name());
queryParams.setSkuId(goodsSku.getId());
//好评数量
long highPraiseNum = memberEvaluationService.getEvaluationCount(queryParams);
//更新商品评价数量
goodsSku.setCommentNum(goodsSku.getCommentNum() != null ? goodsSku.getCommentNum() + 1 : 1);
//好评率
double grade = NumberUtil.mul(NumberUtil.div(highPraiseNum, goodsSku.getCommentNum().doubleValue(), 2), 100);
goodsSku.setGrade(grade);
//修改规格
this.update(goodsSku);
//修改规格索引,发送mq消息
Map<String, Object> updateIndexFieldsMap =
EsIndexUtil.getUpdateIndexFieldsMap(MapUtil.builder(new HashMap<String, Object>()).put("id",
goodsSku.getId()).build(), MapUtil.builder(new HashMap<String, Object>()).put("commentNum",
goodsSku.getCommentNum()).put("highPraiseNum", highPraiseNum).put("grade", grade).build());
String destination =
rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.UPDATE_GOODS_INDEX_FIELD.name();
rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(updateIndexFieldsMap),
RocketmqSendCallbackBuilder.commonCallback());
//修改商品的评价数量
goodsService.updateGoodsCommentNum(goodsSku.getGoodsId());
}
/**
* 根据商品id获取全部skuId的集合
*
@@ -663,26 +836,11 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
public Long countSkuNum(String storeId) {
LambdaQueryWrapper<GoodsSku> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(GoodsSku::getStoreId, storeId).eq(GoodsSku::getDeleteFlag, Boolean.FALSE).eq(GoodsSku::getAuthFlag, GoodsAuthEnum.PASS.name()).eq(GoodsSku::getMarketEnable, GoodsStatusEnum.UPPER.name());
queryWrapper.eq(GoodsSku::getStoreId, storeId).eq(GoodsSku::getDeleteFlag, Boolean.FALSE).eq(GoodsSku::getAuthFlag,
GoodsAuthEnum.PASS.name()).eq(GoodsSku::getMarketEnable, GoodsStatusEnum.UPPER.name());
return this.count(queryWrapper);
}
/**
* 修改库存
*
* @param goodsSkus 商品SKU
*/
private void updateStock(List<GoodsSku> goodsSkus) {
//总库存数量
Integer quantity = 0;
for (GoodsSku sku : goodsSkus) {
this.updateStock(sku.getId(), sku.getQuantity());
quantity += sku.getQuantity();
}
//修改商品库存
goodsService.updateStock(goodsSkus.get(0).getGoodsId(), quantity);
}
/**
* 批量渲染商品sku
@@ -696,10 +854,52 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderBatch(goodsSkuList, goodsOperationDTO));
for (GoodsSku goodsSku : goodsSkuList) {
extendOldSkuValue(goodsSku);
this.renderImages(goodsSku);
this.renderImages(goodsSku, goodsOperationDTO.getGoodsGalleryList());
}
}
@Override
public void updateGoodsSkuBuyCount(String skuId, int buyCount) {
LambdaUpdateWrapper<GoodsSku> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(GoodsSku::getId, skuId);
updateWrapper.set(GoodsSku::getBuyCount, buyCount);
this.update(updateWrapper);
}
@Override
public void updateGoodsSkuGrade(String goodsId, double grade, int commentNum) {
LambdaUpdateWrapper<GoodsSku> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(GoodsSku::getGoodsId, goodsId);
updateWrapper.set(GoodsSku::getGrade, grade);
updateWrapper.set(GoodsSku::getCommentNum, commentNum);
this.update(updateWrapper);
this.getSkuIdsByGoodsId(goodsId).forEach(this::clearCache);
}
@Override
public Integer getGoodsStock(String goodsId) {
List<String> skuIds = this.getSkuIdsByGoodsId(goodsId);
Integer stock = 0;
for (String skuId : skuIds) {
stock += this.getStock(skuId);
}
return stock;
}
@Override
public Boolean freight(List<String> goodsId, String templateId) {
LambdaUpdateWrapper<GoodsSku> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(GoodsSku::getGoodsId, goodsId);
updateWrapper.set(GoodsSku::getFreightTemplateId, templateId);
updateWrapper.set(GoodsSku::getUpdateTime, new Date());
List<String> skuIds = this.list(updateWrapper).stream().map(GoodsSku::getId).collect(Collectors.toList());
skuIds.forEach(this::clearCache);
return this.update(updateWrapper);
}
/**
* 渲染商品sku
*
@@ -710,7 +910,7 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
extendOldSkuValue(goodsSku);
// 商品销售模式渲染器
salesModelRenders.stream().filter(i -> i.getSalesMode().equals(goodsOperationDTO.getSalesModel())).findFirst().ifPresent(i -> i.renderSingle(goodsSku, goodsOperationDTO));
this.renderImages(goodsSku);
this.renderImages(goodsSku, goodsOperationDTO.getGoodsGalleryList());
}
/**
@@ -735,16 +935,23 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
*
* @param goodsSku sku
*/
void renderImages(GoodsSku goodsSku) {
JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs());
List<Map<String, String>> images = jsonObject.get("images", List.class);
if (images != null && !images.isEmpty()) {
GoodsGallery goodsGallery = goodsGalleryService.getGoodsGallery(images.get(0).get("url"));
goodsSku.setBig(goodsGallery.getOriginal());
goodsSku.setOriginal(goodsGallery.getOriginal());
goodsSku.setThumbnail(goodsGallery.getThumbnail());
goodsSku.setSmall(goodsGallery.getSmall());
void renderImages(GoodsSku goodsSku, List<String> goodsImages) {
if (goodsImages == null || goodsImages.isEmpty()) {
return;
}
JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs());
List<String> images = jsonObject.getBeanList("images", String.class);
GoodsGallery goodsGallery;
if (images != null && !images.isEmpty()) {
goodsGallery = goodsGalleryService.getGoodsGallery(images.get(0));
} else {
goodsGallery = goodsGalleryService.getGoodsGallery(goodsImages.get(0));
}
goodsSku.setBig(goodsGallery.getOriginal());
goodsSku.setOriginal(goodsGallery.getOriginal());
goodsSku.setThumbnail(goodsGallery.getThumbnail());
goodsSku.setSmall(goodsGallery.getSmall());
}
/**
@@ -766,4 +973,151 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
return skuSpecVOList;
}
/**
* 初始化填充商品库存导出数据
*
* @param goodsSkuStockDTOList 导出的库存数据
* @return 商品库存导出列表
*/
private XSSFWorkbook initStockExportData(List<GoodsSkuStockDTO> goodsSkuStockDTOList) {
XSSFWorkbook workbook = new XSSFWorkbook();
// 创建模板
this.createTemplate(workbook);
// 创建sku库存列表
this.skuStockList(workbook, goodsSkuStockDTOList);
// 创建sku库存列表
this.skuList(workbook, goodsSkuStockDTOList);
return workbook;
}
/**
* 创建模板
*
* @param workbook
*/
private void createTemplate(XSSFWorkbook workbook) {
Sheet templateSheet = workbook.createSheet("商品库存编辑模板");
// 创建表头
Row description = templateSheet.createRow(0);
description.setHeightInPoints(90);
Cell descriptionCell = description.createCell(0);
descriptionCell.setCellValue("填写说明(请勿删除本说明):\n" +
"1.可批量设置多个商品的库存一次最多10000行\n" +
"2.库存修改方向:选择库存方向后,会在原先库存基础上进行增加或者减少\n" +
"3.库存变更数量需要为整数不能填写0和负数");
// 合并描述行的单元格
templateSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3));
// 设置描述行单元格样式(例如,自动换行)
CellStyle descriptionStyle = workbook.createCellStyle();
descriptionStyle.setWrapText(true);
descriptionStyle.setAlignment(HorizontalAlignment.LEFT);
descriptionStyle.setVerticalAlignment(VerticalAlignment.CENTER);
descriptionCell.setCellStyle(descriptionStyle);
// 创建表头
Row header = templateSheet.createRow(1);
String[] headers = {"商品ID必填", "skuID必填", "库存修改方向(必填,填 增 或者 减)", "库存变更数量(必填)"};
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
//修改列宽
templateSheet.setColumnWidth(0, 30 * 256);
templateSheet.setColumnWidth(1, 30 * 256);
templateSheet.setColumnWidth(2, 40 * 256);
templateSheet.setColumnWidth(3, 25 * 256);
// 设置下拉列表数据验证
DataValidationHelper validationHelper = templateSheet.getDataValidationHelper();
DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(new String[]{"", ""});
// 从第3行到第10002行第3列
CellRangeAddressList addressList = new CellRangeAddressList(2, 10001, 2, 2);
DataValidation validation = validationHelper.createValidation(constraint, addressList);
validation.setSuppressDropDownArrow(true);
validation.setShowErrorBox(true);
templateSheet.addValidationData(validation);
}
/**
* 创建sku库存列表
*
* @param workbook
*/
private void skuStockList(XSSFWorkbook workbook, List<GoodsSkuStockDTO> goodsSkuStockDTOList) {
Sheet skuListSheet = workbook.createSheet("商品库存信息");
// 创建表头
Row header = skuListSheet.createRow(0);
String[] headers = {"商品ID", "商品名称", "规格ID(SKUID)", "规格名称", "货号", "当前库存数量"};
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
}
// 填充数据
for (int i = 0; i < goodsSkuStockDTOList.size(); i++) {
GoodsSkuStockDTO dto = goodsSkuStockDTOList.get(i);
Row row = skuListSheet.createRow(i + 1);
row.createCell(0).setCellValue(dto.getGoodsId());
row.createCell(1).setCellValue(dto.getGoodsName());
row.createCell(2).setCellValue(dto.getSkuId());
row.createCell(3).setCellValue(dto.getSimpleSpecs());
row.createCell(4).setCellValue(dto.getSn());
row.createCell(5).setCellValue(dto.getQuantity());
}
//修改列宽
skuListSheet.setColumnWidth(0, 30 * 256);
skuListSheet.setColumnWidth(1, 30 * 256);
skuListSheet.setColumnWidth(2, 30 * 256);
skuListSheet.setColumnWidth(3, 30 * 256);
skuListSheet.setColumnWidth(4, 30 * 256);
skuListSheet.setColumnWidth(5, 15 * 256);
}
private void skuList(XSSFWorkbook workbook, List<GoodsSkuStockDTO> goodsSkuStockDTOList) {
Sheet skuListSheet = workbook.createSheet("商品规格");
// 创建表头
Row header = skuListSheet.createRow(0);
String[] headers = {"商品ID", "商品名称", "规格ID(SKUID)", "规格名称", "货号"};
for (int i = 0; i < headers.length; i++) {
Cell cell = header.createCell(i);
cell.setCellValue(headers[i]);
}
// 填充数据
for (int i = 0; i < goodsSkuStockDTOList.size(); i++) {
GoodsSkuStockDTO dto = goodsSkuStockDTOList.get(i);
Row row = skuListSheet.createRow(i + 1);
row.createCell(0).setCellValue(dto.getGoodsId());
row.createCell(1).setCellValue(dto.getGoodsName());
row.createCell(2).setCellValue(dto.getSkuId());
row.createCell(3).setCellValue(dto.getSimpleSpecs());
row.createCell(4).setCellValue(dto.getSn());
}
//修改列宽
skuListSheet.setColumnWidth(0, 30 * 256);
skuListSheet.setColumnWidth(1, 30 * 256);
skuListSheet.setColumnWidth(2, 30 * 256);
skuListSheet.setColumnWidth(3, 30 * 256);
skuListSheet.setColumnWidth(4, 30 * 256);
}
}

View File

@@ -64,7 +64,7 @@ public class GoodsSkuBuilder {
Map<String, Object> specMap = new LinkedHashMap<>();
// 原始规格项
String[] ignoreOriginKeys = {"id", "sn", "cost", "price", "quantity", "weight"};
String[] ignoreOriginKeys = {"id", "sn", "cost", "price", "quantity", "weight", "alertQuantity"};
//获取规格信息
for (Map.Entry<String, Object> spec : skuInfo.entrySet()) {
//保存新增规格信息
@@ -91,6 +91,7 @@ public class GoodsSkuBuilder {
goodsSku.setQuantity(Convert.toInt(skuInfo.get("quantity"), 0));
goodsSku.setSpecs(JSONUtil.toJsonStr(specMap));
goodsSku.setSimpleSpecs(simpleSpecs.toString());
goodsSku.setAlertQuantity(Convert.toInt(skuInfo.get("alertQuantity"), 0));
}

View File

@@ -0,0 +1,47 @@
package cn.lili.modules.im.entity.dto;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.modules.im.entity.dos.ImTalk;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author paulG
* @since 2023/2/6
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class IMTalkQueryParams {
@ApiModelProperty("用户1 id")
private String userId1;
@ApiModelProperty("用户2 id")
private String userId2;
@ApiModelProperty("用户1 name")
private String name1;
@ApiModelProperty("用户2 name")
private String name2;
@ApiModelProperty("关键字")
private String userName;
public QueryWrapper<ImTalk> queryWrapper() {
QueryWrapper<ImTalk> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(CharSequenceUtil.isNotEmpty(userId1), "user_id1", this.userId1);
queryWrapper.eq(CharSequenceUtil.isNotEmpty(userId2), "user_id2", this.userId2);
queryWrapper.eq(CharSequenceUtil.isNotEmpty(name1), "name1", this.name1);
queryWrapper.eq(CharSequenceUtil.isNotEmpty(name2), "name2", this.name2);
queryWrapper.nested(CharSequenceUtil.isNotEmpty(userName), i -> i.like("name1", userName).or().like("name2", userName));
return queryWrapper;
}
}

View File

@@ -6,15 +6,19 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author chc
* @since 2022/6/2114:46
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel
public class ImQueryParams extends PageVO {
private static final long serialVersionUID = 5792718094087541134L;
@ApiModelProperty("用户Id")
private String memberId;
@@ -29,7 +33,7 @@ public class ImQueryParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(storeId)) {
queryWrapper.eq("store_id", storeId);
}
queryWrapper.eq("delete_flag",false);
queryWrapper.eq("delete_flag", false);
queryWrapper.orderByDesc("create_time");
return queryWrapper;
}

Some files were not shown because too many files have changed in this diff Show More