135 Commits
dev ... 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
铉清
2d052ffb1a 多了一个结束符号进行移除
Signed-off-by: 铉清 <13698617+xuanqings@user.noreply.gitee.com>
2024-04-19 05:17:44 +00:00
183 changed files with 6257 additions and 2936 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

View File

@@ -1,10 +0,0 @@
/**
交易唤醒表,增加交易流水详情
*/
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 '退款金额';

View File

@@ -1,20 +0,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';

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

@@ -68,8 +68,6 @@ CREATE TABLE `li_order_package_item` (
*/
ALTER TABLE li_order_item ADD `deliver_number` int DEFAULT NULL COMMENT '发货数量';
ALTER TABLE li_goods_sku ADD `alert_quantity` int DEFAULT NULL COMMENT '预警库存';
/*
sku增加预警库存
*/
@@ -105,7 +103,7 @@ 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)
AND b.end_time),0);
UPDATE li_bill b
SET b.kanjia_refund_settlement_price =IFNULL((
@@ -119,3 +117,57 @@ SET b.kanjia_refund_settlement_price =IFNULL((
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 '电子面单客户账户名称';

285
README.md
View File

@@ -1,204 +1,167 @@
## Lilishop 商城系统
# 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/)
---
### 1. 项目简介
**Lilishop** 是一款功能完善的B2B2C多商户商城系统采用前后端分离架构全端代码开源。后端基于 **SpringBoot** 构建具备高内聚、低耦合的特性支持分布式部署。前端覆盖PC、H5、小程序和APP基于 **Vue****uni-app** 开发。
- **官方网站**: <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)
#### 欢迎交流需求,交流业务,交流技术(基础问题自行解决,其他问题先看文档后提问)
---
#### 不用削尖脑袋往老群里加,老群活跃度较低,很多潜水党,新群相对而言活跃一些 :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606:
### 4. 快速开始
#### 开发新手或者不熟悉的同学在群内提问或新开Issue提问前请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【在线文档】](https://docs.pickmall.cn/) ,避免浪费大家的宝贵时间;
#### 环境准备与部署
详细的本地部署指南,请参考官方文档:
[**部署文档 -> 环境准备**](https://docs.pickmall.cn/deply/deply.html)
##### 交流 qq 1群 961316482 (1群扩建至2000人还有300坑)
##### 交流 qq 2群 875294241已满
##### 交流 qq 3群 263785057已满
##### 交流 qq 4群 674617534 (已满)
##### 交流 qq 5群 594675235 (已满)
##### 交流 qq 6群 917026848
#### 数据库初始化
- **推荐方式**: 使用项目提供的 `docker-compose` 配置可自动完成数据库MySQL, Redis, Elasticsearch等的部署与初始化。
- **手动方式**: 如果您选择手动部署SQL脚本位于以下地址。请确保获取与您代码版本一致的SQL文件。
[**数据库脚本 (Gitee)**](https://gitee.com/beijing_hongye_huicheng/docker/tree/master/init/mysql)
##### 体验 公众号/小程序/APP 体验,扫描二维码
---
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
### 5. 技术架构
[![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)
#### 5.1 架构图
![系统架构图](https://lili-system.oss-cn-beijing.aliyuncs.com/docs/%E6%9E%B6%E6%9E%84.png)
#### 5.2 后端技术栈
#### PS **演示站点所有环境均部署master分支。如果有演示站点问题可以反馈如果演示站点没问题本地运行有问题需自行处理**
| 技术 | 选型 | 备注/用途 |
| :-------------- | :-------------- | :--------- |
| 核心框架 | Spring Boot | 简化应用开发 |
| ORM框架 | Mybatis-Plus | 数据持久化 |
| 数据库 | MySQL | 关系型数据存储 |
| 消息队列 | RocketMQ | 异步任务与解耦 |
| 缓存 | Redis, MongoDB | 数据缓存与存储 |
| 搜索引擎 | Elasticsearch | 商品搜索 |
| 安全框架 | Spring Security | 认证与授权 |
| 分库分表 | ShardingSphere | 数据水平扩展 |
| 定时任务 | XXL-Job | 分布式任务调度 |
| 认证方案 | JWT | Token |
#### 5.3 前端技术栈
### 项目地址
**管理端 (平台/商家)**
gitee : https://gitee.com/beijing_hongye_huicheng
github 镜像: https://github.com/lilishop?tab=repositories
商城UI 项目下3个文件夹
buyer买家PC端seller商家端manager后台管理端
### 演示地址
PS手机验证码为 111111
**平台管理端**https://admin-b2b2c.pickmall.cn 账号admin/123456
**店铺管理端**https://store-b2b2c.pickmall.cn 账号13011111111/111111
**商城PC页面**https://pc-b2b2c.pickmall.cn
**商城 小程序/公众号/APP**:扫描二维码
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
### 快速本地部署
[点击跳转](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

@@ -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

@@ -29,7 +29,7 @@ import org.springframework.web.context.request.async.DeferredResult;
import javax.validation.constraints.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 买家端,会员接口
@@ -73,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;
@@ -83,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;
@@ -167,9 +178,13 @@ public class MemberBuyerController {
@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);
@@ -272,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

@@ -67,13 +67,19 @@ public class CouponBuyerController {
if (UserContext.getCurrentUser() == null) {
return ResultUtil.success();
}
return ResultUtil.data(couponActivityService.trigger(
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,5 +1,6 @@
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;
@@ -57,8 +58,10 @@ public class FileDirectoryController {
@ApiOperation(value = "删除文件目录")
@DeleteMapping("/{id}")
public ResultMessage<Object> deleteSceneFileList(@PathVariable String id) {
//删除文件夹下面的图片
fileService.batchDeleteByDirectory(id);
//检测文件夹下是否包含图片
if(fileService.countByDirectory(id)){
return ResultUtil.error(ResultCode.FILE_DIRECTORY_NOT_EMPTY);
}
//删除目录
fileDirectoryService.removeById(id);
return ResultUtil.success();

View File

@@ -55,6 +55,9 @@ public class UploadController {
String base64,
@RequestHeader String accessToken, @RequestParam String directoryPath) {
if(StrUtil.isBlank(directoryPath)){
directoryPath = "default";
}
AuthUser authUser = UserContext.getAuthUser(cache, accessToken);
//如果用户未登录,则无法上传图片

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,6 +20,19 @@ 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:
@@ -29,8 +42,8 @@ spring:
type: redis
# Redis
redis:
host: 192.168.31.100
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.100: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:
@@ -269,7 +269,7 @@ lili:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.31.100:30920
cluster-nodes: 127.0.0.1:9200
index:
number-of-replicas: 0
number-of-shards: 3
@@ -280,7 +280,7 @@ lili:
# password: LiLiShopES
logstash:
server: 192.168.31.100:30560
server: 127.0.0.1:4560
rocketmq:
promotion-topic: shop_lili_promotion_topic
promotion-group: shop_lili_promotion_group
@@ -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.100: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.100:30001/xxl-job-admin
addresses: http://127.0.0.1:9001/xxl-job-admin
executor:
appname: xxl-job-executor-lilishop
address:

View File

@@ -27,7 +27,7 @@ import org.springframework.stereotype.Service;
*/
@Slf4j
@Service
public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDayExecute, AfterSaleStatusChangeEvent {
public class DistributionOrderExecute implements OrderStatusChangeEvent, AfterSaleStatusChangeEvent {
/**
* 分销订单
@@ -35,10 +35,6 @@ public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDa
@Autowired
private DistributionOrderService distributionOrderService;
@Autowired
private SettingService settingService;
@Override
public void orderChange(OrderMessage orderMessage) {
@@ -62,21 +58,6 @@ 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())) {

View File

@@ -4,6 +4,8 @@ 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;
@@ -34,6 +36,8 @@ public class OrderStatusHandlerExecute implements TradeEvent, AfterSaleStatusCha
private OrderItemService orderItemService;
@Autowired
private OrderService orderService;
@Autowired
private AfterSaleService afterSaleService;
@Override
public void orderCreate(TradeDTO tradeDTO) {
@@ -64,10 +68,16 @@ public class OrderStatusHandlerExecute implements TradeEvent, AfterSaleStatusCha
int returnCount = 0;
// 总购买数量
int deliverCount = 0;
for (OrderItem item : orderItems) {
returnCount += item.getReturnGoodsNumber();
deliverCount += item.getNum();
//获取订单货物已完成售后的数量
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

@@ -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::getComplainStatus, OrderComplaintStatusEnum.COMPLETE));
.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;

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

@@ -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,33 +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) {
@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);
//判断是否有符合条件的订单,关闭允许售后申请处理
if (!orderItems.isEmpty()) {
orderItemService.expiredAfterSaleStatus(receiveTime);
//修改对应分销订单状态
distributionOrderService.updateDistributionOrderStatus(orderItems);
}
// OrderItemOperationDTO build = OrderItemOperationDTO.builder().receiveTime(receiveTime)
// .afterSaleStatus(OrderItemAfterSaleStatusEnum.NOT_APPLIED.name()).build();
// List<OrderItem> orderItems = orderItemService.waitOperationOrderItem(build);
//关闭售后订单=未售后订单+小于订单关闭售后申请时间
orderItemService.expiredAfterSaleStatus(receiveTime);
}
@@ -207,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);
//判断是否有符合条件的订单,关闭允许售后申请处理
@@ -217,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

@@ -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;
/**
* 促销分类枚举
*
@@ -52,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, "修改分销员失败"),
/**
* 购物车

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

@@ -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

@@ -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

@@ -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

@@ -5,6 +5,7 @@ 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;
@@ -45,22 +46,12 @@ public interface DistributionOrderService extends IService<DistributionOrder> {
/**
* 订单退款
* 记录分销订单
*
* @param 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,17 +171,12 @@ 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());
@@ -142,14 +185,7 @@ public class DistributionCashServiceImpl extends ServiceImpl<DistributionCashMap
//分销员金额相关处理
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);
@@ -157,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,33 +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;
@@ -39,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;
/**
* 分销订单接口实现
@@ -54,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 {
/**
* 订单
@@ -71,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());
}
@@ -100,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;
//循环店铺流水记录判断是否包含分销商品
//包含分销商品则进行记录分销订单、计算分销总额
@@ -116,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 订单编号
*/
@@ -162,41 +128,35 @@ 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(AfterSale afterSale) {
//判断是否为分销订单
StoreFlow refundStoreFlow = storeFlowService.queryOne(StoreFlowQueryDTO.builder().justDistribution(true).refundSn(afterSale.getSn()).build());
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, afterSale.getOrderItemSn()));
DistributionOrder distributionOrder = this.getOne(
new LambdaQueryWrapper<DistributionOrder>().eq(DistributionOrder::getOrderItemSn,
afterSale.getOrderItemSn()));
//分销订单不存在,则直接返回
if (distributionOrder == null) {
return;
@@ -204,143 +164,41 @@ public class DistributionOrderServiceImpl extends ServiceImpl<DistributionOrderM
if (distributionOrder.getSellBackRebate() == null) {
distributionOrder.setSellBackRebate(refundStoreFlow.getDistributionRebate());
} else {
distributionOrder.setSellBackRebate(CurrencyUtil.add(distributionOrder.getSellBackRebate(), refundStoreFlow.getDistributionRebate()));
distributionOrder.setSellBackRebate(
CurrencyUtil.add(distributionOrder.getSellBackRebate(), refundStoreFlow.getDistributionRebate()));
}
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);
// 修改分销员提成金额
distributionService.subCanRebate(CurrencyUtil.sub(0, refundStoreFlow.getDistributionRebate()), distributionOrder.getDistributionId());
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

@@ -90,7 +90,7 @@ public class DistributionServiceImpl extends ServiceImpl<DistributionMapper, Dis
default:
throw new ServiceException(ResultCode.DISTRIBUTION_IS_APPLY);
}
}else{
} else {
//如果未申请分销员则新增进行申请
//获取当前登录用户
Member member = memberService.getUserInfo();
@@ -181,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

@@ -31,6 +31,13 @@ public interface FileService extends IService<File> {
*/
void batchDeleteByDirectory(String directoryId);
/**
* 根据文件夹ID查看是否有文件
* @param directoryId 文件夹ID
* @return
*/
Boolean countByDirectory(String directoryId);
/**
* 所有者批量删除
*

View File

@@ -4,8 +4,6 @@ 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.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;
@@ -15,11 +13,10 @@ 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;
/**
* 文件管理业务层实现
@@ -42,7 +39,9 @@ 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);
}
@@ -54,10 +53,17 @@ 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 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<>();
@@ -88,26 +94,33 @@ public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements Fi
public IPage<File> customerPage(FileOwnerDTO fileOwnerDTO) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
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());
.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 fileOwnerDTO) {
LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
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());
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

@@ -11,6 +11,7 @@ import lombok.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 商品查询条件
@@ -27,6 +28,7 @@ public class GoodsSearchParams extends PageVO {
private static final long serialVersionUID = 2544015852728566887L;
@ApiModelProperty(value = "商品编号")
private String goodsId;
@@ -69,6 +71,9 @@ public class GoodsSearchParams extends PageVO {
@ApiModelProperty(value = "审核状态")
private String authFlag;
@ApiModelProperty(value = "商品状态")
private String goodsStatus;
@ApiModelProperty(value = "库存数量")
private Integer leQuantity;
@@ -95,6 +100,8 @@ public class GoodsSearchParams extends PageVO {
public <T> QueryWrapper<T> queryWrapper() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 统一使用 CharSequenceUtil.isNotEmpty() 处理字符串
if (CharSequenceUtil.isNotEmpty(goodsId)) {
queryWrapper.eq("goods_id", goodsId);
}
@@ -104,9 +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);
}
@@ -119,36 +129,55 @@ 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) {
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);
}
if (CharSequenceUtil.isNotEmpty(salesModel)) {
queryWrapper.eq("sales_model", salesModel);
}
if(alertQuantity != null && alertQuantity){
if(Objects.nonNull(alertQuantity) && alertQuantity){
queryWrapper.apply("quantity <= alert_quantity");
queryWrapper.ge("alert_quantity", 0);
}
queryWrapper.in(CollUtil.isNotEmpty(ids), "id", ids);
queryWrapper.in(CollUtil.isNotEmpty(ids), "id", ids);
queryWrapper.eq("delete_flag", false);
this.betweenWrapper(queryWrapper);
return queryWrapper;

View File

@@ -23,4 +23,19 @@ public class GoodsSkuStockDTO {
@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

@@ -3,6 +3,8 @@ 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;
@@ -115,6 +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 FROM li_goods_sku ${ew.customSqlSegment}")
@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

@@ -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);
/**
* 商品查询
@@ -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,7 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @param goodsSkuStockDTOS sku库存修改实体
*/
void updateStocks(List<GoodsSkuStockDTO> goodsSkuStockDTOS);
void updateStocksByType(List<GoodsSkuStockDTO> goodsSkuStockDTOS);
/**
* 更新SKU预警库存
@@ -207,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库存
@@ -279,4 +296,13 @@ public interface GoodsSkuService extends IService<GoodsSku> {
* @return 库存数量
*/
Integer getGoodsStock(String goodsId);
/**
* 更新sku运费模版
*
* @param goodsId 商品id
* @param templateId 运费模版id
* @return 操作结果
*/
Boolean freight(List<String> goodsId, String templateId);
}

View File

@@ -205,7 +205,7 @@ 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("上架"));
goodsImportDTO.setRelease("上架".equals(objects.get(5).toString()));
List<String> goodsGalleryList = new ArrayList<>();
goodsGalleryList.add(objects.get(6).toString());

View File

@@ -20,6 +20,7 @@ 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;
@@ -272,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;
}
@@ -281,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;
}
/**
* 商品查询
*
@@ -437,6 +472,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
lambdaUpdateWrapper.in(Goods::getId, 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);
}

View File

@@ -1,6 +1,8 @@
package cn.lili.modules.goods.serviceimpl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
@@ -23,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;
@@ -54,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;
@@ -493,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);
@@ -529,6 +652,27 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
}
}
@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());
@@ -559,7 +703,6 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
this.updateBatchById(goodsSkuList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateStock(String skuId, Integer quantity) {
@@ -591,6 +734,45 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
}
}
@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));
}
}
}
@Override
public Integer getStock(String skuId) {
String cacheKeys = GoodsSkuService.getStockCacheKey(skuId);
@@ -707,6 +889,17 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
}
@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
*
@@ -743,6 +936,9 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
* @param goodsSku sku
*/
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;
@@ -777,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

@@ -46,7 +46,7 @@ public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> impleme
@Autowired
private ImMessageService imMessageService;
@Override
public ImTalk getTalkByUser(String userId) {
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser());

View File

@@ -131,50 +131,67 @@ public class KdniaoPlugin implements LogisticsPlugin {
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
//组装快递鸟应用级参数
String resultDate = "{" +
"'OrderCode': '" + order.getSn() + "'," + //订单编码
"'ShipperCode': '" + logistics.getCode() + "'," + //快递公司编码
"'CustomerName': '" + storeLogistics.getCustomerName() + "'," +//客户编码
"'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "'," + //客户密码
"'MonthCode': '" + storeLogistics.getMonthCode() + "'," + //密钥
"'SendSite': '" + storeLogistics.getSendSite() + "'," + //归属网点
"'SendStaff': '" + storeLogistics.getSendStaff() + "'," + //收件快递员
"'PayType': " + storeLogistics.getPayType() + "," +
"'ExpType': " + storeLogistics.getExpType() + "," +
//发件人信息
"'Sender': {" +
"'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "'," +
"'Mobile': '" + storeDeliverGoodsAddressDTO.getSalesConsignorMobile() + "'," +
"'ProvinceName': '" + consignorAddress[0] + "'," + //省
"'CityName': '" + consignorAddress[1] + "'," + //市
"'ExpAreaName': '" + consignorAddress[2] + "'," + //区
"'Address': '" + storeDeliverGoodsAddressDTO.getSalesConsignorDetail() + "'" + //发件人详细地址
"}," +
//收件人信息
"'Receiver': {" +
"'Name': '" + order.getConsigneeName() + "'," +
"'Mobile': '" + order.getConsigneeMobile() + "'," +
"'ProvinceName': '" + ConsigneeAddress[0] + "'," + //
"'CityName': '" + ConsigneeAddress[1] + "'," + //市
"'ExpAreaName': '" + ConsigneeAddress[2] + "'," + //区
"'Address': '" + order.getConsigneeDetail() + "'" + //收件人详细地址
"}," +
//商品信息
"'Commodity': [";
String resultDate = "{"
// 订单编码
+ "'OrderCode': '" + order.getSn() + "',"
// 快递公司编码
+ "'ShipperCode': '" + logistics.getCode() + "',"
// 客户编码
+ "'CustomerName': '" + storeLogistics.getCustomerName() + "',"
// 客户密码
+ "'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "',"
// 密钥
+ "'MonthCode': '" + storeLogistics.getMonthCode() + "',"
// 归属网点
+ "'SendSite': '" + storeLogistics.getSendSite() + "',"
// 收件快递员
+ "'SendStaff': '" + storeLogistics.getSendStaff() + "',"
+ "'PayType': " + storeLogistics.getPayType() + ","
+ "'ExpType': " + storeLogistics.getExpType() + ","
// 发件人信息
+ "'Sender': {"
+ "'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "',"
+ "'Mobile': '" + storeDeliverGoodsAddressDTO.getSalesConsignorMobile() + "',"
// 省
+ "'ProvinceName': '" + consignorAddress[0] + "',"
//
+ "'CityName': '" + consignorAddress[1] + "',"
//
+ "'ExpAreaName': '" + consignorAddress[2] + "',"
// 发件人详细地址
+ "'Address': '" + storeDeliverGoodsAddressDTO.getSalesConsignorDetail() + "'"
+ "},"
// 收件人信息
+ "'Receiver': {"
+ "'Name': '" + order.getConsigneeName() + "',"
+ "'Mobile': '" + order.getConsigneeMobile() + "',"
// 省
+ "'ProvinceName': '" + ConsigneeAddress[0] + "',"
// 市
+ "'CityName': '" + ConsigneeAddress[1] + "',"
// 区
+ "'ExpAreaName': '" + ConsigneeAddress[2] + "',"
// 收件人详细地址
+ "'Address': '" + order.getConsigneeDetail() + "'"
+ "},"
// 商品信息
+ "'Commodity': [";
//子订单信息
for (OrderItem orderItem : orderItems) {
resultDate = resultDate + "{" +
"'GoodsName': '" + orderItem.getGoodsName() + "'," +
"'Goodsquantity': '" + orderItem.getNum() + "'" +
"},";
resultDate = resultDate + "{"
+ "'GoodsName': '" + orderItem.getGoodsName() + "',"
+ "'Goodsquantity': '" + orderItem.getNum() + "'"
+ "},";
}
resultDate = resultDate + "]," +
"'Quantity': " + orderItems.size() + "," + //包裹数
"'IsReturnPrintTemplate':1," + //生成电子面单模板
"'Remark': '" + order.getRemark() + "'" +//商家备注
"}";
resultDate = resultDate + "],"
// 包裹数
+ "'Quantity': " + orderItems.size() + ","
// 生成电子面单模板
+ "'IsReturnPrintTemplate':1,"
// 商家备注
+ "'Remark': '" + order.getRemark() + "'"
+ "}";
//组织系统级参数
Map<String, String> params = new HashMap<>();
@@ -200,9 +217,9 @@ public class KdniaoPlugin implements LogisticsPlugin {
JSONObject obj = JSONObject.parseObject(result);
log.info("电子面单响应:{}", result);
if (!"100".equals(obj.getString("ResultCode"))) {
// resultMap.put("Reason",obj.getString("Reason"));
// resultMap.put("Reason",obj.getString("Reason"));
throw new ServiceException(obj.getString("Reason"));
// return resultMap;
// return resultMap;
}
JSONObject orderJson = JSONObject.parseObject(obj.getString("Order"));

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.logistics.plugin.kuaidi100;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.modules.logistics.LogisticsPlugin;
import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
@@ -28,6 +29,7 @@ import com.kuaidi100.sdk.request.labelV2.OrderReq;
import com.kuaidi100.sdk.response.QueryTrackData;
import com.kuaidi100.sdk.response.QueryTrackMapResp;
import com.kuaidi100.sdk.response.QueryTrackResp;
import com.kuaidi100.sdk.response.labelV2.OrderResult;
import com.kuaidi100.sdk.response.samecity.OrderResp;
import com.kuaidi100.sdk.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
@@ -140,25 +142,74 @@ public class Kuaidi100Plugin implements LogisticsPlugin {
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
ManInfo recManInfo = new ManInfo();
recManInfo.setName(order.getConsigneeName());
recManInfo.setMobile(order.getConsigneeMobile());
recManInfo.setPrintAddr(consigneeAddress[0] + consigneeAddress[1] + consigneeAddress[2] + consigneeAddress[3] + order.getConsigneeDetail());
ManInfo sendManInfo = new ManInfo();
sendManInfo.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName());
sendManInfo.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile());
sendManInfo.setPrintAddr(consignorAddress[0] + consignorAddress[1] + consignorAddress[2] + consignorAddress[3] + storeDeliverGoodsAddressDTO.getSalesConsignorDetail());
OrderReq orderReq = new OrderReq();
//打印类型NON只下单不打印默认 IMAGE:生成图片短链HTML:生成html短链 CLOUD:使用快递100云打印机打印使用CLOUD时siid必填
orderReq.setPrintType(PrintType.HTML);
//电子面单客户账户或月结账号,需贵司向当地快递公司网点申请(参考电子面单申请指南); 是否必填该属性,请查看参数字典
orderReq.setPartnerId(storeLogistics.getCustomerName());
//电子面单密码,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getCustomerPwd())){
orderReq.setPartnerKey(storeLogistics.getCustomerPwd());
}
//电子面单密钥,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getMonthCode())) {
orderReq.setPartnerSecret(storeLogistics.getMonthCode());
}
//电子面单客户账户名称,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getPartnerName())) {
orderReq.setPartnerName(storeLogistics.getPartnerName());
}
// orderReq.setNet();
// 电子面单承载编号,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getSendSite())) {
orderReq.setCode(storeLogistics.getSendSite());
}
//电子面单承载快递员名,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getSendStaff())) {
orderReq.setCheckMan(storeLogistics.getSendStaff());
}
//快递公司的编码,一律用小写字母,请查看参数字典
orderReq.setKuaidicom(logistics.getCode());
orderReq.setCount(1);
//收件人信息
ManInfo manInfo=new ManInfo();
//收件人姓名
manInfo.setName(order.getConsigneeName());
//收件人的手机号,手机号和电话号二者其一必填
manInfo.setMobile(order.getConsigneeMobile());
//收件人的电话号,手机号和电话号二者其一必填
// manInfo.setTel("");
//收件人所在完整地址如广东深圳市南山区科技南十二路金蝶软件园B10
manInfo.setPrintAddr(consigneeAddress[0]+consigneeAddress[1]+consigneeAddress[2]+consigneeAddress[3]+order.getConsigneeDetail());
orderReq.setRecMan(manInfo);
ManInfo sendMan=new ManInfo();
// 寄件人信息
sendMan.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName());
// 寄件人的手机号,手机号和电话号二者其一必填
sendMan.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile());
//寄件人的电话号,手机号和电话号二者其一必填
// sendMan.setTel("");
//寄件人所在的完整地址如广东深圳市南山区科技南十二路金蝶软件园B10
sendMan.setPrintAddr(consignorAddress[0]+consignorAddress[1]+consignorAddress[2]+consignorAddress[3]+storeDeliverGoodsAddressDTO.getSalesConsignorDetail());
//寄件人所在公司名称
// sendMan.setCompany("");
orderReq.setSendMan(sendMan);
//物品名称,例:文件
String goodsName="";
for (OrderItem orderItem : orderItems) {
goodsName+=orderItem.getGoodsName() + "',";
}
orderReq.setCargo(goodsName);
// 包裹总数量。
orderReq.setCount(orderItems.size());
//打印设备通过打印机输出的设备码进行获取printType为CLOUD时必填
// orderReq.setSiid("");
// orderReq.setSiid(siid);
//orderReq.setTempId("60f6c17c7c223700131d8bc3");
orderReq.setSendMan(sendManInfo);
orderReq.setRecMan(recManInfo);
orderReq.setPrintType(PrintType.CLOUD);
String param = new Gson().toJson(orderReq);
String t = System.currentTimeMillis() + "";
@@ -167,14 +218,19 @@ public class Kuaidi100Plugin implements LogisticsPlugin {
printReq.setT(t);
printReq.setKey(logisticsSetting.getKuaidi100Key());
printReq.setSign(SignUtils.printSign(param, t, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer()));
printReq.setMethod(ApiInfoConstant.ORDER);
printReq.setMethod(ApiInfoConstant.NEW_TEMPLATE_URL);
printReq.setParam(param);
IBaseClient baseClient = new LabelV2();
HttpResult result = baseClient.execute(printReq);
System.out.println(result.getBody());
QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class);
OrderResp orderResp = new Gson().fromJson(result.getBody(), OrderResp.class);
OrderResult orderResult = new Gson().fromJson(result.getBody(), OrderResult.class);
log.info("电子面单响应:{}", orderResult);
System.out.println("快递单号:"+orderResult.getKdComOrderNum());
System.out.println("面单短链:"+orderResult.getLabel());
} catch (Exception e) {
e.printStackTrace();

View File

@@ -22,13 +22,13 @@ import com.sf.csim.express.service.HttpClientUtil;
import com.sf.csim.express.service.IServiceCodeStandard;
import com.sf.csim.express.service.code.ExpressServiceCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* 顺丰插件
*
* @author admin
*/
@Slf4j
@@ -43,7 +43,8 @@ public class ShunfengPlugin implements LogisticsPlugin {
**/
private LogisticsSetting logisticsSetting;
public ShunfengPlugin(){}
public ShunfengPlugin() {
}
public ShunfengPlugin(LogisticsSetting logisticsSetting) {
this.logisticsSetting = logisticsSetting;
@@ -59,10 +60,11 @@ public class ShunfengPlugin implements LogisticsPlugin {
*
* @param orderDetailVO
*/
@Override
public String createOrder(OrderDetailVO orderDetailVO) {
StoreDetailService storeService = SpringContextUtil.getBean(StoreDetailService.class);
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeService.getStoreDeliverGoodsAddressDto(orderDetailVO.getOrder().getStoreId());
if(storeDeliverGoodsAddressDTO == null){
if (storeDeliverGoodsAddressDTO == null) {
throw new ServiceException(ResultCode.STORE_DELIVER_ADDRESS_EXIST);
}
try {
@@ -108,7 +110,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
String result = sendPost(ExpressServiceCodeEnum.EXP_RECE_CREATE_ORDER, msgDataMap);
JSONObject resultData = JSONUtil.parseObj(result).getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("msgData").getJSONArray("waybillNoInfoList").getJSONObject(0).get("waybillNo").toString();
}
throw new ServiceException(resultData.get("errorMsg").toString());
@@ -141,10 +143,10 @@ public class ShunfengPlugin implements LogisticsPlugin {
msgDataMap.put("trackingNumber", trackingNumber);
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_ROUTES, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
JSONArray routesJson = resultData.getJSONObject("msgData").getJSONArray("routeResps").getJSONObject(0).getJSONArray("routes");
List<Map> routes = routesJson.toList(Map.class);
return new Traces(logistics.getName(),expNo,routes);
return new Traces(logistics.getName(), expNo, routes);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
@@ -164,7 +166,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
* @return
*/
@Override
public Map<String,Object> labelOrder(LabelOrderDTO labelOrderDTO) {
public Map<String, Object> labelOrder(LabelOrderDTO labelOrderDTO) {
try {
Map<String, Object> msgDataMap = new HashMap<>();
//模板编码
@@ -173,15 +175,15 @@ public class ShunfengPlugin implements LogisticsPlugin {
//业务数据
Map<String, Object> documents = new HashMap<>();
documents.put("masterWaybillNo", labelOrderDTO.getOrder().getLogisticsNo());
msgDataMap.put("documents",documents);
msgDataMap.put("sync",true);
msgDataMap.put("documents", documents);
msgDataMap.put("sync", true);
/**
* 版本号,传固定值:2.0
*/
msgDataMap.put("version", "2.0");
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.COM_RECE_CLOUD_PRINT_WAYBILLS, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("obj").getJSONArray("files").toList(Map.class).get(0);
}
throw new ServiceException(resultData.getJSONArray("errorMessage").get(0).toString());
@@ -204,13 +206,13 @@ public class ShunfengPlugin implements LogisticsPlugin {
//校验类型 1电话号码校验 2月结卡号校验
msgDataMap.put("checkType", 1);
//校验值 当校验类型为1时传电话号码 当校验类型为2时传月结卡号
List<String> mobileList= new ArrayList<>();
List<String> mobileList = new ArrayList<>();
mobileList.add(checkNos);
msgDataMap.put("checkNos", mobileList);
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_PROMITM, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
return resultData.getJSONObject("msgData").get("promiseTm").toString();
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("msgData").get("promiseTm").toString();
}
throw new ServiceException(resultData.get("errorMsg").toString());
} catch (UnsupportedEncodingException e) {
@@ -219,7 +221,6 @@ public class ShunfengPlugin implements LogisticsPlugin {
}
private String sendPost(IServiceCodeStandard standardService, Map<String, Object> msgDataMap) throws UnsupportedEncodingException {
CallExpressServiceTools tools = CallExpressServiceTools.getInstance();
Map<String, String> params = new HashMap<String, String>();
String timeStamp = String.valueOf(System.currentTimeMillis());
// 顾客编码
@@ -230,7 +231,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
params.put("timestamp", timeStamp);
params.put("msgData", JSONUtil.toJsonStr(msgDataMap));
params.put("msgDigest", tools.getMsgDigest(params.get("msgData"), timeStamp, logisticsSetting.getCheckWord()));
params.put("msgDigest", CallExpressServiceTools.getMsgDigest(params.get("msgData"), timeStamp, logisticsSetting.getCheckWord()));
String result = HttpClientUtil.post(logisticsSetting.getCallUrl(), params);
log.info("===调用地址 ===" + logisticsSetting.getCallUrl());

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.member.entity.dos;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.modules.member.entity.dto.ClerkAddDTO;
import cn.lili.modules.store.entity.dos.Store;
@@ -59,7 +60,7 @@ public class Clerk extends BaseEntity {
* @param clerkAddDTO
*/
public Clerk(ClerkAddDTO clerkAddDTO) {
if (clerkAddDTO.getRoles()!=null && !clerkAddDTO.getRoles().isEmpty()) {
if (CollUtil.isNotEmpty(clerkAddDTO.getRoles()) && !clerkAddDTO.getRoles().isEmpty()) {
this.roleIds = CharSequenceUtil.join(",", clerkAddDTO.getRoles());
}
this.memberId = clerkAddDTO.getMemberId();

View File

@@ -24,13 +24,16 @@ public class MemberGrade extends BaseEntity {
private String gradeName;
@NotNull
@ApiModelProperty(value = "等级图片")
@ApiModelProperty(value = "等级图片 1029*498")
private String gradeImage;
@NotNull
@ApiModelProperty(value = "所需经验值")
private Integer experienceValue;
@ApiModelProperty(value = "会员等级")
private Integer level;
@ApiModelProperty(value = "是否为默认等级")
private Boolean isDefault;
@ApiModelProperty(value = "累计实付")
private Double payPrice;
@ApiModelProperty(value = "累计购买次数")
private Integer count;
}

View File

@@ -135,10 +135,9 @@ public interface MemberService extends IService<Member> {
/**
* 注销账号
*
* @param password 密码
* @return 操作结果
*/
void cancellation(String password);
void cancellation();
/**
* 修改当前会员的手机号
*

View File

@@ -102,8 +102,18 @@ public class MemberEvaluationServiceImpl extends ServiceImpl<MemberEvaluationMap
public MemberEvaluationDTO addMemberEvaluation(MemberEvaluationDTO memberEvaluationDTO, Boolean isSelf) {
//获取子订单信息
OrderItem orderItem = orderItemService.getBySn(memberEvaluationDTO.getOrderItemSn());
if (orderItem == null) {
throw new ServiceException(ResultCode.ORDER_ITEM_NOT_EXIST);
}
//获取订单信息
Order order = orderService.getBySn(orderItem.getOrderSn());
if (order == null) {
throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
}
//检测是否可以添加会员评价
Member member;
@@ -119,8 +129,14 @@ public class MemberEvaluationServiceImpl extends ServiceImpl<MemberEvaluationMap
throw new ServiceException(ResultCode.USER_NOT_EXIST);
}
}
//获取商品信息
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(memberEvaluationDTO.getSkuId());
if (goodsSku == null) {
throw new ServiceException(ResultCode.GOODS_NOT_EXIST);
}
//新增用户评价
MemberEvaluation memberEvaluation = new MemberEvaluation(memberEvaluationDTO, goodsSku, member, order);
//过滤商品咨询敏感词

View File

@@ -118,7 +118,11 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
public Member getUserInfo() {
AuthUser tokenUser = UserContext.getCurrentUser();
if (tokenUser != null) {
return this.findByUsername(tokenUser.getUsername());
Member member = this.findByUsername(tokenUser.getUsername());
if(member != null && !member.getDisabled()){
throw new ServiceException(ResultCode.USER_STATUS_ERROR);
}
return member;
}
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
@@ -233,7 +237,7 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
private Member findMember(String userName) {
QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", userName).or().eq("mobile", userName);
return this.getOne(queryWrapper);
return this.getOne(queryWrapper, false);
}
@Override
@@ -286,12 +290,16 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
public Token mobilePhoneLogin(String mobilePhone) {
QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("mobile", mobilePhone);
Member member = this.baseMapper.selectOne(queryWrapper);
Member member = this.baseMapper.selectOne(queryWrapper, false);
//如果手机号不存在则自动注册用户
if (member == null) {
member = new Member(mobilePhone, UuidUtils.getUUID(), mobilePhone);
registerHandler(member);
}
//判断用户是否有效
if (member.getDisabled().equals(false) || member.getDeleteFlag().equals(true)) {
throw new ServiceException(ResultCode.USER_STATUS_ERROR);
}
loginBindUser(member);
return memberTokenGenerate.createToken(member, false);
}
@@ -327,6 +335,7 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
}
@DemoSite
@Override
public Member modifyPass(String oldPassword, String newPassword) {
AuthUser tokenUser = UserContext.getCurrentUser();
if (tokenUser == null) {
@@ -375,19 +384,17 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
}
@Override
public void cancellation(String password) {
public void cancellation() {
AuthUser tokenUser = UserContext.getCurrentUser();
if (tokenUser == null) {
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
Member member = this.getById(tokenUser.getId());
if (member.getPassword().equals(new BCryptPasswordEncoder().encode(password))) {
//删除联合登录
connectService.deleteByMemberId(member.getId());
//混淆用户信息
this.confusionMember(member);
}
//删除联合登录
connectService.deleteByMemberId(member.getId());
//混淆用户信息
this.confusionMember(member);
}
/**

View File

@@ -0,0 +1,33 @@
package cn.lili.modules.order.aftersale.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 售后数量VO
*
* @author Bulbasaur
* @since 2021/3/12 10:32 上午
*/
@Data
public class AfterSaleNumVO {
@ApiModelProperty(value = "申请中售后数量")
private Integer applyNum;
@ApiModelProperty(value = "已通过售后数量")
private Integer passNum;
@ApiModelProperty(value = "已拒绝售后数量")
private Integer refuseNum;
@ApiModelProperty(value = "待卖家收货售后数量")
private Integer buyerReturnNum;
@ApiModelProperty(value = "卖家确认收货售后数量")
private Integer sellerConfirmNum;
@ApiModelProperty(value = "卖家终止售后售后数量")
private Integer sellerTerminationNum;
@ApiModelProperty(value = "买家取消售后售后数量")
private Integer buyerCancelNum;
@ApiModelProperty(value = "等待平台退款售后数量")
private Integer waitRefundNum;
@ApiModelProperty(value = "已完成售后数量")
private Integer completeNum;
}

View File

@@ -86,20 +86,23 @@ public class AfterSaleSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(orderSn)) {
queryWrapper.like("order_sn", orderSn);
}
//按买家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) {
queryWrapper.eq("member_id", UserContext.getCurrentUser().getId());
}
//按卖家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) {
queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId());
if(UserContext.getCurrentUser() != null){
//按买家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) {
queryWrapper.eq("member_id", UserContext.getCurrentUser().getId());
}
//按卖家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) {
queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId());
}
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name())
&& CharSequenceUtil.isNotEmpty(storeId)
) {
queryWrapper.eq("store_id", storeId);
}
}
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name())
&& CharSequenceUtil.isNotEmpty(storeId)
) {
queryWrapper.eq("store_id", storeId);
}
if (CharSequenceUtil.isNotEmpty(memberName)) {
queryWrapper.like("member_name", memberName);
}

View File

@@ -4,6 +4,7 @@ package cn.lili.modules.order.aftersale.service;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.dto.AfterSaleDTO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleApplyVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleNumVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleVO;
import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO;
@@ -30,6 +31,9 @@ public interface AfterSaleService extends IService<AfterSale> {
*/
IPage<AfterSaleVO> getAfterSalePages(AfterSaleSearchParams saleSearchParams);
AfterSaleNumVO getAfterSaleNumVO(AfterSaleSearchParams saleSearchParams);
/**
* 查询导出售后信息
*

View File

@@ -2,9 +2,9 @@ package cn.lili.modules.order.aftersale.serviceimpl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.common.security.AuthUser;
@@ -17,6 +17,7 @@ import cn.lili.modules.order.aftersale.aop.AfterSaleLogPoint;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.dto.AfterSaleDTO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleApplyVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleNumVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleVO;
import cn.lili.modules.order.aftersale.mapper.AfterSaleMapper;
@@ -38,7 +39,6 @@ import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.vo.Traces;
import cn.lili.modules.system.service.LogisticsService;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
import cn.lili.rocketmq.tags.AfterSaleTagsEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -46,13 +46,14 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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 java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -95,17 +96,64 @@ public class AfterSaleServiceImpl extends ServiceImpl<AfterSaleMapper, AfterSale
*/
@Autowired
private RocketmqCustomProperties rocketmqCustomProperties;
/**
* RocketMQ
*/
@Autowired
private RocketMQTemplate rocketMQTemplate;
private ApplicationEventPublisher applicationEventPublisher;
@Override
public IPage<AfterSaleVO> getAfterSalePages(AfterSaleSearchParams saleSearchParams) {
return baseMapper.queryByParams(PageUtil.initPage(saleSearchParams), saleSearchParams.queryWrapper());
}
@Override
public AfterSaleNumVO getAfterSaleNumVO(AfterSaleSearchParams saleSearchParams) {
AfterSaleNumVO afterSaleNumVO = new AfterSaleNumVO();
// 获取基础查询条件
QueryWrapper<AfterSale> baseWrapper = saleSearchParams.queryWrapper();
// 使用聚合查询一次性获取所有状态的售后数量
List<Map<String, Object>> results = this.baseMapper.selectMaps(
baseWrapper.select(
"COUNT(CASE WHEN service_status = 'APPLY' THEN 1 END) as applyNum",
"COUNT(CASE WHEN service_status = 'PASS' THEN 1 END) as passNum",
"COUNT(CASE WHEN service_status = 'REFUSE' THEN 1 END) as refuseNum",
"COUNT(CASE WHEN service_status = 'BUYER_RETURN' THEN 1 END) as buyerReturnNum",
"COUNT(CASE WHEN service_status = 'SELLER_CONFIRM' THEN 1 END) as sellerConfirmNum",
"COUNT(CASE WHEN service_status = 'SELLER_TERMINATION' THEN 1 END) as sellerTerminationNum",
"COUNT(CASE WHEN service_status = 'BUYER_CANCEL' THEN 1 END) as buyerCancelNum",
"COUNT(CASE WHEN service_status = 'WAIT_REFUND' THEN 1 END) as waitRefundNum",
"COUNT(CASE WHEN service_status = 'COMPLETE' THEN 1 END) as completeNum"
)
);
if (!results.isEmpty()) {
Map<String, Object> result = results.get(0);
afterSaleNumVO.setApplyNum(((Number) result.get("applyNum")).intValue());
afterSaleNumVO.setPassNum(((Number) result.get("passNum")).intValue());
afterSaleNumVO.setRefuseNum(((Number) result.get("refuseNum")).intValue());
afterSaleNumVO.setBuyerReturnNum(((Number) result.get("buyerReturnNum")).intValue());
afterSaleNumVO.setSellerConfirmNum(((Number) result.get("sellerConfirmNum")).intValue());
afterSaleNumVO.setSellerTerminationNum(((Number) result.get("sellerTerminationNum")).intValue());
afterSaleNumVO.setBuyerCancelNum(((Number) result.get("buyerCancelNum")).intValue());
afterSaleNumVO.setWaitRefundNum(((Number) result.get("waitRefundNum")).intValue());
afterSaleNumVO.setCompleteNum(((Number) result.get("completeNum")).intValue());
} else {
// 如果没有结果设置默认值为0
afterSaleNumVO.setApplyNum(0);
afterSaleNumVO.setPassNum(0);
afterSaleNumVO.setRefuseNum(0);
afterSaleNumVO.setBuyerReturnNum(0);
afterSaleNumVO.setSellerConfirmNum(0);
afterSaleNumVO.setSellerTerminationNum(0);
afterSaleNumVO.setBuyerCancelNum(0);
afterSaleNumVO.setWaitRefundNum(0);
afterSaleNumVO.setCompleteNum(0);
}
return afterSaleNumVO;
}
@Override
public List<AfterSale> exportAfterSaleOrder(AfterSaleSearchParams saleSearchParams) {
return this.list(saleSearchParams.queryWrapper());
@@ -580,11 +628,10 @@ public class AfterSaleServiceImpl extends ServiceImpl<AfterSaleMapper, AfterSale
*
* @param afterSale 售后对象
*/
private void sendAfterSaleMessage(AfterSale afterSale) {
//发送售后创建消息
String destination = rocketmqCustomProperties.getAfterSaleTopic() + ":" + AfterSaleTagsEnum.AFTER_SALE_STATUS_CHANGE.name();
//发送订单变更mq消息
rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(afterSale), RocketmqSendCallbackBuilder.commonCallback());
@Transactional(rollbackFor = Exception.class)
public void sendAfterSaleMessage(AfterSale afterSale) {
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("发送售后单状态变更MQ消息", rocketmqCustomProperties.getAfterSaleTopic(),
AfterSaleTagsEnum.AFTER_SALE_STATUS_CHANGE.name(), JSONUtil.toJsonStr(afterSale)));
}
/**

View File

@@ -19,7 +19,11 @@ public enum DeliveryMethodEnum {
/**
* "物流"
*/
LOGISTICS("物流");
LOGISTICS("物流"),
/**
* 虚拟发货
*/
VIRTUAL("虚拟发货");
private final String description;

View File

@@ -212,14 +212,27 @@ public class CouponRender implements CartRenderStep {
MemberCouponDTO platformCoupon = tradeDTO.getPlatformCoupon();
//如果有勾选平台优惠券
if (platformCoupon != null) {
renderSku(tradeDTO, platformCoupon);
//判断该优惠券是否可以使用,如果可以进行价格渲染,如果不可以使用,去掉该优惠券的使用
boolean checkFlag = tradeDTO.getCanUseCoupons().stream().anyMatch(item -> item.getCouponId().equals(platformCoupon.getMemberCoupon().getCouponId()));
if(checkFlag){
renderSku(tradeDTO, platformCoupon);
}else{
tradeDTO.setPlatformCoupon(null);
}
}
//计算商家优惠券
Map<String, MemberCouponDTO> map = tradeDTO.getStoreCoupons();
if (map != null && map.size() > 0) {
for (MemberCouponDTO memberCouponDTO : map.values()) {
renderSku(tradeDTO, memberCouponDTO);
//判断该优惠券是否可以使用,如果可以进行价格渲染,如果不可以使用,去掉该优惠券的使用
boolean storeCouponCheck = tradeDTO.getCanUseCoupons().stream().anyMatch(item -> item.getCouponId().equals(memberCouponDTO.getMemberCoupon().getCouponId()));
if(storeCouponCheck){
renderSku(tradeDTO, memberCouponDTO);
}else{
map.values().remove(memberCouponDTO);
}
}
tradeDTO.setStoreCoupons(map);
}
}

View File

@@ -141,6 +141,7 @@ public class SkuPromotionRender implements CartRenderStep {
KanjiaActivitySearchParams kanjiaActivitySearchParams = new KanjiaActivitySearchParams();
kanjiaActivitySearchParams.setGoodsSkuId(cartSkuVO.getGoodsSku().getId());
kanjiaActivitySearchParams.setMemberId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
kanjiaActivitySearchParams.setKanjiaActivityId(cartSkuVO.getKanjiaId());
kanjiaActivitySearchParams.setStatus(KanJiaStatusEnum.SUCCESS.name());
KanjiaActivityVO kanjiaActivityVO = kanjiaActivityService.getKanjiaActivityVO(kanjiaActivitySearchParams);
//可以砍价金额购买,则处理信息
@@ -266,8 +267,8 @@ public class SkuPromotionRender implements CartRenderStep {
}
}
if (quantity != null && cartSkuVO.getNum() > (Integer) quantity) {//设置购物车未选中
//设置购物车未选中
if (quantity != null && cartSkuVO.getNum() > (Integer) quantity) {
cartSkuVO.setChecked(false);
//设置失效消息
cartSkuVO.setErrorMessage("促销商品库存不足,现有库存数量[" + quantity + "]");

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.order.cart.render.util;
import cn.hutool.core.map.MapUtil;
import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
@@ -10,7 +11,8 @@ import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* 促销价格计算业务层实现
@@ -33,7 +35,7 @@ public class PromotionPriceUtil {
PromotionTypeEnum promotionTypeEnum, String activityId) {
// sku 促销信息非空判定
if (skuPromotionDetail == null || skuPromotionDetail.size() == 0) {
if (skuPromotionDetail == null || skuPromotionDetail.isEmpty()) {
return;
}
@@ -68,40 +70,39 @@ public class PromotionPriceUtil {
List<CartSkuVO> skuVOList = tradeDTO.getSkuList();
// 获取map分配sku的总数如果是最后一个商品分配金额则将金额从百分比改为总金额扣减避免出现小数除不尽
int count = skuPromotionDetail.size();
AtomicInteger count = new AtomicInteger(skuPromotionDetail.size());
//已优惠金额
Double deducted = 0D;
AtomicReference<Double> deducted = new AtomicReference<>(0D);
for (String skuId : skuPromotionDetail.keySet()) {
//获取对应商品进行计算
for (CartSkuVO cartSkuVO : skuVOList) {
if (cartSkuVO.getGoodsSku().getId().equals(skuId)) {
Double finalDiscountPrice = discountPrice;
Double finalTotalPrice = totalPrice;
skuVOList.stream().filter(l -> l.getGoodsSku().getId().equals(skuId)).findFirst().ifPresent(cartSkuVO -> {
//sku 优惠金额
Double skuDiscountPrice;
count.getAndDecrement();
count--;
//sku 优惠金额
Double skuDiscountPrice;
//非最后一个商品,则按照比例计算
if (count > 0) {
//商品金额占比
double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4);
//商品优惠金额
skuDiscountPrice = CurrencyUtil.mul(discountPrice, point);
//累加已优惠金额
deducted = CurrencyUtil.add(deducted, skuDiscountPrice);
}
// 如果是最后一个商品 则减去之前优惠的金额来进行计算
else {
skuDiscountPrice = CurrencyUtil.sub(discountPrice, deducted);
}
calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId);
//非最后一个商品,则按照比例计算
if (count.get() > 0) {
//商品金额占比
double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), finalTotalPrice, 4);
//商品优惠金额
skuDiscountPrice = CurrencyUtil.mul(finalDiscountPrice, point);
//累加已优惠金额
deducted.set(CurrencyUtil.add(deducted.get(), skuDiscountPrice));
}
}
// 如果是最后一个商品 则减去之前优惠的金额来进行计算
else {
skuDiscountPrice = CurrencyUtil.sub(finalDiscountPrice, deducted.get());
}
calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId);
});
}
calculateNotEnoughPromotionsPrice(skuVOList, skuPromotionDetail, discountPrice, totalPrice, promotionTypeEnum, activityId);
@@ -186,51 +187,100 @@ public class PromotionPriceUtil {
// 特殊情况处理,如参与多个促销活动,部分商品在其他促销计算后的金额不足以满足与当前参与的促销活动的优惠金额
// 但当前购物车内存在当前当前促销活动的其他商品且剩余金额也满足分摊不足商品的不足金额,则分摊到其他商品上
// 满足当前促销的总优惠金额
if (skuPromotionDetail == null || skuPromotionDetail.isEmpty()) {
if (skuPromotionDetail == null || skuPromotionDetail.size() < 2) {
return;
}
// clone skuPromotionDetail
Map<String, Double> skuPromotionDetailClone = MapUtil.sortByValue(skuPromotionDetail, false);
// 未满足优惠金额的商品
long matchPromotionsZeroCount =
skuVOList.stream().filter(l -> l.getPriceDetailDTO().getFlowPrice() == 0 && skuPromotionDetail.containsKey(l.getGoodsSku().getId())).count();
long matchPromotionsCount = skuVOList.stream().filter(l -> skuPromotionDetail.containsKey(l.getGoodsSku().getId())).count();
skuVOList.stream().filter(l -> l.getPriceDetailDTO().getFlowPrice() == 0 && skuPromotionDetailClone.containsKey(l.getGoodsSku().getId())).count();
// 参与当前促销活动的商品
long matchPromotionsCount = skuVOList.stream().filter(l -> skuPromotionDetailClone.containsKey(l.getGoodsSku().getId())).count();
if (matchPromotionsZeroCount == matchPromotionsCount) {
return;
}
// 获取剩余金额不足优惠金额的商品
List<CartSkuVO> unEnoughSku = skuVOList.stream().filter(k -> {
if (skuPromotionDetail.containsKey(k.getGoodsSku().getId()) && skuPromotionDetail.size() >= 2) {
//商品金额占比
double point = CurrencyUtil.div(k.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4);
//商品优惠金额
Double skuDiscountPrice = CurrencyUtil.mul(discountPrice, point);
return k.getPriceDetailDTO().getCouponPrice() > 0 && skuDiscountPrice > k.getPriceDetailDTO().getCouponPrice();
// 分配到其他商品的优惠金额
AtomicReference<Double> balance = new AtomicReference<>(0D);
StringBuilder lastSkuId = new StringBuilder();
// 计数器
int count = 0;
// 检查是否有不满足优惠金额的商品
filterEnoughSku(skuVOList, skuPromotionDetailClone, discountPrice, totalPrice, balance, lastSkuId, promotionTypeEnum, activityId);
// 循环 优惠金额分摊,直到所有商品都满足优惠金额
while (true) {
// 如果还有剩余金额,则继续分摊
if (balance.get() > 0) {
skuPromotionDetailClone.remove(lastSkuId.toString());
double lastDiscountPrice = CurrencyUtil.sub(discountPrice, skuPromotionDetail.get(lastSkuId.toString()));
double lastTotalPrice = CurrencyUtil.sub(totalPrice, skuPromotionDetail.get(lastSkuId.toString()));
filterEnoughSku(skuVOList, skuPromotionDetailClone, lastDiscountPrice, lastTotalPrice, balance, lastSkuId, promotionTypeEnum, activityId);
} else {
break;
}
return false;
}).collect(Collectors.toList());
if (!unEnoughSku.isEmpty()) {
if (unEnoughSku.size() == skuVOList.size()) {
return;
count++;
// 防止死循环
if (count > skuPromotionDetail.size()) {
break;
}
for (CartSkuVO cartSkuVO : skuVOList) {
if (unEnoughSku.isEmpty()) {
break;
}
if (skuPromotionDetail.containsKey(cartSkuVO.getGoodsSku().getId()) && unEnoughSku.stream().noneMatch(k -> k.getGoodsSku().getId().equals(cartSkuVO.getGoodsSku().getId()))) {
// 商品金额占比
double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4);
// 商品优惠金额
Double skuDiscountPrice = CurrencyUtil.mul(discountPrice, point);
// 商品优惠金额 - 不足优惠金额 = 差额
Double sub = CurrencyUtil.sub(skuDiscountPrice, unEnoughSku.get(0).getPriceDetailDTO().getCouponPrice());
// 分摊到其他商品: 其他商品原优惠金额 + 差额
calculateCartSkuPromotionsPrice(cartSkuVO, sub, promotionTypeEnum, activityId);
// 从不足商品列表中移除
unEnoughSku.remove(0);
}
}
} else {
return;
}
calculateNotEnoughPromotionsPrice(skuVOList, skuPromotionDetail, discountPrice, totalPrice, promotionTypeEnum, activityId);
}
private static void filterEnoughSku(List<CartSkuVO> skuVOList, Map<String, Double> skuPromotionDetail,
Double discountPrice, Double totalPrice,
AtomicReference<Double> balance, StringBuilder lastSkuId,
PromotionTypeEnum promotionTypeEnum, String activityId) {
AtomicInteger count = new AtomicInteger(skuPromotionDetail.size());
AtomicReference<Double> countPrice = new AtomicReference<>(0D);
for (String skuId : skuPromotionDetail.keySet()) {
skuVOList.forEach(l -> {
if (l.getGoodsSku().getId().equals(skuId)) {
count.getAndDecrement();
//商品金额占比
double point = CurrencyUtil.div(l.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4);
//商品优惠金额
Double skuDiscountPrice;
if (count.get() > 0) {
//非最后一个商品,则按照比例计算
skuDiscountPrice = CurrencyUtil.mul(discountPrice, point);
} else {
// 如果是最后一个商品 则减去之前优惠的金额来进行计算
skuDiscountPrice = CurrencyUtil.sub(discountPrice, countPrice.get());
}
if (balance.get() > 0) {
// 分摊到其他商品: 其他商品原优惠金额 + 差额
calculateCartSkuPromotionsPrice(l, balance.get(), promotionTypeEnum, activityId);
}
// 如果商品优惠金额大于商品金额,则取商品金额。差额分摊到其他商品
if (skuDiscountPrice > l.getPriceDetailDTO().getGoodsPrice()) {
balance.set(CurrencyUtil.sub(skuDiscountPrice, l.getPriceDetailDTO().getGoodsPrice()));
lastSkuId.append(skuId);
skuDiscountPrice = l.getPriceDetailDTO().getGoodsPrice();
} else {
balance.set(0D);
}
countPrice.set(CurrencyUtil.add(countPrice.get(), skuDiscountPrice));
}
});
}
}
/**

View File

@@ -638,18 +638,20 @@ public class CartServiceImpl implements CartService {
if (Boolean.FALSE.equals(cartSkuVO.getChecked())) {
continue;
}
//使用优惠券时判断最新的sku价格。与后面渲染一致
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(cartSkuVO.getGoodsSku().getId());
//有促销金额则用促销金额,否则用商品原价
if (cartSkuVO.getPromotionMap() != null && !cartSkuVO.getPromotionMap().isEmpty()) {
if (cartSkuVO.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.PINTUAN.name()) || i.contains(PromotionTypeEnum.SECKILL.name()))) {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getPurchasePrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getPurchasePrice(), cartSkuVO.getNum()));
} else {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
}
} else {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
}
}

View File

@@ -148,6 +148,9 @@ public class Order extends BaseEntity {
@ApiModelProperty(value = "买家订单备注")
private String remark;
@ApiModelProperty(value = "卖家订单备注")
private String sellerRemark;
@ApiModelProperty(value = "订单取消原因")
private String cancelReason;
@@ -240,7 +243,7 @@ public class Order extends BaseEntity {
this.setDeliverStatus(DeliverStatusEnum.UNDELIVERED.name());
this.setTradeSn(tradeDTO.getSn());
this.setRemark(cartVO.getRemark());
this.setFreightPrice(tradeDTO.getPriceDetailDTO().getFreightPrice());
this.setFreightPrice(cartVO.getPriceDetailDTO().getFreightPrice());
//会员收件信息
if (tradeDTO.getMemberAddress() != null && DeliveryMethodEnum.LOGISTICS.name().equals(cartVO.getDeliveryMethod())) {
this.setConsigneeAddressIdPath(tradeDTO.getMemberAddress().getConsigneeAddressIdPath());

View File

@@ -2,6 +2,7 @@ package cn.lili.modules.order.order.entity.dos;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.common.utils.BeanUtil;
import cn.lili.common.utils.SnowFlake;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
@@ -187,4 +188,10 @@ public class OrderItem extends BaseEntity {
this.priceDetail = JSONUtil.toJsonStr(priceDetail);
}
public String getAfterSaleStatus() {
if (!PromotionTypeEnum.isCanAfterSale(this.promotionType)) {
return OrderItemAfterSaleStatusEnum.EXPIRED.name();
}
return afterSaleStatus;
}
}

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.order.order.entity.dos;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.utils.BeanUtil;
@@ -8,6 +9,7 @@ import cn.lili.common.utils.SnowFlake;
import cn.lili.modules.order.order.entity.dto.PriceDetailDTO;
import cn.lili.modules.order.order.entity.enums.FlowTypeEnum;
import cn.lili.modules.order.order.entity.enums.OrderPromotionTypeEnum;
import cn.lili.modules.order.order.entity.enums.ProfitSharingStatusEnum;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.mybatis.BaseIdEntity;
import com.baomidou.mybatisplus.annotation.FieldFill;
@@ -141,6 +143,24 @@ public class StoreFlow extends BaseIdEntity {
@ApiModelProperty(value = "创建时间", hidden = true)
private Date createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "结算时间", hidden = true)
private Date billTime;
@ApiModelProperty(value = "是否全部退款true为全部退款")
private Boolean fullRefund;
/**
* @see ProfitSharingStatusEnum
*/
@ApiModelProperty(value = "分账状态")
private String profitSharingStatus;
@ApiModelProperty(value = "实际分账金额DTO", hidden = true)
private String profitSharing;
public StoreFlow(Order order, OrderItem item, FlowTypeEnum flowTypeEnum) {
//获取订单促销类型,如果为促销订单则获取促销商品并获取结算价
@@ -196,6 +216,10 @@ public class StoreFlow extends BaseIdEntity {
this.setPaymentName(order.getPaymentMethod());
//添加第三方支付流水号
this.setTransactionId(order.getReceivableNo());
//默认结算时间180天
if (flowTypeEnum.equals(FlowTypeEnum.PAY)) {
this.billTime = DateUtil.offsetDay(new Date(), 180);
this.fullRefund = false;
}
}
}

View File

@@ -1,8 +1,9 @@
package cn.lili.modules.order.order.entity.dto;
import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum;
import cn.lili.common.enums.ClientTypeEnum;
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.PayStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderTypeEnum;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -19,77 +20,71 @@ import java.util.Date;
@Data
public class OrderExportDTO {
@ApiModelProperty("订单编号")
private String sn;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "用户名")
private String memberName;
@ApiModelProperty(value = "收件人姓名")
private String consigneeName;
@ApiModelProperty(value = "收件人手机")
private String consigneeMobile;
@ApiModelProperty(value = "收件人地址")
private String consigneeAddressPath;
@ApiModelProperty(value = "详细地址")
private String consigneeDetail;
@ApiModelProperty(value = "支付方式")
private String paymentMethod;
@ApiModelProperty(value = "物流公司名称")
private String logisticsName;
@ApiModelProperty(value = "运费")
private Double freightPrice;
@ApiModelProperty(value = "商品价格")
private Double goodsPrice;
@ApiModelProperty(value = "优惠的金额")
private Double discountPrice;
@ApiModelProperty(value = "总价格")
private Double flowPrice;
@ApiModelProperty(value = "商品名称")
@ApiModelProperty(value = "订单编号")
private String orderSn;
@ApiModelProperty(value = "子订单编号")
private String orderItemSn;
@ApiModelProperty(value = "选购商品")
private String goodsName;
@ApiModelProperty(value = "商品数量")
private Integer num;
@ApiModelProperty(value = "买家订单备注")
@ApiModelProperty(value = "商品ID")
private String goodsId;
@ApiModelProperty(value = "商品单价")
private Double unitPrice;
@ApiModelProperty(value = "订单应付金额")
private Double flowPrice;
@ApiModelProperty(value = "价格内容")
private String priceDetail;
@ApiModelProperty(value = "支付方式")
private String paymentMethod;
@ApiModelProperty(value = "收件人")
private String consigneeName;
@ApiModelProperty(value = "收件人手机")
private String consigneeMobile;
@ApiModelProperty(value = "收件人地址")
private String consigneeAddressPath;
@ApiModelProperty(value = "详细地址")
private String consigneeDetail;
@ApiModelProperty(value = "买家留言")
private String remark;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "订单提交时间")
private Date createTime;
@ApiModelProperty(value = "支付完成时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date paymentTime;
/**
* @see ClientTypeEnum
*/
@ApiModelProperty(value = "来源")
private String clientType;
/**
* @see OrderStatusEnum
*/
@ApiModelProperty(value = "订单状态")
private String orderStatus;
/**
* @see PayStatusEnum
* @see OrderTypeEnum
*/
@ApiModelProperty(value = "付款状态")
private String payStatus;
@ApiModelProperty(value = "订单类型")
private String orderType;
/**
* @see DeliverStatusEnum
* @see OrderItemAfterSaleStatusEnum
*/
@ApiModelProperty(value = "货运状态")
private String deliverStatus;
@ApiModelProperty(value = "是否需要发票")
private Boolean needReceipt;
@ApiModelProperty(value = "店铺名称")
@ApiModelProperty(value = "售后状态")
private String afterSaleStatus;
@ApiModelProperty(value = "取消原因")
private String cancelReason;
@ApiModelProperty(value = "发货时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date logisticsTime;
@ApiModelProperty(value = "完成时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date completeTime;
@ApiModelProperty(value = "店铺")
private String storeName;
}

View File

@@ -0,0 +1,93 @@
package cn.lili.modules.order.order.entity.dto;
import cn.lili.common.enums.ClientTypeEnum;
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 io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 订单导出DTO
* @author Bulbasaur
* @since 2021/6/3 6:36 下午
*
*/
@Data
public class OrderExportDetailDTO {
@ApiModelProperty(value = "主订单编号")
private String orderSn;
@ApiModelProperty(value = "子订单编号")
private String orderItemSn;
@ApiModelProperty(value = "选购商品")
private String goodsName;
@ApiModelProperty(value = "商品数量")
private Integer num;
@ApiModelProperty(value = "商品ID")
private String goodsId;
@ApiModelProperty(value = "商品单价")
private Double unitPrice;
@ApiModelProperty(value = "订单应付金额")
private Double flowPrice;
@ApiModelProperty(value = "运费")
private Double freightPrice;
@ApiModelProperty(value = "优惠总金额")
private Double discountPrice;
@ApiModelProperty(value = "平台")
private Double siteMarketingCost;
@ApiModelProperty(value = "商家优惠")
private Double storeMarketingCost;
@ApiModelProperty(value = "商家改价")
private Double updatePrice;
@ApiModelProperty(value = "支付方式")
private String paymentMethod;
@ApiModelProperty(value = "收件人")
private String consigneeName;
@ApiModelProperty(value = "收件人手机")
private String consigneeMobile;
@ApiModelProperty(value = "")
private String province;
@ApiModelProperty(value = "")
private String city;
@ApiModelProperty(value = "")
private String district;
@ApiModelProperty(value = "街道")
private String street;
@ApiModelProperty(value = "详细地址")
private String consigneeDetail;
@ApiModelProperty(value = "买家留言")
private String remark;
@ApiModelProperty(value = "订单提交时间")
private String createTime;
@ApiModelProperty(value = "支付完成时间")
private String paymentTime;
/**
* @see ClientTypeEnum
*/
@ApiModelProperty(value = "来源")
private String clientType;
/**
* @see OrderStatusEnum
*/
@ApiModelProperty(value = "订单状态")
private String orderStatus;
/**
* @see OrderTypeEnum
*/
@ApiModelProperty(value = "订单类型")
private String orderType;
/**
* @see OrderItemAfterSaleStatusEnum
*/
@ApiModelProperty(value = "售后状态")
private String afterSaleStatus;
@ApiModelProperty(value = "取消原因")
private String cancelReason;
@ApiModelProperty(value = "发货时间")
private String logisticsTime;
@ApiModelProperty(value = "完成时间")
private String completeTime;
@ApiModelProperty(value = "店铺")
private String storeName;
}

View File

@@ -63,9 +63,6 @@ public class OrderSearchParams extends PageVO {
@ApiModelProperty(value = "关键字 商品名称/买家名称/店铺名称")
private String keywords;
@ApiModelProperty(value = "付款方式")
private String paymentType;
/**
* @see OrderTypeEnum
* @see cn.lili.modules.order.order.entity.enums.OrderPromotionTypeEnum
@@ -121,7 +118,11 @@ public class OrderSearchParams extends PageVO {
//关键字查询
if (CharSequenceUtil.isNotEmpty(keywords)) {
wrapper.and(keyWrapper -> keyWrapper.like("o.sn", keywords).or().like("oi.goods_name", keywords));
wrapper.and(keyWrapper -> keyWrapper.like("o.sn", keywords).or()
.like("oi.goods_name", keywords).or()
.like("o.consignee_name", keywords).or()
.like("o.consignee_mobile", keywords).or()
.like("o.store_name", keywords));
}
if (currentUser != null) {
//按卖家查询
@@ -157,9 +158,6 @@ public class OrderSearchParams extends PageVO {
//按商品名称查询
wrapper.like(CharSequenceUtil.isNotEmpty(goodsName), "oi.goods_name", goodsName);
//付款方式
wrapper.like(CharSequenceUtil.isNotEmpty(paymentType), "o.payment_type", paymentType);
//按支付方式
wrapper.eq(CharSequenceUtil.isNotEmpty(paymentMethod), "o.payment_method", paymentMethod);

View File

@@ -0,0 +1,20 @@
package cn.lili.modules.order.order.entity.dto;
import lombok.Data;
/**
* 流水-实际分账DTO
*/
@Data
public class StoreFlowProfitSharingDTO {
//平台获取金额
private Double platformPrice;
//店铺获取金额
private Double storePrice;
//分销员获取金额
private Double distributionPrice;
//合计金额 --剩余金额
private Double price;
//补差金额
private Double subsidies;
}

View File

@@ -12,6 +12,7 @@ public enum DeliverStatusEnum {
* 发货状态
*/
UNDELIVERED("未发货"),
PARTS_DELIVERED("部分发货"),
DELIVERED("已发货"),
RECEIVED("已收货");

View File

@@ -1,5 +1,10 @@
package cn.lili.modules.order.order.entity.enums;
import cn.lili.common.utils.StringUtils;
import java.util.Arrays;
import java.util.EnumSet;
/**
* 订单促销类型枚举
*
@@ -27,6 +32,30 @@ public enum OrderPromotionTypeEnum {
/**
* 砍价订单
*/
KANJIA
KANJIA;
/**
* 判断促销类型是否有效
* @param typeEnumValue
* @return
*/
public static boolean isValid(String typeEnumValue) {
if (StringUtils.isBlank(typeEnumValue)) {
return false;
}
return Arrays.stream(OrderPromotionTypeEnum.values()).anyMatch(c -> c.name().equals(typeEnumValue));
}
/**
* 判断订单类型是否可售后
* GIFT\POINTS\KANJIA 三种促销类型的订单不可进行售后
* @return true 可售后 false 不可售后
*/
public static boolean isCanAfterSale(String orderPromotionType) {
if (!isValid(orderPromotionType)) {
return true;
}
EnumSet<OrderPromotionTypeEnum> noAfterSale = EnumSet.of(OrderPromotionTypeEnum.GIFT, OrderPromotionTypeEnum.POINTS, OrderPromotionTypeEnum.KANJIA);
return !noAfterSale.contains(OrderPromotionTypeEnum.valueOf(orderPromotionType));
}
}

View File

@@ -14,6 +14,7 @@ public enum OrderStatusEnum {
UNPAID("未付款"),
PAID("已付款"),
UNDELIVERED("待发货"),
PARTS_DELIVERED("部分发货"),
DELIVERED("已发货"),
COMPLETED("已完成"),
STAY_PICKED_UP("待自提"),

View File

@@ -0,0 +1,22 @@
package cn.lili.modules.order.order.entity.enums;
/**
* 分账状态 枚举
*/
public enum ProfitSharingStatusEnum {
ORDER_CANCEL("订单取消"),
WAIT_COMPLETE("待订单完成"),
PROCESSING("处理中"),
FINISHED("分账完成");
// FAIL("分账失败"),
// ARTIFICIAL("人工处理");
private String description;
ProfitSharingStatusEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View File

@@ -73,18 +73,18 @@ public class AllowOperation implements Serializable {
//可编辑订单收件人信息=实物订单 && 订单未发货 && 订单未取消 && 订单不是自提
this.editConsignee = order.getOrderType().equals(OrderTypeEnum.NORMAL.name())
&& order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name())
&& (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name()))
&& !status.equals(OrderStatusEnum.CANCELLED.name())
&& !order.getDeliveryMethod().equals(DeliveryMethodEnum.SELF_PICK_UP.name());
//是否允许被发货
this.ship = editConsignee && status.equals(OrderStatusEnum.UNDELIVERED.name());
this.ship = editConsignee && (status.equals(OrderStatusEnum.UNDELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name()));
//是否允许被收货
this.rog = status.equals(OrderStatusEnum.DELIVERED.name());
//是否允许查看物流信息
this.showLogistics = order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) && status.equals(OrderStatusEnum.DELIVERED.name());
this.showLogistics = (order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name())) && (status.equals(OrderStatusEnum.DELIVERED.name()) || status.equals(OrderStatusEnum.PARTS_DELIVERED.name()));
//虚拟订单 或 自提订单可以核销
this.take =
@@ -107,11 +107,8 @@ public class AllowOperation implements Serializable {
}
//取消判定
if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name(),
OrderStatusEnum.STAY_PICKED_UP.name(),
OrderStatusEnum.TAKE.name())) {
if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name())) {
this.cancel = true;
}
//新订单,允许支付
this.pay = status.equals(OrderStatusEnum.UNPAID.name());

View File

@@ -0,0 +1,27 @@
package cn.lili.modules.order.order.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class OrderNumVO {
@ApiModelProperty(value = "未付款订单数量")
private Integer waitPayNum;
@ApiModelProperty(value = "已付款订单数量")
private Integer waitDeliveryNum;
@ApiModelProperty(value = "待发货订单数量")
private Integer waitShipNum;
@ApiModelProperty(value = "部分发货订单数量")
private Integer partsDeliveredNumNum;
@ApiModelProperty(value = "待收货订单数量")
private Integer deliveredNum;
@ApiModelProperty(value = "待核验订单数量")
private Integer waitCheckNum;
@ApiModelProperty(value = "待自提订单数量")
private Integer waitSelfPickNum;
@ApiModelProperty(value = "已完成订单数量")
private Integer finishNum;
@ApiModelProperty(value = "已关闭订单数量")
private Integer closeNum;
}

View File

@@ -4,6 +4,8 @@ import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ClientTypeEnum;
import cn.lili.common.security.sensitive.Sensitive;
import cn.lili.common.security.sensitive.enums.SensitiveStrategy;
import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum;
import cn.lili.modules.order.order.entity.enums.OrderPromotionTypeEnum;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -54,6 +56,9 @@ public class OrderSimpleVO {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date paymentTime;
@ApiModelProperty(value = "用户ID")
private String memberId;
@ApiModelProperty(value = "用户名")
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String memberName;
@@ -148,6 +153,9 @@ public class OrderSimpleVO {
@ApiModelProperty(value = "退款金额")
private String groupRefundPrice;
@ApiModelProperty(value = "卖家订单备注")
private String sellerRemark;
public List<OrderItemVO> getOrderItems() {
if (CharSequenceUtil.isEmpty(groupGoodsId)) {
return new ArrayList<>();
@@ -183,7 +191,11 @@ public class OrderSimpleVO {
orderItemVO.setImage(groupImages.split(",")[i]);
}
if (CharSequenceUtil.isNotEmpty(groupAfterSaleStatus) && groupAfterSaleStatus.split(",").length == groupGoodsId.split(",").length) {
orderItemVO.setAfterSaleStatus(groupAfterSaleStatus.split(",")[i]);
if (!OrderPromotionTypeEnum.isCanAfterSale(this.orderPromotionType)) {
orderItemVO.setAfterSaleStatus(OrderItemAfterSaleStatusEnum.EXPIRED.name());
} else {
orderItemVO.setAfterSaleStatus(groupAfterSaleStatus.split(",")[i]);
}
}
if (CharSequenceUtil.isNotEmpty(groupComplainStatus) && groupComplainStatus.split(",").length == groupGoodsId.split(",").length) {
orderItemVO.setComplainStatus(groupComplainStatus.split(",")[i]);
@@ -211,5 +223,11 @@ public class OrderSimpleVO {
return new AllowOperation(this);
}
public String getGroupAfterSaleStatus() {
// 不可售后的订单类型集合
if (!OrderPromotionTypeEnum.isCanAfterSale(this.orderPromotionType)) {
return OrderItemAfterSaleStatusEnum.EXPIRED.name();
}
return groupAfterSaleStatus;
}
}

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