Compare commits
No commits in common. "master" and "HEAD" have entirely different histories.
|
|
@ -34,4 +34,3 @@ build/
|
|||
.vscode/
|
||||
/logs/
|
||||
/ghy-admin/src/test/
|
||||
ghy-shop/ADDRESS_QUERY_EXAMPLE.md
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
<!-- @author Yifei.Kuang -->
|
||||
# 房间 / 群 / 群员业务需求说明文档
|
||||
|
||||
## 一、项目背景与目标
|
||||
|
||||
- **背景**
|
||||
在现有「师傅 / 商城 / 分销」体系上,引入「房间(圈子)+ 群 + 分销」的新结构,实现多房间、多群、多身份的精细化运营与监管。
|
||||
|
||||
- **目标**
|
||||
- 建立清晰的层级结构:**平台 → 房间 → 群 → 群员/分销**。
|
||||
- 统一分销身份规则,保证订单与佣金归属准确。
|
||||
- 支持群主、房间主、下级分销、运营商等多角色协同。
|
||||
- 为后续房间收费、群收费、群员收费等模式预留空间。
|
||||
|
||||
---
|
||||
|
||||
## 二、核心概念定义
|
||||
|
||||
### 2.1 平台
|
||||
|
||||
- 系统最上层运营主体,负责全局参数(平台扣点、结算规则等)。
|
||||
|
||||
### 2.2 房间(圈子)
|
||||
|
||||
- **定义**:平台下的独立运营空间,一个平台可创建多个房间。
|
||||
- **特点**
|
||||
- 房间名称:限制在 4–6 个字。
|
||||
- 房间标识图片、品牌注解由房间主配置。
|
||||
- 顶部功能栏:默认为「师傅库 / 商城 / 会话厅」,文字可改,总长度有限制但功能不变。
|
||||
- 房间可配置自身的功能权限(是否开放商城、是否允许群选品等)以及扣点、收费策略。
|
||||
- **关系**
|
||||
- 「微社圈」是整体产品名,“切换圈子”即切换不同房间。
|
||||
|
||||
### 2.3 群(分销群)
|
||||
|
||||
- **定义**:隶属于某房间的运营群,每个房间下可以创建多个群。
|
||||
- **建群要求**
|
||||
- 填写城市、区域、主营品类、预期群规模等信息。
|
||||
- 建群申请需由房间主及后台审核(形式类似现有分销申请)。
|
||||
- **职责**
|
||||
- 每个群经营自己的商品与客户。
|
||||
- 群商城中仅群主有选品权限,下级分销不能选品。
|
||||
|
||||
### 2.4 群主
|
||||
|
||||
- 该群的最高级分销者。
|
||||
- 新建群后自动成为本群顶级分销(无上级分销)。
|
||||
- 负责本群的选品、群成员管理、群内规则等。
|
||||
|
||||
### 2.5 群员 / 分销人员
|
||||
|
||||
- 所有群员可根据既有三级分销规则绑定上级关系。
|
||||
- **分销身份规则**
|
||||
- 每个群员在「平台 + 房间 + 群」这一组合下有一个独立分销身份。
|
||||
- 同一微信号在不同房间、不同群中,对系统而言是多个独立身份。
|
||||
- **影响**
|
||||
- 每个身份对应一条独立的分销线。
|
||||
- 订单结算与分账都按所在群对应的分销线进行。
|
||||
|
||||
### 2.6 系统机器人 / 监管号
|
||||
|
||||
- 平台可在任意群中驻入一个“系统机器人”账号:
|
||||
- 拥有类似群主的管理能力及后台监管能力。
|
||||
- 用于监管群运营是否健康、是否违规。
|
||||
|
||||
### 2.7 运营商(按微信号)
|
||||
|
||||
- 以微信号为主体的运营视角。
|
||||
- 同一微信号在多个房间、多个群中有多条分销线,但在运营统计中可统一聚合。
|
||||
|
||||
---
|
||||
|
||||
## 三、分销身份与订单归属规则
|
||||
|
||||
### 3.1 唯一身份线
|
||||
|
||||
- 每条分销身份线由四段组合定义:
|
||||
**平台ID + 房间ID + 群ID + 用户ID**
|
||||
- 每一条组合都被视为“系统中的一个人、一条分销线”。
|
||||
|
||||
### 3.2 多群多房间身份
|
||||
|
||||
- 同一微信号在不同房间/群中创建或加入时:
|
||||
- 系统会为其生成新的分销身份线。
|
||||
- 不同房间、群的身份互不影响。
|
||||
|
||||
### 3.3 分销绑定
|
||||
|
||||
- 在某群中被拉入的新成员:
|
||||
- 按现有三级分销规则建立上下级关系。
|
||||
- 上下级关系限定在本群对应的分销线中。
|
||||
|
||||
### 3.4 订单与分销归属
|
||||
|
||||
- 用户从某个群入口进入商城并下单:
|
||||
- 所有分销与分账都按该群的分销线计算。
|
||||
- 即使服务商在其他群也有身份,本订单仍只归属于当前群对应的身份线。
|
||||
|
||||
---
|
||||
|
||||
## 四、房间与群的前台结构
|
||||
|
||||
### 4.1 房间首页结构
|
||||
|
||||
- 房间头部展示:
|
||||
- 房间头像/标识图;
|
||||
- 品牌名称(4–6 字);
|
||||
- 简短品牌注解。
|
||||
- 顶部功能栏:
|
||||
- 默认入口为:「师傅库 / 商城 / 会话厅」。
|
||||
- 房间主可修改这三个名称(总长度有限制),但功能含义不变。
|
||||
- 顶部入口与底部导航对应,为功能快捷入口。
|
||||
|
||||
### 4.2 师傅库
|
||||
|
||||
- 展示本房间下已入驻的师傅与服务店铺。
|
||||
- 支持按街道、服务品类筛选。
|
||||
- 师傅资料在现有认证信息基础上增加头像和定位信息。
|
||||
- 用户可在师傅详情中:
|
||||
- 直接发起“发布订单”给该师傅;
|
||||
- 或将已有未接订单指派给该师傅。
|
||||
|
||||
### 4.3 商城
|
||||
|
||||
- 房间/群共用一个商城体系:
|
||||
- 房间级定义基础类目与商品池。
|
||||
- 群主在此基础上为本群做选品配置。
|
||||
- 首页默认展示热门服务类目,可左划展示其他商品类目。
|
||||
- 列表支持按价格、评分、销量筛选。
|
||||
- 商品销量按整条上架商品做统一统计。
|
||||
|
||||
### 4.4 会话厅
|
||||
|
||||
- 作为房间的首页使用:
|
||||
- 展示全部群、自建群、已加入群、单聊会话等。
|
||||
- 顶部菜单包含:申请房间、申请建群、搜索群、房间名称设置等入口。
|
||||
- 与群聊页面不同,主要承载群列表与会话入口。
|
||||
|
||||
---
|
||||
|
||||
## 五、订单与发布相关需求(概要)
|
||||
|
||||
### 5.1 师傅端状态汇总
|
||||
|
||||
- 师傅端首页顶部使用黄条展示:
|
||||
- 新订单数量;
|
||||
- 未约订单数量;
|
||||
- 待上门/待发货数量。
|
||||
- 点击不同统计可跳转到对应订单列表。
|
||||
|
||||
### 5.2 发单入口
|
||||
|
||||
- 页面底部设置蓝色「+」按钮作为发布主单入口。
|
||||
- 支持:
|
||||
- 服务类/商品类发单;
|
||||
- 选择到第 4 级类目,可多选;
|
||||
- 将常用配置保存为模板。
|
||||
- 支持代填发布,由专业师傅代填问题内容,客户补充地址、电话后提交。
|
||||
|
||||
### 5.3 保险规则(概念级)
|
||||
|
||||
- 发单时默认勾选“启用保险核查服务费”。
|
||||
- 发单方需选择保险费用由谁承担:
|
||||
- 服务费由发单方支付;
|
||||
- 服务费在订单中扣除。
|
||||
- 到付场景下,已产生的保险费用仍需保留,并可在后续订单中抵扣。
|
||||
|
||||
### 5.4 报价/加价模式(概念级)
|
||||
|
||||
- 在满足一定条件(报价模式或允许商家报价)时,显示“加价/报价”入口。
|
||||
- 支持大厅主单、未约/未排/监控单、已接单主单进行报价。
|
||||
- 客户最多可选择 3 家(特例 4 家)报价,确认后进入支付流程。
|
||||
|
||||
---
|
||||
|
||||
## 六、「我的」与运营统计(概念级)
|
||||
|
||||
- “我的”页面延用现有结构:
|
||||
- 展示分销中心、订单、选品等信息。
|
||||
- 运营统计以微信号为主体:
|
||||
- 汇总展示多房间、多群的运营数据;
|
||||
- 可按房间、群维度下钻查看。
|
||||
|
||||
---
|
||||
|
||||
## 七、商品与服务上架(概念级)
|
||||
|
||||
- 服务上架沿用当前规则。
|
||||
- 商品上架需区分于服务:
|
||||
- 针对库存、规格、展示方式做单独设计;
|
||||
- 与服务上架在前台展示上明显区分。
|
||||
|
||||
---
|
||||
|
||||
## 八、后台与安全监管(概念级)
|
||||
|
||||
- 房间级管理:
|
||||
- 房间创建与审核;
|
||||
- 房间启用/禁用;
|
||||
- 房间收费与扣点配置。
|
||||
- 群级管理:
|
||||
- 建群审核;
|
||||
- 群启用/禁用;
|
||||
- 群主调整;
|
||||
- 系统机器人驻入/移除。
|
||||
- 操作审计:
|
||||
- 记录房间、群创建和重要参数修改,便于运营追溯。
|
||||
|
||||
---
|
||||
|
||||
**文档定位**:业务需求说明(偏产品视角)
|
||||
**用途**:为后续详细设计与开发提供业务背景和规则依据
|
||||
|
||||
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
<!-- @author Yifei.Kuang -->
|
||||
# 房间 / 群 / 分销功能说明文档(开发视角)
|
||||
|
||||
## 一、整体架构
|
||||
|
||||
- 层级结构:
|
||||
- 平台 → 房间(Room)→ 群(Group)→ 群员 / 分销身份(Distributor)
|
||||
- 身份核心:
|
||||
- 每条分销身份由「平台 + 房间 + 群 + 用户」组合唯一确定,用于分销归属和订单结算。
|
||||
|
||||
---
|
||||
|
||||
## 二、房间模块(Room)
|
||||
|
||||
### 2.1 房间功能点
|
||||
|
||||
- 创建房间申请:
|
||||
- 用户提交房间名称、头像、品牌注解等信息。
|
||||
- 后台审核通过后,生成房间,并指定房间主。
|
||||
- 房间信息维护:
|
||||
- 房间主可编辑名称、头像、品牌注解。
|
||||
- 房间顶部功能栏配置:
|
||||
- 默认显示「师傅库 / 商城 / 会话厅」三项。
|
||||
- 房间主可以修改显示文案(总字数受限),功能类型不变。
|
||||
- 房间权限与扣点设置(后台):
|
||||
- 是否开启本房间商城。
|
||||
- 是否允许本房间下的群进行选品。
|
||||
- 房间级扣点与收费策略配置。
|
||||
- 房间切换:
|
||||
- 同一微信可加入多个房间,通过「切换圈子」在不同房间间切换。
|
||||
|
||||
---
|
||||
|
||||
## 三、群模块(Group)
|
||||
|
||||
### 3.1 建群与审核
|
||||
|
||||
- 在某房间内,用户可发起建群申请。
|
||||
- 填写群基础信息(城市、区域、主营类目、群规模等)。
|
||||
- 审核流程:
|
||||
- 房间主初审;
|
||||
- 后台终审;
|
||||
- 通过后创建群,申请人自动成为群主。
|
||||
|
||||
### 3.2 群基本操作
|
||||
|
||||
- 群信息维护:
|
||||
- 群主可编辑群名、介绍、主营描述等。
|
||||
- 群状态控制(后台):
|
||||
- 支持群的启用/禁用;
|
||||
- 禁用后,群内发单、接单、分销等操作关闭或受限。
|
||||
- 群主变更:
|
||||
- 后台可在必要时调整群主(例如违规处理)。
|
||||
|
||||
### 3.3 群监管
|
||||
|
||||
- 支持在任意群中驻入“系统机器人/系统监管号”:
|
||||
- 具备基本群管理与后台数据采集、监控能力。
|
||||
- 用于运营与风险控制。
|
||||
|
||||
---
|
||||
|
||||
## 四、群员 / 分销身份模块
|
||||
|
||||
### 4.1 分销身份规则
|
||||
|
||||
- 每个用户在某「平台 + 房间 + 群」组合下对应一个分销身份。
|
||||
- 同一微信在不同房间/群中拥有多个独立身份:
|
||||
- 每个身份有独立的分销链;
|
||||
- 后台视为多个“人”。
|
||||
|
||||
### 4.2 加入群与分销绑定
|
||||
|
||||
- 成员加入群时:
|
||||
- 若在该群下不存在分销身份,则系统为其创建一条新身份线。
|
||||
- 按既有三级分销规则绑定上级分销。
|
||||
- 上下级分销关系:
|
||||
- 限定在当前群对应的分销线中,不跨群共享。
|
||||
|
||||
### 4.3 角色与权限(概念层)
|
||||
|
||||
- 群主:
|
||||
- 本群最高级分销;
|
||||
- 拥有选品、群配置、成员管理等高权限。
|
||||
- 管理员:
|
||||
- 由群主任命,可拥有部分管理能力(禁言、审核、公告等)。
|
||||
- 普通分销/群员:
|
||||
- 不能在群内进行选品;
|
||||
- 可以拉人建立下级分销关系。
|
||||
|
||||
---
|
||||
|
||||
## 五、会话厅与群聊相关功能
|
||||
|
||||
### 5.1 订单页面导航(左侧)
|
||||
|
||||
- 导航栏包含 4 项:
|
||||
- 资源库;
|
||||
- 商品城;
|
||||
- 会话厅;
|
||||
- 附近圈动态。
|
||||
- 群主可以:
|
||||
- 修改导航名称(总字数有限制);
|
||||
- 单独开放/禁用任一入口;
|
||||
- 与外层房间首页的导航机制保持一致。
|
||||
- 右下角「会话大厅」按钮:
|
||||
- 进入群聊页面(中间页);
|
||||
- 文案可改,群主/平台可控制是否可用。
|
||||
|
||||
### 5.2 资源库(A)
|
||||
|
||||
- 展示服务师傅资料与店铺:
|
||||
- 格式类似现有“师傅圈”,但样式可定制。
|
||||
- 群主可设置仅群内成员可见,或继承房间级展示。
|
||||
- 提供发布入口,支持基于群选品的类目发单。
|
||||
|
||||
### 5.3 商品城(B)
|
||||
|
||||
- 群级商城展示逻辑:
|
||||
- 与整体商城逻辑一致;
|
||||
- 差异在于:群商城的选品由群主独立配置。
|
||||
- 所有人看到的商品、类目等均来源于群主选品结果。
|
||||
- 首页热门类目默认服务类,可左滑出商品类目列表。
|
||||
- 列表筛选按价格、评分、销量进行。
|
||||
- 销量统计按整条商品聚合,不再按规格拆分。
|
||||
|
||||
### 5.4 会话厅(C)
|
||||
|
||||
- 会话厅为“会话/订单流”页面,不是群聊页面。
|
||||
- 默认进入订单页时落在该列:
|
||||
- 支持上拉刷新;
|
||||
- 承载订单、系统相关信息的流式展示。
|
||||
|
||||
### 5.5 附近圈动态(D)
|
||||
|
||||
- 展示商家图文动态的“公共圈子”区域:
|
||||
- 默认名称为“附近圈动态”;
|
||||
- 群主可以改名,但含义不变(商家圈/动态圈)。
|
||||
|
||||
### 5.6 会话大厅图与编辑订单(E)
|
||||
|
||||
- 页面中部展示“会话大厅相关图”:
|
||||
- 使用会话大厅图的一部分放大,作为视觉入口。
|
||||
- 支持进入“编辑订单/发单页”:
|
||||
- 编辑后的订单可发至全群;
|
||||
- 管理员可选择仅发送至单个客户。
|
||||
|
||||
---
|
||||
|
||||
## 六、群聊在线控制与刷新机制
|
||||
|
||||
### 6.1 在线人数限制
|
||||
|
||||
- 当群聊在线人数超过上限(群主/后台可配置):
|
||||
- 群主与管理员不受上线限制,始终在线。
|
||||
- 系统自动将部分成员标记为离线:
|
||||
- 优先踢出近期未发言的成员;
|
||||
- 该过程对成员前端为“默默离线”,不会强制踢出页面。
|
||||
|
||||
### 6.2 重新上线与刷新
|
||||
|
||||
- 被标记为离线的成员:
|
||||
- 在输入框输入内容时,自动重新上线。
|
||||
- 新进群但未发言的成员:
|
||||
- 一段时间后,输入框变为“消息刷新”按钮;
|
||||
- 点击后重新加载消息,并作为新上线行为。
|
||||
- 在已超上线限制的状态下:
|
||||
- 新进入成员默认离线;
|
||||
- 需主动刷新或发言才开始拉取实时消息。
|
||||
|
||||
### 6.3 页面跳转与上线状态
|
||||
|
||||
- 在群聊中点击任何产品、品类跳转其他页面:
|
||||
- 无论是否超限,都视为离线(停止实时连接)。
|
||||
- 从商品/其他页面返回群聊页:
|
||||
- 自动恢复为在线状态。
|
||||
- 在商品页面点击“聊天”:
|
||||
- 跳回群聊,并恢复在线。
|
||||
|
||||
---
|
||||
|
||||
## 七、会话厅顶部类目与选品关系
|
||||
|
||||
### 7.1 顶部类目区
|
||||
|
||||
- 顶部显示:
|
||||
- 「全部」入口;
|
||||
- 若干热门类目。
|
||||
- 热门类目:
|
||||
- 群主从本群的主营/重点品类中选定;
|
||||
- 可对特别重要的类目打红点标记。
|
||||
- 展示方式:
|
||||
- 左右滑动;
|
||||
- 每行最多 8 个类目,最多展示两行。
|
||||
|
||||
### 7.2 选品关系
|
||||
|
||||
- 群主 = 分销顶层:
|
||||
- 群主在本群选品的结果,体现在“全部”和热门类目展示中。
|
||||
- 群员:
|
||||
- 无法直接在群内进行选品;
|
||||
- 仅能浏览、转发商品并参与分销。
|
||||
|
||||
---
|
||||
|
||||
## 八、群内订单与全域订单
|
||||
|
||||
### 8.1 群内订单发起
|
||||
|
||||
- 在群聊页面可以通过以下方式生成群内订单:
|
||||
- 使用专门的“群内订单发单入口”;
|
||||
- 将当前输入框的文字通过快捷键转换为订单(跳转到发单确认页)。
|
||||
- 生成的群内订单:
|
||||
- 在群聊中以订单卡片形式展示;
|
||||
- 提供“接单”按钮。
|
||||
|
||||
### 8.2 与专业版(师傅端)的联动
|
||||
|
||||
- 若群内发单时勾选“保险”等高级字段:
|
||||
- 该订单将按专业版(师傅端)逻辑处理;
|
||||
- 接单、保险、结算等均走专业端流程。
|
||||
- 群主可配置:
|
||||
- 群内订单是否仅允许本群服务人员接单;
|
||||
- 或是否允许进入大厅,被所有专业师傅接单。
|
||||
|
||||
### 8.3 全域订单
|
||||
|
||||
- 群主可将部分群内订单开放到“全域订单池”:
|
||||
- 其他群的服务商也能在“全域订单”中看到这些订单并接单。
|
||||
- 展示限制:
|
||||
- 在群页面或全域订单列表中:
|
||||
- 只有服务商能看到具体金额;
|
||||
- 普通消费者看不到金额。
|
||||
|
||||
### 8.4 订单生命周期(概念)
|
||||
|
||||
- 典型状态流:
|
||||
- 已发单 → 待服务(需要填写上门时间)→ 已提交 → 已付款 → 已完成。
|
||||
- 客户侧操作:
|
||||
- 在“已提交”中查看支付方式并确认;
|
||||
- 确认支付后进入“已付款”。
|
||||
- 服务侧操作:
|
||||
- 服务方确认“已收款,结单”,进入“已完成”。
|
||||
|
||||
### 8.5 纯发单群模式
|
||||
|
||||
- 对于“只发单、不聊天”的群:
|
||||
- 群聊可以不保持实时连接;
|
||||
- 用户仅在手动刷新时看到新订单;
|
||||
- 群形态接近“单纯接单页面”,以降低系统实时成本。
|
||||
|
||||
---
|
||||
|
||||
## 九、「我的」与运营视角
|
||||
|
||||
- “我的”页面沿用现有商城结构:
|
||||
- 包含分销中心、订单、选品等模块。
|
||||
- 统计以微信号为主视角:
|
||||
- 汇总展示该用户在不同房间、群中的运营情况;
|
||||
- 可按房间、群进行明细下钻。
|
||||
|
||||
---
|
||||
|
||||
## 十、后台与审计(功能级)
|
||||
|
||||
- 房间管理:
|
||||
- 房间申请审批;
|
||||
- 房间启用/禁用;
|
||||
- 房间级扣点/收费配置。
|
||||
- 群管理:
|
||||
- 建群申请审核;
|
||||
- 群启用/禁用;
|
||||
- 群主调整;
|
||||
- 系统机器人驻入/移除。
|
||||
- 审计日志:
|
||||
- 创建/修改房间、群;
|
||||
- 修改扣点与重要配置;
|
||||
- 关键权限调整(如群主、管理员变更)等操作均需记录。
|
||||
|
||||
---
|
||||
|
||||
**文档定位**:开发可读的功能说明(不含字段/接口细节)
|
||||
**用途**:指导后续接口设计、表结构设计与前后端实现拆分
|
||||
|
||||
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
# 群成员身份标签与权限管理功能文档
|
||||
|
||||
## 文档说明
|
||||
本文档描述群聊中**群成员身份标签、分类管理、权限控制、消息通知**相关的功能逻辑,不涉及字段/表结构设计,仅面向功能实现。
|
||||
|
||||
---
|
||||
|
||||
## 一、群成员身份标签系统
|
||||
|
||||
### 1.1 身份标签类型
|
||||
系统支持以下身份标签:
|
||||
- **创建者**:群创建者(通常也是群主)
|
||||
- **管理员**:被群主任命的管理员
|
||||
- **服务商家**:提供服务的商家/师傅
|
||||
- **商品商家**:提供商品的商家
|
||||
- **消费者**:普通消费者
|
||||
- **发单人**:经常发单的用户
|
||||
- **接单人**:经常接单的用户
|
||||
- **群成员**:普通群成员
|
||||
- **VIP客户**:VIP客户
|
||||
|
||||
### 1.2 标签分配流程
|
||||
- **入群时自选标签**
|
||||
- 用户加入群时,系统提供两个初始选项:
|
||||
- **非商家**
|
||||
- **商家**
|
||||
- 用户选择一个作为初始身份标识。
|
||||
- **群主/管理员最终确认**
|
||||
- 群主或管理员可以在群成员资料中:
|
||||
- 查看用户自选的标签。
|
||||
- 从系统标签库中选择一个标签,覆盖或确认用户的标签。
|
||||
- 后续可以随时修改成员的标签。
|
||||
- **标签对外展示**
|
||||
- 标签会显示在群成员资料、群成员列表等位置,作为该成员在群内的“对外身份”。
|
||||
|
||||
### 1.3 群成员资料栏位
|
||||
- **群成员资料页包含7个栏位**:
|
||||
- 每个栏位可以放置一个名称/备注。
|
||||
- 这些栏位对所有身份/标签的群员都开放使用。
|
||||
- 用途:方便所有群员查看和识别成员信息。
|
||||
|
||||
---
|
||||
|
||||
## 二、群成员分类/分组系统
|
||||
|
||||
### 2.1 分类概念
|
||||
- **分类定义**
|
||||
- 群内成员可以按照“分类”进行组织,每个分类可以理解为“分组”或“分仓”。
|
||||
- 系统支持创建 **1~7个分类**。
|
||||
- 每个分类可以自定义命名(例如“1类”、“2类”,或自定义别名如“核心商家组”、“普通客户组”等)。
|
||||
- **分类与标签的关系**
|
||||
- 分类与身份标签是**独立的两套体系**:
|
||||
- 一个成员可以拥有一个身份标签(如“服务商家”)。
|
||||
- 同时,该成员可以被放入任意一个分类中(如“1类”)。
|
||||
- 标签不影响分类归属,分类也不影响标签展示。
|
||||
|
||||
### 2.2 分类操作
|
||||
- **成员分类归属**
|
||||
- 任何成员可以被放入任意一个分类中。
|
||||
- 例如:可以将某个“服务商家”放入“1类”,也可以将某个“消费者”放入“1类”。
|
||||
- 成员可以只属于一个分类,也可以不属于任何分类(显示在“未分类”或“全部”中)。
|
||||
- **按分类查看成员**
|
||||
- 群主与管理员可以:
|
||||
- 按分类查看成员列表。
|
||||
- 按标签查看成员列表。
|
||||
- 按“分类+标签”组合筛选查看。
|
||||
- **普通群成员查看**
|
||||
- 普通群成员在群名单中:
|
||||
- 可以按“全部”及“标签身份”查看成员信息。
|
||||
- 不能按分类查看(分类信息对普通成员不展示)。
|
||||
|
||||
### 2.3 分类消息发送
|
||||
- **管理员按分类发消息**
|
||||
- 管理员可以:
|
||||
- 选择向某个分类的所有成员发送消息(其他分类看不到)。
|
||||
- 选择向某个分类中的单个成员发送消息。
|
||||
- 用途:主要用于群管理员进行精细化运营和定向通知。
|
||||
|
||||
---
|
||||
|
||||
## 三、管理员任命与权限体系
|
||||
|
||||
### 3.1 管理员任命
|
||||
- **任命流程**
|
||||
- 群主可以任命任意群成员为管理员。
|
||||
- 任命时可以:
|
||||
- 标注该管理员的“岗位名称”(例如“客服主管”、“运营专员”等)。
|
||||
- 有岗位名称的管理员会在“服务团队消息专列”中显示。
|
||||
- **订单交付权限(独立于管理员)**
|
||||
- 订单交付栏的权限与管理员身份独立:
|
||||
- 群主可以勾选某些成员(不一定是管理员)拥有“订单交付权限”。
|
||||
- 拥有交付权限的人可以在订单交付栏中发布交付信息。
|
||||
|
||||
### 3.2 管理员权限列表
|
||||
群主在任命管理员时,可以勾选该管理员拥有的权限项。管理员在哪个分类中不影响其权限范围。
|
||||
|
||||
#### 权限1:按类发群信息
|
||||
- 管理员可以向指定分类的所有成员发送群消息(其他分类看不到)。
|
||||
|
||||
#### 权限2:禁言与消息可见性控制
|
||||
- **禁言功能(实际为“审核可见”)**
|
||||
- 管理员可以按分类或单个成员设置“禁言”。
|
||||
- 被禁言的成员:
|
||||
- 仍然可以发送消息,但消息默认**仅管理员可见 + 自己可见**。
|
||||
- 管理员可以在后台审核这些消息,勾选后才会展示到群聊页面。
|
||||
- 严格来说这不是传统“禁言”,而是“审核可见”机制。
|
||||
- **消息可见性设置**
|
||||
- 管理员可以:
|
||||
- 按分类设置:该类群员发的消息仅该类成员可见,或仅管理员可见。
|
||||
- 按单个成员设置:该成员发的消息仅管理员可见(在群内形成独立通道,对其他人无感)。
|
||||
- **特殊规则**
|
||||
- 被禁言的成员仍然可以发送商品链接到群(商品链接不受禁言限制)。
|
||||
- 管理员查看消息的规则:
|
||||
- 如果管理员属于某个分类,只能看到该类群员发的信息。
|
||||
- 如果管理员在“全部”分类中,可以看到所有分类群员发的信息。
|
||||
- **管理员@解禁**
|
||||
- 管理员可以@被禁言的成员,临时解禁3小时。
|
||||
|
||||
#### 权限3:禁止接单
|
||||
- 管理员可以按分类或单个成员设置“禁止点击接单键”。
|
||||
- 被禁止的成员无法接取群消息中的订单。
|
||||
|
||||
#### 权限4:禁止发单
|
||||
- 管理员可以按分类或单个成员设置“禁止点击发单键”。
|
||||
- 被禁止的成员无法使用发单功能(包括类目发单)。
|
||||
|
||||
#### 权限5:禁止互加好友
|
||||
- 管理员可以按分类或单个成员设置“禁止互加好友”。
|
||||
- 被禁止的成员:
|
||||
- 别人无法添加他为好友。
|
||||
- 他也无法添加别人为好友。
|
||||
- **特殊规则**
|
||||
- 即使被禁止加好友,双方仍然可以:
|
||||
- 在服务团队聊天中直接对话(通过服务团队入口)。
|
||||
|
||||
#### 权限6:屏蔽群内信息
|
||||
- 管理员可以按分类或单个成员设置“屏蔽群内信息”。
|
||||
- 被屏蔽的成员:
|
||||
- 看不到群内在发的消息。
|
||||
- **但订单仍然可见可接**(屏蔽消息,不屏蔽订单展示)。
|
||||
|
||||
#### 权限7:禁止发圈信息
|
||||
- 管理员可以按分类或单个成员设置“禁止发圈信息”。
|
||||
- 被禁止的成员无法在商家圈/动态圈中发布内容。
|
||||
|
||||
#### 权限8:审核成员入群
|
||||
- 管理员可以审核新成员的入群申请。
|
||||
|
||||
#### 权限9:删除成员与撤回消息
|
||||
- 管理员可以:
|
||||
- 删除群成员(将成员移出群)。
|
||||
- 撤回群成员的消息:
|
||||
- 可以撤回任意成员的一条消息。
|
||||
- 可以彻底删除消息(删除后不可见,不可恢复)。
|
||||
|
||||
#### 权限10:分类分标签发公告
|
||||
- 管理员可以:
|
||||
- 按分类发送公告。
|
||||
- 按标签发送公告。
|
||||
- 按“分类+标签”组合发送公告。
|
||||
|
||||
---
|
||||
|
||||
## 四、群主专属权限
|
||||
|
||||
### 4.1 群名设置
|
||||
- 群主可以修改群名称。
|
||||
|
||||
### 4.2 群转让
|
||||
- 群主可以将群主身份转让给其他成员。
|
||||
|
||||
### 4.3 开通入群申请
|
||||
- 群主可以开启/关闭“入群申请”功能。
|
||||
- 开启后,新成员需要申请才能加入群。
|
||||
|
||||
### 4.4 待接单显示规则(待定)
|
||||
- 群主可以设置“待接单是否仅显示本群的订单”(此功能待定,可能不实现)。
|
||||
|
||||
---
|
||||
|
||||
## 五、群消息通知机制
|
||||
|
||||
### 5.1 消息通知基础规则
|
||||
|
||||
#### 未打开登录页面的通知策略
|
||||
- 用户未打开登录页面(APP未打开)时:
|
||||
- 系统每 **5分钟** 聚合一次新消息,发送一条通知。
|
||||
- 通知内容:显示所有群的新消息汇总(例如“您有X条新消息”)。
|
||||
- 用户打开APP后,不再发送聚合通知。
|
||||
|
||||
#### 群外消息计数
|
||||
- 在群列表页面(群外):
|
||||
- 每个群名旁边显示该群的消息数量角标。
|
||||
- APP端统一使用角标显示。
|
||||
|
||||
### 5.2 群消息免打扰设置
|
||||
|
||||
#### 免打扰选项
|
||||
每个群成员可以为自己设置该群的“免打扰”策略,选项包括:
|
||||
- **仅1小时1条**:1小时内只通知一次。
|
||||
- **仅1天1条**:1天内只通知一次。
|
||||
- **仅3天1条**:3天内只通知一次。
|
||||
- **仅10天1条**:10天内只通知一次。
|
||||
|
||||
#### 免打扰与通知队列的关系
|
||||
- 系统维护多个通知队列:
|
||||
- **5分钟通知队列**:默认队列,每5分钟通知一次。
|
||||
- **1小时通知队列**:设置了“1小时1条”的用户进入此队列。
|
||||
- **1天通知队列**:设置了“1天1条”的用户进入此队列。
|
||||
- **3天通知队列**:设置了“3天1条”的用户进入此队列。
|
||||
- **10天通知队列**:设置了“10天1条”的用户进入此队列。
|
||||
- 用户设置免打扰后:
|
||||
- 从“5分钟队列”中剔除,进入对应的免打扰队列。
|
||||
- 例如:设置“1小时1条”后,每1小时聚合通知一次(而不是每5分钟)。
|
||||
|
||||
#### 通知时间窗口
|
||||
- **5分钟队列与1小时队列**:
|
||||
- 晚上7点后不通知。
|
||||
- 早上8点后才开始通知。
|
||||
- **1天队列、3天队列、10天队列**:
|
||||
- 通知时间点可以设定在12点,或错开时间点(例如1天队列12点,3天队列14点,10天队列16点),以减少大量通知同时推送。
|
||||
|
||||
#### 用户类型与免打扰选择
|
||||
- **经营者(商家/师傅)**:
|
||||
- 更多选择“5分钟通知”或“1小时通知”(需要及时响应)。
|
||||
- **客户(消费者)**:
|
||||
- 更多选择“3天通知”或“10天通知”(降低打扰频率)。
|
||||
|
||||
### 5.3 @通知机制
|
||||
|
||||
#### @通知规则
|
||||
- **无论用户是否设置免打扰**,被@时:
|
||||
- 使用**手机上方消息栏**直接通知(不进入队列等待)。
|
||||
- 通知带有声音提醒(用户可以自定义声音)。
|
||||
- 不等待5分钟/1小时等时间窗口,立即推送。
|
||||
|
||||
#### @通知展示位置
|
||||
- @通知显示在手机顶部通知栏(系统级通知)。
|
||||
|
||||
---
|
||||
|
||||
## 六、消息通知总结
|
||||
|
||||
### 6.1 通知队列体系
|
||||
系统维护以下通知队列:
|
||||
1. **5分钟通知队列**(默认)
|
||||
- 时间窗口:早上8点 ~ 晚上7点。
|
||||
- 每5分钟聚合通知一次。
|
||||
2. **1小时通知队列**
|
||||
- 时间窗口:早上8点 ~ 晚上7点。
|
||||
- 每1小时聚合通知一次。
|
||||
3. **1天通知队列**
|
||||
- 在指定时间点(例如12点)通知一次。
|
||||
4. **3天通知队列**
|
||||
- 在指定时间点(例如14点)通知一次。
|
||||
5. **10天通知队列**
|
||||
- 在指定时间点(例如16点)通知一次。
|
||||
|
||||
### 6.2 特殊通知(不受队列限制)
|
||||
- **@通知**:
|
||||
- 立即推送,使用手机顶部通知栏。
|
||||
- 带声音提醒。
|
||||
- 不受免打扰设置影响。
|
||||
|
||||
### 6.3 通知聚合规则
|
||||
- **未打开APP时**:
|
||||
- 所有群的新消息统一聚合为一条通知(例如“您有X条新消息”)。
|
||||
- 按用户所属队列的时间窗口进行通知。
|
||||
- **打开APP后**:
|
||||
- 不再发送聚合通知。
|
||||
- 用户可以在APP内查看各群的具体消息。
|
||||
|
||||
---
|
||||
|
||||
## 七、功能总结
|
||||
|
||||
### 7.1 身份标签与分类
|
||||
- 9种身份标签,入群时自选,群主/管理员可最终确认和修改。
|
||||
- 支持1~7个分类,分类与标签独立,成员可被放入任意分类。
|
||||
|
||||
### 7.2 管理员权限
|
||||
- 10个权限点,群主可灵活配置每个管理员的权限。
|
||||
- 订单交付权限独立于管理员身份。
|
||||
|
||||
### 7.3 消息通知
|
||||
- 5个通知队列(5分钟/1小时/1天/3天/10天),用户可自由选择。
|
||||
- @通知立即推送,不受免打扰影响。
|
||||
- 时间窗口控制,避免夜间打扰。
|
||||
|
||||
---
|
||||
|
||||
## 八、待定事项
|
||||
- 待接单是否仅显示本群的订单(功能待定)。
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.0
|
||||
**最后更新**:2024年
|
||||
|
||||
|
|
@ -143,10 +143,6 @@
|
|||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ghy</groupId>
|
||||
<artifactId>ghy-shop</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import com.ghy.common.adapay.model.PayCallback;
|
|||
import com.ghy.common.adapay.model.PaymentDTO;
|
||||
import com.ghy.common.enums.PayStatus;
|
||||
import com.ghy.order.domain.OrderAddSubtract;
|
||||
import com.ghy.order.domain.OrderMaster;
|
||||
import com.ghy.order.mapper.OrderMasterMapper;
|
||||
import com.ghy.order.service.IOrderAddSubtractService;
|
||||
import com.ghy.order.service.OrderMasterService;
|
||||
import com.ghy.payment.domain.FinancialChangeRecord;
|
||||
|
|
@ -41,7 +39,6 @@ public class PayCallbackService implements CallBackService {
|
|||
|
||||
@Resource
|
||||
OrderMasterService orderMasterService;
|
||||
|
||||
@Resource
|
||||
FinancialMasterService financialMasterService;
|
||||
@Resource
|
||||
|
|
@ -100,31 +97,6 @@ public class PayCallbackService implements CallBackService {
|
|||
financialMasterService.updatePay(relationId, paymentId, PayStatus.PAID.getCode());
|
||||
log.info("主财务单[{}]支付成功", relationId);
|
||||
|
||||
// 检查是否为商品订单且有关联的服务订单,需要同步更新服务订单财务主单支付状态
|
||||
try {
|
||||
OrderMaster orderMaster = orderMasterService.selectById(financialMaster.getOrderMasterId());
|
||||
if (orderMaster != null && orderMaster.getOrderType() == 1 && orderMaster.getHasServiceOrder() == 1) {
|
||||
// 查询关联的服务订单
|
||||
OrderMaster serviceOrder = orderMasterService.selectByGoodsOrderMasterId(orderMaster.getId());
|
||||
if (serviceOrder != null) {
|
||||
// 查询服务订单对应的财务主单
|
||||
FinancialMaster serviceFinancialMaster = financialMasterService.selectByOrderMasterId(serviceOrder.getId());
|
||||
if (serviceFinancialMaster != null && !PayStatus.PAID.getCode().equals(serviceFinancialMaster.getPayStatus())) {
|
||||
// 更新服务订单财务主单支付状态
|
||||
financialMasterService.updatePay(serviceFinancialMaster.getId(), paymentId, PayStatus.PAID.getCode());
|
||||
// 更新服务订单支付状态
|
||||
orderMasterService.updatePayStatus(serviceOrder.getId(), PayStatus.PAID.getCode());
|
||||
log.info("商品订单[{}]关联的服务订单[{}]财务主单[{}]支付状态已同步更新为已支付",
|
||||
orderMaster.getId(), serviceOrder.getId(), serviceFinancialMaster.getId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("同步更新商品订单关联服务订单支付状态时发生异常,商品订单ID: {}, 异常信息: {}",
|
||||
financialMaster.getOrderMasterId(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
} else if (PaymentRelation.FINANCIAL_CHANGE.equals(relation.getRelationIdType())) {
|
||||
|
||||
// 更新加价单的支付信息
|
||||
|
|
@ -134,14 +106,14 @@ public class PayCallbackService implements CallBackService {
|
|||
FinancialDetail fd = financialDetailService.selectByOrderDetailId(fc.getOrderDetailId());
|
||||
FinancialDetail fd2Update = new FinancialDetail();
|
||||
fd2Update.setId(fd.getId());
|
||||
fd2Update.setPayMoney(fd.getPayMoney());
|
||||
fd2Update.setPayMoney(fd.getPayMoney().add(fc.getChangeMoney()));
|
||||
fd2Update.setPayStatus(PayStatus.PAID.getCode());
|
||||
financialDetailService.updateFinancialDetail(fd2Update);
|
||||
// 修改主单的payMoney
|
||||
FinancialMaster financialMaster = financialMasterService.selectById(fd.getFinancialMasterId());
|
||||
FinancialMaster financialMaster2Update = new FinancialMaster();
|
||||
financialMaster2Update.setId(financialMaster.getId());
|
||||
financialMaster2Update.setPayMoney(financialMaster.getPayMoney());
|
||||
financialMaster2Update.setPayMoney(financialMaster.getPayMoney().add(fc.getChangeMoney()));
|
||||
financialMaster2Update.setPayStatus(PayStatus.PAID.getCode());
|
||||
financialMasterService.updateFinancialMaster(financialMaster2Update);
|
||||
// 更新主订单的支付信息
|
||||
|
|
@ -156,14 +128,14 @@ public class PayCallbackService implements CallBackService {
|
|||
FinancialDetail fd = financialDetailService.selectByOrderDetailId(orderAdd.getOrderDetailId());
|
||||
FinancialDetail fd2Update = new FinancialDetail();
|
||||
fd2Update.setId(fd.getId());
|
||||
fd2Update.setPayMoney(fd.getPayMoney());
|
||||
fd2Update.setPayMoney(fd.getPayMoney().add(orderAdd.getMoney()));
|
||||
fd2Update.setPayStatus(PayStatus.PAID.getCode());
|
||||
financialDetailService.updateFinancialDetail(fd2Update);
|
||||
// 修改主单的payMoney
|
||||
FinancialMaster financialMaster = financialMasterService.selectById(fd.getFinancialMasterId());
|
||||
FinancialMaster financialMaster2Update = new FinancialMaster();
|
||||
financialMaster2Update.setId(financialMaster.getId());
|
||||
financialMaster2Update.setPayMoney(financialMaster.getPayMoney());
|
||||
financialMaster2Update.setPayMoney(financialMaster.getPayMoney().add(orderAdd.getMoney()));
|
||||
financialMaster2Update.setPayStatus(PayStatus.PAID.getCode());
|
||||
financialMasterService.updateFinancialMaster(financialMaster2Update);
|
||||
log.info("订单追加[{}]支付成功", relationId);
|
||||
|
|
|
|||
|
|
@ -1,576 +0,0 @@
|
|||
package com.ghy.web.controller;
|
||||
|
||||
import com.ghy.common.core.controller.BaseController;
|
||||
import com.ghy.shop.domain.Shop;
|
||||
import com.ghy.shop.domain.ShopDistanceQuery;
|
||||
import com.ghy.shop.service.ShopService;
|
||||
import com.ghy.common.core.domain.AjaxResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import com.ghy.common.utils.LocationUtils;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* 店铺管理接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/shop")
|
||||
public class ShopController extends BaseController {
|
||||
@Autowired
|
||||
private ShopService shopService;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@PostMapping("/add")
|
||||
public AjaxResult addShop(@RequestBody Shop shop) {
|
||||
// 如果没有经纬度,尝试通过地址解析获取
|
||||
if ((shop.getLatitude() == null || shop.getLongitude() == null) &&
|
||||
shop.getAddress() != null && !shop.getAddress().trim().isEmpty()) {
|
||||
try {
|
||||
Map<String, Double> coordinates = getCoordinatesByAddress(shop);
|
||||
if (coordinates != null) {
|
||||
shop.setLatitude(coordinates.get("latitude"));
|
||||
shop.setLongitude(coordinates.get("longitude"));
|
||||
logger.info("为店铺[{}]自动获取到坐标: 经度={}, 纬度={}",
|
||||
shop.getShopName(), shop.getLongitude(), shop.getLatitude());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取地址经纬度失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
int result = shopService.addShop(shop);
|
||||
if (result > 0) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("shopId", shop.getShopId());
|
||||
response.put("shopName", shop.getShopName());
|
||||
response.put("latitude", shop.getLatitude());
|
||||
response.put("longitude", shop.getLongitude());
|
||||
response.put("address", shop.getAddress());
|
||||
return AjaxResult.success("店铺创建成功", response);
|
||||
} else {
|
||||
return AjaxResult.error("店铺创建失败");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public AjaxResult listShops() {
|
||||
List<Shop> list = shopService.listShops();
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getShop(@PathVariable Long id) {
|
||||
Shop shop = shopService.getShop(id);
|
||||
return shop != null ? AjaxResult.success(shop) : AjaxResult.error("未找到店铺");
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
public AjaxResult updateShop(@RequestBody Shop shop) {
|
||||
// 如果没有经纬度但有地址,尝试通过地址解析获取经纬度
|
||||
if ((shop.getLatitude() == null || shop.getLongitude() == null) &&
|
||||
shop.getAddress() != null && !shop.getAddress().trim().isEmpty()) {
|
||||
try {
|
||||
Map<String, Double> coordinates = getCoordinatesByAddress(shop);
|
||||
if (coordinates != null) {
|
||||
shop.setLatitude(coordinates.get("latitude"));
|
||||
shop.setLongitude(coordinates.get("longitude"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("更新店铺时获取地址经纬度失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
return toAjax(shopService.updateShop(shop));
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public AjaxResult deleteShop(@PathVariable Long id) {
|
||||
return toAjax(shopService.deleteShop(id));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/location")
|
||||
public AjaxResult getShopLocation(@PathVariable Long id) {
|
||||
Shop shop = shopService.getShop(id);
|
||||
if (shop == null) {
|
||||
return AjaxResult.error("未找到店铺");
|
||||
}
|
||||
Map<String, Object> location = new HashMap<>();
|
||||
location.put("address", shop.getAddress());
|
||||
location.put("latitude", shop.getLatitude());
|
||||
location.put("longitude", shop.getLongitude());
|
||||
location.put("provinceName", shop.getProvinceName());
|
||||
location.put("cityName", shop.getCityName());
|
||||
location.put("countryName", shop.getCountryName());
|
||||
location.put("streetName", shop.getStreetName());
|
||||
return AjaxResult.success(location);
|
||||
}
|
||||
|
||||
// @GetMapping("/getCurrentLocation")
|
||||
// public AjaxResult getCurrentLocation() {
|
||||
// try {
|
||||
// // 直接调用百度地图API获取当前位置信息
|
||||
// String url = "/tool/baidu/getLocation";
|
||||
// return restTemplate.getForObject(url, AjaxResult.class);
|
||||
// } catch (Exception e) {
|
||||
// return AjaxResult.error("获取当前位置失败:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
|
||||
@GetMapping("/worker/{workerId}")
|
||||
public AjaxResult getShopsByWorkerId(@PathVariable Long workerId) {
|
||||
List<Shop> shops = shopService.getShopsByWorkerId(workerId);
|
||||
return AjaxResult.success(shops);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 通过地址获取经纬度坐标
|
||||
// */
|
||||
// @PostMapping("/getCoordinates")
|
||||
// public AjaxResult getCoordinatesByAddress(@RequestBody Shop shop) {
|
||||
// try {
|
||||
// Map<String, Double> coordinates = getCoordinatesByAddress(shop);
|
||||
// if (coordinates != null) {
|
||||
// return AjaxResult.success("获取坐标成功", coordinates);
|
||||
// } else {
|
||||
// return AjaxResult.error("无法获取该地址的坐标信息");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// logger.error("获取坐标失败: {}", e.getMessage(), e);
|
||||
// return AjaxResult.error("获取坐标失败: " + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 通过经纬度逆解析获取地址信息并保存到店铺
|
||||
*/
|
||||
@PostMapping("/saveShopByLocation")
|
||||
public AjaxResult saveShopByLocation(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String location = params.get("location").toString(); // 格式: "经度,纬度"
|
||||
|
||||
// 调用百度地图逆解析API
|
||||
String url = "/tool/baidu/getLocation";
|
||||
Map<String, String> requestBody = new HashMap<>();
|
||||
requestBody.put("location", location);
|
||||
|
||||
// 这里应该调用百度接口获取地址信息
|
||||
// AjaxResult locationResult = restTemplate.postForObject(url, requestBody, AjaxResult.class);
|
||||
|
||||
// 解析经纬度
|
||||
String[] coordinates = location.split(",");
|
||||
if (coordinates.length != 2) {
|
||||
return AjaxResult.error("location格式错误,应为:经度,纬度");
|
||||
}
|
||||
|
||||
Double longitude = Double.parseDouble(coordinates[0]);
|
||||
Double latitude = Double.parseDouble(coordinates[1]);
|
||||
|
||||
// 创建店铺对象
|
||||
Shop shop = new Shop();
|
||||
shop.setLongitude(longitude);
|
||||
shop.setLatitude(latitude);
|
||||
|
||||
// 设置其他信息
|
||||
if (params.containsKey("shopName")) {
|
||||
shop.setShopName(params.get("shopName").toString());
|
||||
}
|
||||
if (params.containsKey("workerId")) {
|
||||
shop.setWorkerId(Long.valueOf(params.get("workerId").toString()));
|
||||
}
|
||||
if (params.containsKey("phone")) {
|
||||
shop.setPhone(params.get("phone").toString());
|
||||
}
|
||||
|
||||
// 保存店铺
|
||||
int result = shopService.addShop(shop);
|
||||
if (result > 0) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("shopId", shop.getShopId());
|
||||
response.put("longitude", longitude);
|
||||
response.put("latitude", latitude);
|
||||
response.put("location", location);
|
||||
return AjaxResult.success("店铺创建成功", response);
|
||||
} else {
|
||||
return AjaxResult.error("店铺创建失败");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("通过位置创建店铺失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("创建失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部方法:通过地址解析获取经纬度
|
||||
*/
|
||||
private Map<String, Double> getCoordinatesByAddress(Shop shop) {
|
||||
try {
|
||||
// 构建完整地址
|
||||
StringBuilder fullAddress = new StringBuilder();
|
||||
if (shop.getProvinceName() != null) fullAddress.append(shop.getProvinceName());
|
||||
if (shop.getCityName() != null) fullAddress.append(shop.getCityName());
|
||||
if (shop.getCountryName() != null) fullAddress.append(shop.getCountryName());
|
||||
if (shop.getStreetName() != null) fullAddress.append(shop.getStreetName());
|
||||
if (shop.getAddress() != null) fullAddress.append(shop.getAddress());
|
||||
|
||||
String address = fullAddress.toString();
|
||||
if (address.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("开始解析地址获取坐标: {}", address);
|
||||
|
||||
// TODO: 这里应该调用真实的地图API
|
||||
// 示例:百度地图正向地理编码API
|
||||
// String url = "https://api.map.baidu.com/geocoding/v3/?address=" +
|
||||
// java.net.URLEncoder.encode(address, "UTF-8") +
|
||||
// "&output=json&ak=YOUR_BAIDU_API_KEY";
|
||||
//
|
||||
// String result = restTemplate.getForObject(url, String.class);
|
||||
// JSONObject jsonResult = JSONObject.parseObject(result);
|
||||
//
|
||||
// if ("0".equals(jsonResult.getString("status"))) {
|
||||
// JSONObject location = jsonResult.getJSONObject("result").getJSONObject("location");
|
||||
// Double lng = location.getDouble("lng");
|
||||
// Double lat = location.getDouble("lat");
|
||||
//
|
||||
// Map<String, Double> coordinates = new HashMap<>();
|
||||
// coordinates.put("longitude", lng);
|
||||
// coordinates.put("latitude", lat);
|
||||
// return coordinates;
|
||||
// }
|
||||
|
||||
// 临时返回示例坐标(实际使用时请替换为真实的API调用)
|
||||
Map<String, Double> coordinates = new HashMap<>();
|
||||
coordinates.put("latitude", 39.915 + Math.random() * 0.1); // 示例纬度
|
||||
coordinates.put("longitude", 116.404 + Math.random() * 0.1); // 示例经度
|
||||
|
||||
logger.info("地址解析成功: {} -> 经度={}, 纬度={}",
|
||||
address, coordinates.get("longitude"), coordinates.get("latitude"));
|
||||
|
||||
return coordinates;
|
||||
} catch (Exception e) {
|
||||
logger.error("地址解析失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动设置店铺经纬度
|
||||
*/
|
||||
@PostMapping("/setCoordinates")
|
||||
public AjaxResult setCoordinates(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Long shopId = Long.valueOf(params.get("shopId").toString());
|
||||
Double latitude = Double.valueOf(params.get("latitude").toString());
|
||||
Double longitude = Double.valueOf(params.get("longitude").toString());
|
||||
|
||||
Shop shop = new Shop();
|
||||
shop.setShopId(shopId);
|
||||
shop.setLatitude(latitude);
|
||||
shop.setLongitude(longitude);
|
||||
|
||||
int result = shopService.updateShop(shop);
|
||||
if (result > 0) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("shopId", shopId);
|
||||
response.put("latitude", latitude);
|
||||
response.put("longitude", longitude);
|
||||
return AjaxResult.success("坐标设置成功", response);
|
||||
} else {
|
||||
return AjaxResult.error("坐标设置失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("设置坐标失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("设置坐标失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询店铺(包含经纬度信息)
|
||||
*/
|
||||
@PostMapping("/listWithLocation")
|
||||
public AjaxResult listShopsWithLocation(@RequestBody(required = false) Map<String, Object> params) {
|
||||
try {
|
||||
List<Shop> shops = shopService.listShops();
|
||||
|
||||
// 可以根据参数进行筛选
|
||||
if (params != null) {
|
||||
// 按城市筛选
|
||||
if (params.containsKey("cityId")) {
|
||||
Long cityId = Long.valueOf(params.get("cityId").toString());
|
||||
shops = shops.stream()
|
||||
.filter(shop -> shop.getCityId() != null && shop.getCityId().equals(cityId))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
// 按师傅筛选
|
||||
if (params.containsKey("workerId")) {
|
||||
Long workerId = Long.valueOf(params.get("workerId").toString());
|
||||
shops = shops.stream()
|
||||
.filter(shop -> shop.getWorkerId() != null && shop.getWorkerId().equals(workerId))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
// 只返回有坐标的店铺
|
||||
if (params.containsKey("hasLocation") && Boolean.valueOf(params.get("hasLocation").toString())) {
|
||||
shops = shops.stream()
|
||||
.filter(shop -> shop.getLatitude() != null && shop.getLongitude() != null)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
return AjaxResult.success(shops);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询店铺列表失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询附近的店铺
|
||||
*/
|
||||
@PostMapping("/nearby")
|
||||
public AjaxResult getNearbyShops(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Double latitude = Double.valueOf(params.get("latitude").toString());
|
||||
Double longitude = Double.valueOf(params.get("longitude").toString());
|
||||
Double radiusKm = params.containsKey("radiusKm") ?
|
||||
Double.valueOf(params.get("radiusKm").toString()) : 5.0; // 默认5公里
|
||||
|
||||
// 验证坐标有效性
|
||||
if (!LocationUtils.isValidCoordinate(latitude, longitude)) {
|
||||
return AjaxResult.error("无效的坐标参数");
|
||||
}
|
||||
|
||||
// 获取所有店铺
|
||||
List<Shop> allShops = shopService.listShops();
|
||||
|
||||
// 筛选出有坐标的店铺并计算距离
|
||||
List<Map<String, Object>> nearbyShops = allShops.stream()
|
||||
.filter(shop -> LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude()))
|
||||
.map(shop -> {
|
||||
double distance = LocationUtils.getDistanceInKilometers(
|
||||
latitude, longitude, shop.getLatitude(), shop.getLongitude());
|
||||
|
||||
Map<String, Object> shopWithDistance = new HashMap<>();
|
||||
shopWithDistance.put("shop", shop);
|
||||
shopWithDistance.put("distance", distance);
|
||||
shopWithDistance.put("distanceText", LocationUtils.formatDistance(distance * 1000));
|
||||
|
||||
// 计算方向
|
||||
double bearing = LocationUtils.getBearing(latitude, longitude,
|
||||
shop.getLatitude(), shop.getLongitude());
|
||||
shopWithDistance.put("direction", LocationUtils.getDirectionDescription(bearing));
|
||||
shopWithDistance.put("bearing", bearing);
|
||||
|
||||
return shopWithDistance;
|
||||
})
|
||||
.filter(shopMap -> (Double) shopMap.get("distance") <= radiusKm)
|
||||
.sorted(Comparator.comparingDouble(shopMap -> (Double) shopMap.get("distance")))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("centerLatitude", latitude);
|
||||
result.put("centerLongitude", longitude);
|
||||
result.put("radiusKm", radiusKm);
|
||||
result.put("totalCount", nearbyShops.size());
|
||||
result.put("shops", nearbyShops);
|
||||
|
||||
return AjaxResult.success("查询成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询附近店铺失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个店铺之间的距离
|
||||
*/
|
||||
@PostMapping("/distance")
|
||||
public AjaxResult getShopsDistance(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Long shopId1 = Long.valueOf(params.get("shopId1").toString());
|
||||
Long shopId2 = Long.valueOf(params.get("shopId2").toString());
|
||||
|
||||
Shop shop1 = shopService.getShop(shopId1);
|
||||
Shop shop2 = shopService.getShop(shopId2);
|
||||
|
||||
if (shop1 == null || shop2 == null) {
|
||||
return AjaxResult.error("店铺不存在");
|
||||
}
|
||||
|
||||
if (!LocationUtils.isValidCoordinate(shop1.getLatitude(), shop1.getLongitude()) ||
|
||||
!LocationUtils.isValidCoordinate(shop2.getLatitude(), shop2.getLongitude())) {
|
||||
return AjaxResult.error("店铺坐标信息不完整");
|
||||
}
|
||||
|
||||
double distanceKm = LocationUtils.getDistanceInKilometers(
|
||||
shop1.getLatitude(), shop1.getLongitude(),
|
||||
shop2.getLatitude(), shop2.getLongitude());
|
||||
|
||||
double bearing = LocationUtils.getBearing(
|
||||
shop1.getLatitude(), shop1.getLongitude(),
|
||||
shop2.getLatitude(), shop2.getLongitude());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("shop1", shop1);
|
||||
result.put("shop2", shop2);
|
||||
result.put("distanceKm", distanceKm);
|
||||
result.put("distanceText", LocationUtils.formatDistance(distanceKm * 1000));
|
||||
result.put("direction", LocationUtils.getDirectionDescription(bearing));
|
||||
result.put("bearing", bearing);
|
||||
|
||||
return AjaxResult.success("计算成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("计算店铺距离失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("计算失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过店铺ID查询详细信息并计算距离
|
||||
* 最终效果:
|
||||
* 1. 传入的详细地址 → 转换为经纬度
|
||||
* 2. 店铺有经纬度 → 直接使用
|
||||
* 3. 店铺没有经纬度 → 通过详细地址生成经纬度
|
||||
* 4. 最终通过经纬度计算距离 → 放在店铺的距离字段上
|
||||
*
|
||||
* @param query 距离查询请求实体
|
||||
* @return 包含距离信息的完整店铺详情
|
||||
*/
|
||||
@PostMapping("/getShopDetailWithDistance")
|
||||
public AjaxResult getShopDetailWithDistance(@RequestBody ShopDistanceQuery query) {
|
||||
try {
|
||||
// 验证参数
|
||||
if (query.getShopId() == null) {
|
||||
return AjaxResult.error("店铺ID不能为空");
|
||||
}
|
||||
|
||||
// 验证是否有足够的信息来计算距离或获取位置
|
||||
if (!query.hasEnoughInfo()) {
|
||||
return AjaxResult.error("请提供经纬度或地址信息");
|
||||
}
|
||||
|
||||
// 如果提供了经纬度,验证其有效性
|
||||
if (query.hasCoordinateInfo() && !LocationUtils.isValidCoordinate(query.getLatitude(), query.getLongitude())) {
|
||||
return AjaxResult.error("无效的坐标参数");
|
||||
}
|
||||
|
||||
// 验证地址参数(如果提供了地址信息,则验证其完整性)
|
||||
if (query.hasAddressInfo()) {
|
||||
String fullAddress = query.getFullAddress();
|
||||
if (fullAddress.trim().length() < 5) {
|
||||
return AjaxResult.error("地址信息不完整,请提供更详细的地址");
|
||||
}
|
||||
logger.info("使用传入的地址信息: {}", fullAddress);
|
||||
}
|
||||
|
||||
// 调用服务层方法获取店铺详情和距离
|
||||
Shop shop = shopService.getShopWithDistance(query);
|
||||
|
||||
if (shop == null) {
|
||||
return AjaxResult.error("未找到店铺");
|
||||
}
|
||||
|
||||
// 检查距离是否有效
|
||||
if (shop.getDistance() == null || shop.getDistance().equals("无法计算距离")) {
|
||||
if (query.hasCoordinateInfo()) {
|
||||
return AjaxResult.error("无法计算店铺距离,请检查店铺地址信息或提供更详细的地址参数");
|
||||
} else {
|
||||
// 如果没有经纬度,只获取位置信息
|
||||
logger.info("仅获取店铺位置信息,无法计算距离");
|
||||
}
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("shopId", shop.getShopId());
|
||||
result.put("shopName", shop.getShopName());
|
||||
result.put("imageUrl", shop.getImageUrl());
|
||||
result.put("workerId", shop.getWorkerId());
|
||||
result.put("provinceId", shop.getProvinceId());
|
||||
result.put("provinceName", shop.getProvinceName());
|
||||
result.put("cityId", shop.getCityId());
|
||||
result.put("cityName", shop.getCityName());
|
||||
result.put("countryId", shop.getCountryId());
|
||||
result.put("countryName", shop.getCountryName());
|
||||
result.put("streetId", shop.getStreetId());
|
||||
result.put("streetName", shop.getStreetName());
|
||||
result.put("address", shop.getAddress());
|
||||
result.put("phone", shop.getPhone());
|
||||
result.put("latitude", shop.getLatitude());
|
||||
result.put("longitude", shop.getLongitude());
|
||||
result.put("distance", shop.getDistance());
|
||||
|
||||
|
||||
return AjaxResult.success("查询成功", result);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询店铺详情失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 获取店铺覆盖范围内的其他店铺
|
||||
// */
|
||||
// @PostMapping("/coverage")
|
||||
// public AjaxResult getShopsCoverage(@RequestBody Map<String, Object> params) {
|
||||
// try {
|
||||
// Long shopId = Long.valueOf(params.get("shopId").toString());
|
||||
// Double radiusKm = params.containsKey("radiusKm") ?
|
||||
// Double.valueOf(params.get("radiusKm").toString()) : 10.0; // 默认10公里
|
||||
//
|
||||
// Shop centerShop = shopService.getShop(shopId);
|
||||
// if (centerShop == null) {
|
||||
// return AjaxResult.error("店铺不存在");
|
||||
// }
|
||||
//
|
||||
// if (!LocationUtils.isValidCoordinate(centerShop.getLatitude(), centerShop.getLongitude())) {
|
||||
// return AjaxResult.error("中心店铺坐标信息不完整");
|
||||
// }
|
||||
//
|
||||
// // 获取附近的店铺
|
||||
// Map<String, Object> nearbyParams = new HashMap<>();
|
||||
// nearbyParams.put("latitude", centerShop.getLatitude());
|
||||
// nearbyParams.put("longitude", centerShop.getLongitude());
|
||||
// radiusKm);
|
||||
//
|
||||
// AjaxResult nearbyResult = getNearbyShops(nearbyParams);
|
||||
// if (nearbyResult.isSuccess()) {
|
||||
// Map<String, Object> data = (Map<String, Object>) nearbyResult.get("data");
|
||||
// List<Map<String, Object>> shops = (List<Map<String, Object>>) data.get("shops");
|
||||
//
|
||||
// // 排除自己
|
||||
// shops = shops.stream()
|
||||
// .filter(shopMap -> {
|
||||
// Shop shop = (Shop) shopMap.get("shop");
|
||||
// return !shop.getShopId().equals(shopId);
|
||||
// })
|
||||
// .collect(Collectors.toList());
|
||||
//
|
||||
// data.put("centerShop", centerShop);
|
||||
// data.put("shops", shops);
|
||||
// data.put("totalCount", shops.size());
|
||||
//
|
||||
// return AjaxResult.success("查询成功", data);
|
||||
// } else {
|
||||
// return nearbyResult;
|
||||
// }
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// logger.error("查询店铺覆盖范围失败: {}", e.getMessage(), e);
|
||||
// return AjaxResult.error("查询失败: " + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
@ -131,7 +131,6 @@ public class CustomerAddressController extends BaseController {
|
|||
@PostMapping("/update")
|
||||
@ResponseBody
|
||||
AjaxResult updateCustomerAddress(@RequestBody CustomerAddress customerAddress){
|
||||
logger.info("地址库的订单修改方法{}",customerAddress);
|
||||
try {
|
||||
// 判断下当前是否是默认地址
|
||||
if(ObjectUtil.equals(customerAddress.getIsDefault(), 1)){
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.ghy.web.controller.customer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.ghy.common.exception.base.BaseException;
|
||||
|
|
@ -153,13 +152,12 @@ public class CustomerSelectionController extends BaseController
|
|||
hisParam.setSelectionType(customerSelection.getSelectionType());
|
||||
List<CustomerSelection> hisList = customerSelectionService.selectCustomerSelectionList(hisParam);
|
||||
if(!CollectionUtils.isEmpty(hisList)){
|
||||
List<String> hisIds = hisList.stream()
|
||||
.filter(customerSelection1 -> Objects.equals(customerSelection1.getType(), customerSelection.getType())).map(CustomerSelection::getId).collect(Collectors.toList());
|
||||
List<String> hisIds = hisList.stream().map(CustomerSelection::getId).collect(Collectors.toList());
|
||||
StringBuilder ids = new StringBuilder();
|
||||
hisIds.forEach(model->{
|
||||
ids.append(model.trim()).append(",");
|
||||
});
|
||||
if(!StringUtil.isEmpty(ids)&&ids.length()>0){
|
||||
if(!StringUtil.isEmpty(ids)){
|
||||
String idString = ids.substring(0, ids.length()-1);
|
||||
customerSelectionService.deleteCustomerSelectionByIds(idString);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,25 +181,6 @@ public class GoodsCategoryController extends BaseController {
|
|||
return goodsCategoryService.selectCategoryTree(new GoodsCategory());
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择服务类目树
|
||||
*/
|
||||
@GetMapping("/selectServiceCategoryTree")
|
||||
public String selectServiceCategoryTree(ModelMap mmap) {
|
||||
return PREFIX + "/serviceTree";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务类目树数据
|
||||
*/
|
||||
@GetMapping("/serviceTreeData")
|
||||
@ResponseBody
|
||||
public List<Ztree> serviceTreeData() {
|
||||
GoodsCategory category = new GoodsCategory();
|
||||
category.setType(1); // 1表示服务类
|
||||
return goodsCategoryService.selectCategoryTree(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品类别表
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,13 +10,8 @@ import com.ghy.common.utils.ShiroUtils;
|
|||
import com.ghy.common.utils.StringUtils;
|
||||
import com.ghy.common.utils.bean.BeanUtils;
|
||||
import com.ghy.common.utils.poi.ExcelUtil;
|
||||
import com.ghy.common.utils.LocationUtils;
|
||||
import com.ghy.common.utils.http.HttpUtils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ghy.goods.domain.*;
|
||||
import com.ghy.goods.service.*;
|
||||
import com.ghy.shop.domain.Shop;
|
||||
import com.ghy.shop.service.ShopService;
|
||||
import com.ghy.system.domain.SysArea;
|
||||
import com.ghy.system.service.ISysAreaService;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
|
|
@ -31,7 +26,6 @@ import javax.annotation.Resource;
|
|||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import com.ghy.common.utils.BaiduMapUtils;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/goods/goods")
|
||||
|
|
@ -57,10 +51,6 @@ public class GoodsController extends BaseController {
|
|||
private IInsuranceManagerService insuranceManagerService;
|
||||
@Resource
|
||||
private IDeptCategoryInsuranceRelationService deptCategoryInsuranceRelationService;
|
||||
@Resource
|
||||
private ShopService shopService;
|
||||
@Resource
|
||||
private BaiduMapUtils baiduMapUtils;
|
||||
|
||||
@RequiresPermissions("goods:goods:view")
|
||||
@GetMapping()
|
||||
|
|
@ -174,15 +164,6 @@ public class GoodsController extends BaseController {
|
|||
@PostMapping("/app/list")
|
||||
@ResponseBody
|
||||
public TableDataInfo appList(@RequestBody Goods goods) {
|
||||
Integer type=goods.getType();
|
||||
if (type==null){
|
||||
goods.setType(1);
|
||||
}
|
||||
|
||||
// 获取用户当前位置
|
||||
Double userLatitude = goods.getLatitude();
|
||||
Double userLongitude = goods.getLongitude();
|
||||
|
||||
// 判断类目id是否为第三级,不是的话需要找到所有符合条件的第三级类目id作为新的条件
|
||||
if (goods.getDeptGoodsCategoryId() != null) {
|
||||
logger.info("入参:" + goods.getDeptGoodsCategoryId());
|
||||
|
|
@ -221,40 +202,9 @@ public class GoodsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
// 通过商品规格名称模糊查询
|
||||
if (StringUtils.isNotEmpty(goods.getGoodsStandard())) {
|
||||
logger.info("通过商品规格查询:{}", goods.getGoodsStandard());
|
||||
List<GoodsStandard> matchedStandards = goodsStandardService.selectByStandardNameLike(goods.getGoodsStandard());
|
||||
if (CollectionUtils.isNotEmpty(matchedStandards)) {
|
||||
// 获取所有符合规格条件的商品ID
|
||||
List<Long> goodsIds = matchedStandards.stream()
|
||||
.map(GoodsStandard::getGoodsId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
logger.info("通过规格查询到的商品ID列表:{}", goodsIds);
|
||||
|
||||
// 设置商品ID筛选条件,与现有的类目筛选条件组合使用
|
||||
if (goods.getGoodsIds() == null) {
|
||||
goods.setGoodsIds(goodsIds);
|
||||
} else {
|
||||
// 如果已有商品ID筛选条件,取交集
|
||||
goods.getGoodsIds().retainAll(goodsIds);
|
||||
}
|
||||
} else {
|
||||
logger.info("未找到符合规格条件的商品,设置空结果");
|
||||
// 如果没找到符合条件的规格,设置一个不存在的商品ID
|
||||
goods.setGoodsIds(Arrays.asList(-1L));
|
||||
}
|
||||
}
|
||||
|
||||
startPage();
|
||||
|
||||
List<Goods> list = goodsService.selectGoodsList(goods);
|
||||
logger.info("传入的类目id汇总{},传入的goods信息为{},获取到的所有商品{}",goods.getDeptGoodsCategoryIds(),goods,list);
|
||||
|
||||
// 用于缓存店铺信息,避免重复查询
|
||||
Map<Long, Shop> shopCache = new HashMap<>();
|
||||
|
||||
list.forEach(one -> {
|
||||
// 补全商品服务区域
|
||||
List<GoodsArea> goodsAreas = goodsAreaService.selectByGoodsId(one.getGoodsId());
|
||||
|
|
@ -272,89 +222,10 @@ public class GoodsController extends BaseController {
|
|||
one.setParGoodsCategoryId(parGoodsCategory.getGoodsCategoryId());
|
||||
one.setParGoodsCategoryName(parGoodsCategory.getGoodsCategoryName());
|
||||
}
|
||||
logger.debug("验证坐标是否合理: {}", LocationUtils.isValidCoordinate(userLatitude, userLongitude));
|
||||
// 计算距离逻辑
|
||||
if (LocationUtils.isValidCoordinate(userLatitude, userLongitude) && one.getShopId() != null) {
|
||||
try {
|
||||
// 从缓存获取店铺信息,避免重复查询
|
||||
Shop shop = shopCache.computeIfAbsent(one.getShopId(),
|
||||
shopId -> shopService.getShop(shopId));
|
||||
|
||||
if (shop != null && LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
|
||||
// 计算距离(米)
|
||||
double distanceInMeters = LocationUtils.getDistanceInMeters(
|
||||
userLatitude, userLongitude,
|
||||
shop.getLatitude(), shop.getLongitude()
|
||||
);
|
||||
|
||||
// 格式化距离并设置到商品对象
|
||||
one.setDistance(LocationUtils.formatDistance(distanceInMeters));
|
||||
|
||||
logger.debug("商品[{}]距离用户: {}", one.getGoodsName(), one.getDistance());
|
||||
} else {
|
||||
// 店铺坐标不完整
|
||||
one.setDistance(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("计算商品[{}]距离失败: {}", one.getGoodsName(), e.getMessage());
|
||||
// 计算异常时设为null
|
||||
one.setDistance(null);
|
||||
}
|
||||
} else {
|
||||
// 用户未提供位置或商品无店铺信息
|
||||
one.setDistance(null);
|
||||
}
|
||||
});
|
||||
|
||||
// 如果用户提供了位置信息,按距离排序(距离为null的排在最后)
|
||||
if (LocationUtils.isValidCoordinate(userLatitude, userLongitude)) {
|
||||
list.sort((goods1, goods2) -> {
|
||||
String distance1 = goods1.getDistance();
|
||||
String distance2 = goods2.getDistance();
|
||||
|
||||
// 距离为null的排在最后
|
||||
if (distance1 == null && distance2 == null) return 0;
|
||||
if (distance1 == null) return 1;
|
||||
if (distance2 == null) return -1;
|
||||
|
||||
// 解析距离数值进行比较
|
||||
try {
|
||||
double dist1 = parseDistanceToMeters(distance1);
|
||||
double dist2 = parseDistanceToMeters(distance2);
|
||||
return Double.compare(dist1, dist2);
|
||||
} catch (Exception e) {
|
||||
// 解析失败时保持原顺序
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("已按距离排序,用户位置: 纬度={}, 经度={}", userLatitude, userLongitude);
|
||||
}
|
||||
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析距离字符串为米数(用于排序)
|
||||
*/
|
||||
private double parseDistanceToMeters(String distanceText) {
|
||||
if (distanceText == null || distanceText.trim().isEmpty()) {
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
try {
|
||||
if (distanceText.endsWith("米")) {
|
||||
return Double.parseDouble(distanceText.replace("米", ""));
|
||||
} else if (distanceText.endsWith("公里")) {
|
||||
return Double.parseDouble(distanceText.replace("公里", "")) * 1000;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("无法解析距离字符串: {}", distanceText);
|
||||
}
|
||||
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
@PostMapping("/hot/list")
|
||||
@ResponseBody
|
||||
public TableDataInfo hotList(@RequestBody Goods goods) {
|
||||
|
|
@ -404,225 +275,11 @@ public class GoodsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
*
|
||||
* @param requestBody 请求参数
|
||||
* @return 商品详情信息
|
||||
*/
|
||||
@PostMapping("/getDetail")
|
||||
@ResponseBody
|
||||
public AjaxResult getDetail(@RequestBody JSONObject requestBody) {
|
||||
public AjaxResult getDetail(@RequestBody Goods goods) {
|
||||
try {
|
||||
// 从请求体中提取参数
|
||||
Long goodsId = requestBody.getLong("goodsId");
|
||||
Double userLatitude = requestBody.getDouble("latitude");
|
||||
Double userLongitude = requestBody.getDouble("longitude");
|
||||
String provinceName = requestBody.getString("provinceName");
|
||||
String cityName = requestBody.getString("cityName");
|
||||
String countryName = requestBody.getString("countryName");
|
||||
String streetName = requestBody.getString("streetName");
|
||||
String address = requestBody.getString("address");
|
||||
|
||||
if (goodsId == null) {
|
||||
return AjaxResult.error("商品ID不能为空");
|
||||
}
|
||||
|
||||
Goods result = goodsService.selectById(goodsId);
|
||||
if (result == null) {
|
||||
return AjaxResult.error("商品不存在");
|
||||
}
|
||||
|
||||
// 如果用户没有提供经纬度,但有详细地址,则通过地址获取经纬度
|
||||
if ((userLatitude == null || userLongitude == null) &&
|
||||
(provinceName != null || cityName != null ||
|
||||
countryName != null || streetName != null ||
|
||||
address != null)) {
|
||||
try {
|
||||
// 使用BaiduMapUtils工具类获取经纬度
|
||||
Map<String, Double> coordinates = baiduMapUtils.getCoordinatesByAddress(
|
||||
provinceName, cityName, countryName, streetName, address
|
||||
);
|
||||
|
||||
if (coordinates != null) {
|
||||
userLongitude = coordinates.get("longitude");
|
||||
userLatitude = coordinates.get("latitude");
|
||||
logger.info("通过地址获取到用户经纬度: 经度={}, 纬度={}", userLongitude, userLatitude);
|
||||
} else {
|
||||
logger.warn("通过地址获取用户经纬度失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地图API异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品店铺信息
|
||||
Shop goodsShop = null;
|
||||
if (result.getShopId() != null) {
|
||||
try {
|
||||
goodsShop = shopService.getShop(result.getShopId());
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取商品店铺信息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 获取服务店铺信息
|
||||
Shop serviceShop = null;
|
||||
|
||||
if (result.getDeptGoodsCategoryId() != null) {
|
||||
try {
|
||||
// 1. 通过商品的类目ID获取类目信息
|
||||
DeptGoodsCategory deptGoodsCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(result.getDeptGoodsCategoryId());
|
||||
Long serviceCategoryId = null;
|
||||
|
||||
if (deptGoodsCategory != null) {
|
||||
// 先检查当前类目是否有服务类目ID
|
||||
if (deptGoodsCategory.getServiceCategoryId() != null) {
|
||||
serviceCategoryId = deptGoodsCategory.getServiceCategoryId();
|
||||
logger.debug("当前类目[{}]的服务类目ID: {}", deptGoodsCategory.getGoodsCategoryName(), serviceCategoryId);
|
||||
} else {
|
||||
// 如果当前类目没有服务类目ID,查找上一级类目
|
||||
logger.debug("当前类目[{}]未配置服务类目ID,查找上一级类目", deptGoodsCategory.getGoodsCategoryName());
|
||||
|
||||
if (deptGoodsCategory.getParentCategoryId() != null) {
|
||||
DeptGoodsCategory parentCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(deptGoodsCategory.getParentCategoryId());
|
||||
if (parentCategory != null && parentCategory.getServiceCategoryId() != null) {
|
||||
serviceCategoryId = parentCategory.getServiceCategoryId();
|
||||
logger.debug("上一级类目[{}]的服务类目ID: {}", parentCategory.getGoodsCategoryName(), serviceCategoryId);
|
||||
} else {
|
||||
logger.debug("上一级类目[{}]也未配置服务类目ID", parentCategory != null ? parentCategory.getGoodsCategoryName() : "null");
|
||||
}
|
||||
} else {
|
||||
logger.debug("当前类目没有父级类目");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serviceCategoryId != null) {
|
||||
// 2. 通过服务类目ID查询所有使用该服务类目的商品
|
||||
Goods queryGoods = new Goods();
|
||||
queryGoods.setDeptGoodsCategoryId(serviceCategoryId);
|
||||
queryGoods.setStatus(0); // 只查询上架的商品
|
||||
List<Goods> goodsList = goodsService.selectGoodsList(queryGoods);
|
||||
if (goodsList.size()==0){
|
||||
DeptGoodsCategory deptGoodsCategory1=deptGoodsCategoryService.selectOneByGoodsCategoryId(serviceCategoryId);
|
||||
serviceCategoryId=deptGoodsCategory1.getDeptGoodsCategoryId();
|
||||
// 直接使用新方法获取商品列表
|
||||
goodsList = goodsStandardService.selectGoodsByDeptGoodsCategoryId(serviceCategoryId);
|
||||
// 过滤只保留上架的商品
|
||||
goodsList = goodsList.stream()
|
||||
.filter(goods -> goods.getStatus() != null && goods.getStatus() == 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
logger.info("获取到的服务类目id{} 取到的商品列表:{}", serviceCategoryId,goodsList);
|
||||
// 3. 提取所有店铺ID(去重)
|
||||
Set<Long> shopIds = goodsList.stream()
|
||||
.filter(g -> g.getShopId() != null)
|
||||
.map(Goods::getShopId)
|
||||
.collect(Collectors.toSet());
|
||||
logger.info("取到的店铺列表:{}", shopIds);
|
||||
if (!shopIds.isEmpty()) {
|
||||
// 找到最近的店铺
|
||||
Shop nearestShop = null;
|
||||
double minDistance = Double.MAX_VALUE;
|
||||
|
||||
for (Long shopId : shopIds) {
|
||||
Shop shop = shopService.getShop(shopId);
|
||||
if (shop != null && LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
|
||||
if (LocationUtils.isValidCoordinate(userLatitude, userLongitude)) {
|
||||
try {
|
||||
double distanceInMeters = LocationUtils.getDistanceInMeters(
|
||||
userLatitude, userLongitude,
|
||||
shop.getLatitude(), shop.getLongitude()
|
||||
);
|
||||
if (distanceInMeters < minDistance) {
|
||||
minDistance = distanceInMeters;
|
||||
nearestShop = shop;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("计算店铺[{}]距离失败: {}", shop.getShopName(), e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// 如果用户没有提供位置,选择第一个有效店铺
|
||||
if (nearestShop == null) {
|
||||
nearestShop = shop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serviceShop = nearestShop;
|
||||
} else {
|
||||
logger.debug("服务类目[{}]下没有找到商品", serviceCategoryId);
|
||||
}
|
||||
} else {
|
||||
logger.debug("未找到有效的服务类目ID");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取服务店铺信息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 设置商品店铺和服务店铺信息
|
||||
if(result.getType()==1){
|
||||
serviceShop=goodsShop;
|
||||
result.setServiceShop(serviceShop);
|
||||
}else {
|
||||
result.setShop(goodsShop);
|
||||
}
|
||||
// 计算距离逻辑
|
||||
if (LocationUtils.isValidCoordinate(userLatitude, userLongitude)) {
|
||||
// 计算服务店铺距离
|
||||
if (serviceShop != null) {
|
||||
try {
|
||||
if (LocationUtils.isValidCoordinate(serviceShop.getLatitude(), serviceShop.getLongitude())) {
|
||||
// 计算距离(米)
|
||||
double distanceInMeters = LocationUtils.getDistanceInMeters(
|
||||
userLatitude, userLongitude,
|
||||
serviceShop.getLatitude(), serviceShop.getLongitude()
|
||||
);
|
||||
|
||||
// 格式化距离并设置到服务店铺对象
|
||||
serviceShop.setDistance(LocationUtils.formatDistance(distanceInMeters));
|
||||
|
||||
logger.debug("商品[{}]服务店铺距离用户: {}", result.getGoodsName(), serviceShop.getDistance());
|
||||
} else {
|
||||
// 店铺坐标不完整
|
||||
serviceShop.setDistance(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("计算商品[{}]服务店铺距离失败: {}", result.getGoodsName(), e.getMessage());
|
||||
// 计算异常时设为null
|
||||
serviceShop.setDistance(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算商品店铺距离
|
||||
if (goodsShop != null) {
|
||||
try {
|
||||
if (LocationUtils.isValidCoordinate(goodsShop.getLatitude(), goodsShop.getLongitude())) {
|
||||
// 计算距离(米)
|
||||
double distanceInMeters = LocationUtils.getDistanceInMeters(
|
||||
userLatitude, userLongitude,
|
||||
goodsShop.getLatitude(), goodsShop.getLongitude()
|
||||
);
|
||||
|
||||
// 格式化距离并设置到商品店铺对象
|
||||
goodsShop.setDistance(LocationUtils.formatDistance(distanceInMeters));
|
||||
|
||||
logger.debug("商品[{}]商品店铺距离用户: {}", result.getGoodsName(), goodsShop.getDistance());
|
||||
} else {
|
||||
// 店铺坐标不完整
|
||||
goodsShop.setDistance(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("计算商品[{}]商品店铺距离失败: {}", result.getGoodsName(), e.getMessage());
|
||||
// 计算异常时设为null
|
||||
goodsShop.setDistance(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.setServiceShop(serviceShop);
|
||||
Goods result = goodsService.selectById(goods.getGoodsId());
|
||||
|
||||
// 补全商品类目及父级类目信息
|
||||
GoodsCategory goodsCategory = goodsCategoryService.selectById(result.getDeptGoodsCategoryId());
|
||||
|
|
@ -695,186 +352,6 @@ public class GoodsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品ID和用户位置获取服务店铺列表
|
||||
* @return 服务店铺列表,按距离排序
|
||||
*/
|
||||
@PostMapping("/getShopsByGoodsId")
|
||||
@ResponseBody
|
||||
public AjaxResult getShopsByGoodsId(@RequestBody JSONObject requestBody) {
|
||||
try {
|
||||
Long goodsId = requestBody.getLong("goodsId");
|
||||
Double latitude = requestBody.getDouble("latitude");
|
||||
Double longitude = requestBody.getDouble("longitude");
|
||||
String provinceName = requestBody.getString("provinceName");
|
||||
String cityName = requestBody.getString("cityName");
|
||||
String countryName = requestBody.getString("countryName");
|
||||
String streetName = requestBody.getString("streetName");
|
||||
String address = requestBody.getString("address");
|
||||
|
||||
if (goodsId == null) {
|
||||
return AjaxResult.error("商品ID不能为空");
|
||||
}
|
||||
|
||||
// 如果用户没有提供经纬度,但有详细地址,则通过地址获取经纬度
|
||||
if ((latitude == null || longitude == null) &&
|
||||
(provinceName != null || cityName != null || countryName != null ||
|
||||
streetName != null || address != null)) {
|
||||
try {
|
||||
// 使用BaiduMapUtils工具类获取经纬度
|
||||
Map<String, Double> coordinates = baiduMapUtils.getCoordinatesByAddress(
|
||||
provinceName, cityName, countryName, streetName, address
|
||||
);
|
||||
|
||||
if (coordinates != null) {
|
||||
longitude = coordinates.get("longitude");
|
||||
latitude = coordinates.get("latitude");
|
||||
logger.info("通过地址获取到用户经纬度: 经度={}, 纬度={}", longitude, latitude);
|
||||
} else {
|
||||
logger.warn("通过地址获取用户经纬度失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地图API异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品信息
|
||||
Goods goods = goodsService.selectById(goodsId);
|
||||
if (goods == null) {
|
||||
return AjaxResult.error("商品不存在");
|
||||
}
|
||||
|
||||
List<Shop> serviceShops = new ArrayList<>();
|
||||
|
||||
// 通过商品的服务类目获取服务店铺
|
||||
if (goods.getDeptGoodsCategoryId() != null) {
|
||||
try {
|
||||
// 1. 通过商品的类目ID获取类目信息
|
||||
DeptGoodsCategory deptGoodsCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(goods.getDeptGoodsCategoryId());
|
||||
Long serviceCategoryId = null;
|
||||
|
||||
if (deptGoodsCategory != null) {
|
||||
// 先检查当前类目是否有服务类目ID
|
||||
if (deptGoodsCategory.getServiceCategoryId() != null) {
|
||||
serviceCategoryId = deptGoodsCategory.getServiceCategoryId();
|
||||
logger.debug("当前类目[{}]的服务类目ID: {}", deptGoodsCategory.getGoodsCategoryName(), serviceCategoryId);
|
||||
} else {
|
||||
// 如果当前类目没有服务类目ID,查找上一级类目
|
||||
logger.debug("当前类目[{}]未配置服务类目ID,查找上一级类目", deptGoodsCategory.getGoodsCategoryName());
|
||||
|
||||
if (deptGoodsCategory.getParentCategoryId() != null) {
|
||||
DeptGoodsCategory parentCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(deptGoodsCategory.getParentCategoryId());
|
||||
if (parentCategory != null && parentCategory.getServiceCategoryId() != null) {
|
||||
serviceCategoryId = parentCategory.getServiceCategoryId();
|
||||
logger.debug("上一级类目[{}]的服务类目ID: {}", parentCategory.getGoodsCategoryName(), serviceCategoryId);
|
||||
} else {
|
||||
logger.debug("上一级类目[{}]也未配置服务类目ID", parentCategory != null ? parentCategory.getGoodsCategoryName() : "null");
|
||||
}
|
||||
} else {
|
||||
logger.debug("当前类目没有父级类目");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Goods> goodsList = new ArrayList<>();
|
||||
if (serviceCategoryId != null) {
|
||||
// 2. 通过服务类目ID查询所有使用该服务类目的商品
|
||||
Goods queryGoods = new Goods();
|
||||
queryGoods.setDeptGoodsCategoryId(serviceCategoryId);
|
||||
queryGoods.setStatus(0); // 只查询上架的商品
|
||||
goodsList = goodsService.selectGoodsList(queryGoods);
|
||||
if (goodsList.size() == 0) {
|
||||
DeptGoodsCategory deptGoodsCategory1 = deptGoodsCategoryService.selectOneByGoodsCategoryId(serviceCategoryId);
|
||||
serviceCategoryId = deptGoodsCategory1.getDeptGoodsCategoryId();
|
||||
// 直接使用新方法获取商品列表
|
||||
goodsList = goodsStandardService.selectGoodsByDeptGoodsCategoryId(serviceCategoryId);
|
||||
// 过滤只保留上架的商品
|
||||
goodsList = goodsList.stream()
|
||||
.filter(g -> g.getStatus() != null && g.getStatus() == 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
logger.info("获取到的服务类目id{} 取到的商品列表:{}", serviceCategoryId, goodsList);
|
||||
}
|
||||
|
||||
if (!goodsList.isEmpty()) {
|
||||
logger.debug("通过商品ID[{}]找到{}个相关服务商品", goodsId, goodsList.size());
|
||||
|
||||
// 提取所有店铺ID(去重)
|
||||
Set<Long> shopIds = goodsList.stream()
|
||||
.filter(g -> g.getShopId() != null)
|
||||
.map(Goods::getShopId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (!shopIds.isEmpty()) {
|
||||
// 获取所有店铺信息并计算距离
|
||||
for (Long shopId : shopIds) {
|
||||
Shop shop = shopService.getShop(shopId);
|
||||
if (shop != null) {
|
||||
// 计算距离
|
||||
if (LocationUtils.isValidCoordinate(latitude, longitude) &&
|
||||
LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
|
||||
try {
|
||||
double distanceInMeters = LocationUtils.getDistanceInMeters(
|
||||
latitude, longitude,
|
||||
shop.getLatitude(), shop.getLongitude()
|
||||
);
|
||||
shop.setDistance(LocationUtils.formatDistance(distanceInMeters));
|
||||
} catch (Exception e) {
|
||||
logger.warn("计算店铺[{}]距离失败: {}", shop.getShopName(), e.getMessage());
|
||||
shop.setDistance(null);
|
||||
}
|
||||
} else {
|
||||
shop.setDistance(null);
|
||||
}
|
||||
serviceShops.add(shop);
|
||||
}
|
||||
}
|
||||
|
||||
// 按距离排序(有距离的排在前面,然后按距离升序)
|
||||
serviceShops.sort((s1, s2) -> {
|
||||
if (s1.getDistance() == null && s2.getDistance() == null) {
|
||||
return 0;
|
||||
}
|
||||
if (s1.getDistance() == null) {
|
||||
return 1;
|
||||
}
|
||||
if (s2.getDistance() == null) {
|
||||
return -1;
|
||||
}
|
||||
// 提取距离数值进行比较
|
||||
try {
|
||||
double d1 = Double.parseDouble(s1.getDistance().replaceAll("[^0-9.]", ""));
|
||||
double d2 = Double.parseDouble(s2.getDistance().replaceAll("[^0-9.]", ""));
|
||||
return Double.compare(d1, d2);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logger.debug("未找到商品ID[{}]的相关服务商品", goodsId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取服务店铺信息失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("goodsId", goodsId);
|
||||
result.put("goodsName", goods.getGoodsName());
|
||||
result.put("userLatitude", latitude);
|
||||
result.put("userLongitude", longitude);
|
||||
result.put("serviceShops", serviceShops);
|
||||
result.put("totalCount", serviceShops.size());
|
||||
|
||||
return AjaxResult.success(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取服务店铺列表失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("获取服务店铺列表失败: " + ExceptionUtil.getExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Log(title = "商品管理", businessType = BusinessType.EXPORT)
|
||||
@RequiresPermissions("goods:goods:export")
|
||||
@PostMapping("/export")
|
||||
|
|
@ -945,13 +422,6 @@ public class GoodsController extends BaseController {
|
|||
@ResponseBody
|
||||
public AjaxResult editSave(@Validated Goods goods) {
|
||||
goods.setUpdateBy(getLoginName());
|
||||
|
||||
// 处理Long类型字段的空值情况
|
||||
// 当前端传入0或空字符串时,将shopId设置为null以便清空
|
||||
if (goods.getShopId() != null && goods.getShopId().equals(0L)) {
|
||||
goods.setShopId(null);
|
||||
}
|
||||
|
||||
return toAjax(goodsService.updateGoods(goods));
|
||||
}
|
||||
|
||||
|
|
@ -959,12 +429,6 @@ public class GoodsController extends BaseController {
|
|||
@ResponseBody
|
||||
public AjaxResult appEditSave(@RequestBody @Validated Goods goods) {
|
||||
|
||||
// 处理Long类型字段的空值情况
|
||||
// 当前端传入0或空字符串时,将shopId设置为null以便清空
|
||||
if (goods.getShopId() != null && goods.getShopId().equals(0L)) {
|
||||
goods.setShopId(null);
|
||||
}
|
||||
|
||||
goodsService.edit(goods);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
|
@ -1024,6 +488,4 @@ public class GoodsController extends BaseController {
|
|||
return AjaxResult.error(ExceptionUtil.getExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.ghy.web.controller.goods;
|
|||
import com.ghy.common.core.controller.BaseController;
|
||||
import com.ghy.common.core.domain.AjaxResult;
|
||||
import com.ghy.common.core.page.TableDataInfo;
|
||||
import com.ghy.goods.domain.Goods;
|
||||
import com.ghy.goods.domain.GoodsStandard;
|
||||
import com.ghy.goods.service.GoodsStandardService;
|
||||
import com.ghy.order.domain.OrderTemplate;
|
||||
|
|
@ -65,14 +64,4 @@ public class GoodsStandardController extends BaseController {
|
|||
return toAjax(goodsStandardService.save(goodsStandardList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门商品分类ID获取商品列表
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping("/goods/by-dept-category/{deptGoodsCategoryId}")
|
||||
public AjaxResult getGoodsByDeptCategoryId(@PathVariable("deptGoodsCategoryId") Long deptGoodsCategoryId) {
|
||||
List<Goods> goodsList = goodsStandardService.selectGoodsByDeptGoodsCategoryId(deptGoodsCategoryId);
|
||||
return AjaxResult.success(goodsList);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package com.ghy.web.controller.order;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 售后纠纷处理请求
|
||||
*
|
||||
* @author system
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
public class AfterServiceDisputeRequest {
|
||||
|
||||
/**
|
||||
* 售后记录ID
|
||||
*/
|
||||
private String recordId;
|
||||
|
||||
/**
|
||||
* 主单ID
|
||||
*/
|
||||
private Long orderMasterId;
|
||||
|
||||
/**
|
||||
* 子单ID
|
||||
*/
|
||||
private Long orderDetailId;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 售后纠纷平台处理原因
|
||||
*/
|
||||
private String platformHandleReason;
|
||||
|
||||
public String getRecordId() {
|
||||
return recordId;
|
||||
}
|
||||
|
||||
public void setRecordId(String recordId) {
|
||||
this.recordId = recordId;
|
||||
}
|
||||
|
||||
public Long getOrderMasterId() {
|
||||
return orderMasterId;
|
||||
}
|
||||
|
||||
public void setOrderMasterId(Long orderMasterId) {
|
||||
this.orderMasterId = orderMasterId;
|
||||
}
|
||||
|
||||
public Long getOrderDetailId() {
|
||||
return orderDetailId;
|
||||
}
|
||||
|
||||
public void setOrderDetailId(Long orderDetailId) {
|
||||
this.orderDetailId = orderDetailId;
|
||||
}
|
||||
|
||||
public BigDecimal getRefundAmount() {
|
||||
return refundAmount;
|
||||
}
|
||||
|
||||
public void setRefundAmount(BigDecimal refundAmount) {
|
||||
this.refundAmount = refundAmount;
|
||||
}
|
||||
|
||||
public String getPlatformHandleReason() {
|
||||
return platformHandleReason;
|
||||
}
|
||||
|
||||
public void setPlatformHandleReason(String platformHandleReason) {
|
||||
this.platformHandleReason = platformHandleReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AfterServiceDisputeRequest{" +
|
||||
"recordId='" + recordId + '\'' +
|
||||
", orderMasterId=" + orderMasterId +
|
||||
", orderDetailId=" + orderDetailId +
|
||||
", refundAmount=" + refundAmount +
|
||||
", platformHandleReason='" + platformHandleReason + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -141,21 +141,6 @@ public class AfterServiceRecordController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改商品售后记录
|
||||
*/
|
||||
@PostMapping("/editGoods")
|
||||
@ResponseBody
|
||||
public AjaxResult editGoodsSave(@RequestBody AfterServiceRecord afterServiceRecord) {
|
||||
logger.info("修改商品售后记录:{}", afterServiceRecord);
|
||||
try {
|
||||
return afterServiceRecordService.updateGoodsAfterServiceRecord(afterServiceRecord);
|
||||
} catch (Exception exception) {
|
||||
logger.error(ExceptionUtils.getStackTrace(exception));
|
||||
return AjaxResult.error(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除售后记录
|
||||
*/
|
||||
|
|
@ -166,34 +151,4 @@ public class AfterServiceRecordController extends BaseController {
|
|||
public AjaxResult remove(String ids) {
|
||||
return toAjax(afterServiceRecordService.deleteAfterServiceRecordByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 师傅重发/补发操作
|
||||
* 师傅端点击重发补发按钮,保存重发/补发方案
|
||||
*/
|
||||
@PostMapping("/workerResendPlan")
|
||||
@ResponseBody
|
||||
public AjaxResult workerResendPlan(@RequestBody AfterServiceRecord afterServiceRecord) {
|
||||
return afterServiceRecordService.workerResendPlan(afterServiceRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退货操作
|
||||
* 客户或师傅端进行退货操作,保存退货信息
|
||||
*/
|
||||
@PostMapping("/returnGoods")
|
||||
@ResponseBody
|
||||
public AjaxResult returnGoods(@RequestBody AfterServiceRecord afterServiceRecord) {
|
||||
return afterServiceRecordService.returnGoods(afterServiceRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 师傅确认收货
|
||||
* 师傅确认收到货物后,根据同意处理方式决定是否执行退款
|
||||
*/
|
||||
@PostMapping("/workerConfirmReceive")
|
||||
@ResponseBody
|
||||
public AjaxResult workerConfirmReceive(@RequestBody AfterServiceRecord afterServiceRecord) {
|
||||
return afterServiceRecordService.workerConfirmReceive(afterServiceRecord);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.ghy.web.controller.order;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
|
@ -142,16 +140,4 @@ public class OrderAttachmentRecordController extends BaseController
|
|||
{
|
||||
return toAjax(orderAttachmentRecordService.deleteOrderAttachmentRecordByIds(ids));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 删除附件费 根据子单id
|
||||
*/
|
||||
@PostMapping( "/deleteByDetailId")
|
||||
@ResponseBody
|
||||
public AjaxResult deleteByDetailId(@RequestParam Long orderDetailId)
|
||||
{
|
||||
return toAjax(orderAttachmentRecordService.deleteOrderAttachmentRecordByOrderDetailId(orderDetailId));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -78,23 +78,8 @@ public class AlipayController extends BaseController {
|
|||
PayParam payParam = PayParam.delayPayParam(om.getCode() + "_" + System.currentTimeMillis(), payMoney, "商品标题", "商品描述信息");
|
||||
try {
|
||||
JSONObject response = adapayService.alipayQrPay(om.getDeptId(), payParam, null, null, null);
|
||||
boolean status = AdapayStatusEnum.succeeded.code.equals(response.getString("status"));
|
||||
if (!status) {
|
||||
logger.error("创建支付失败: {}", response.toJSONString());
|
||||
return AjaxResult.error("网络不佳 请稍后再试");
|
||||
}
|
||||
// 支付二维码创建成功 保存一下paymentId
|
||||
String paymentId = response.getString("id");
|
||||
// 更新财务主单的paymentId
|
||||
FinancialMaster fm2update = new FinancialMaster();
|
||||
fm2update.setId(fm.getId());
|
||||
fm2update.setPaymentId(paymentId);
|
||||
fm2update.setPayType(PayTypeEnum.ALIPAY_QR.getCode());
|
||||
financialMasterService.updateFinancialMaster(fm2update);
|
||||
// 保存支付ID与主财务单ID到关系表
|
||||
PaymentRelation relation = new PaymentRelation(null, fm.getId(), PaymentRelation.FINANCIAL_MASTER, fm.getPayMoney());
|
||||
relation.setPaymentId(paymentId);
|
||||
paymentRelationService.insert(relation);
|
||||
paymentRelationService.insert(new PaymentRelation(null, fm.getId(), PaymentRelation.FINANCIAL_MASTER, fm.getPayMoney()));
|
||||
return AjaxResult.success(response);
|
||||
} catch (BaseAdaPayException e) {
|
||||
logger.error("创建支付失败", e);
|
||||
|
|
|
|||
|
|
@ -107,25 +107,6 @@ public class WxPayController extends BaseController {
|
|||
String.valueOf(payMoney), "工圈子居家设备", "工圈子居家设备购买付费");
|
||||
JSONObject response = adapayService.wxLitePay(orderMaster.getDeptId(), payParam, expend, null, null);
|
||||
String paymentId = response.getString("id");
|
||||
|
||||
// 更新财务主单的paymentId
|
||||
if (PayStatus.WAIT_PAY.getCode().equals(financialMaster.getPayStatus())) {
|
||||
FinancialMaster fm2update = new FinancialMaster();
|
||||
fm2update.setId(financialMaster.getId());
|
||||
fm2update.setPaymentId(paymentId);
|
||||
financialMasterService.updateFinancialMaster(fm2update);
|
||||
}
|
||||
|
||||
// 更新财务变更记录的paymentId
|
||||
for (FinancialChangeRecord fcr : financialChangeRecords) {
|
||||
if (PayStatus.WAIT_PAY.getCode().equals(fcr.getPayStatus())) {
|
||||
FinancialChangeRecord fcr2update = new FinancialChangeRecord();
|
||||
fcr2update.setId(fcr.getId());
|
||||
fcr2update.setPaymentId(paymentId);
|
||||
financialChangeRecordService.update(fcr2update);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存支付ID与订单ID到关系表
|
||||
for (PaymentRelation relation : relations) {
|
||||
relation.setPaymentId(paymentId);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 百度地图逆解析
|
||||
* @author clunt
|
||||
|
|
@ -41,34 +38,12 @@ public class BaiduController extends BaseController {
|
|||
JSONObject json = new JSONObject();
|
||||
|
||||
String location = jsonObject.getString("location");
|
||||
|
||||
// 解析经纬度
|
||||
String[] coordinates = location.split(",");
|
||||
if (coordinates.length != 2) {
|
||||
return AjaxResult.error("location格式错误,应为:经度,纬度");
|
||||
}
|
||||
|
||||
try {
|
||||
double longitude = Double.parseDouble(coordinates[1]); // 经度
|
||||
double latitude = Double.parseDouble(coordinates[0]); // 纬度
|
||||
|
||||
// 将经纬度添加到返回结果中
|
||||
json.put("longitude", longitude);
|
||||
json.put("latitude", latitude);
|
||||
json.put("coordinates", coordinates);
|
||||
|
||||
logger.info("解析到的坐标 - 经度: {}, 纬度: {}", longitude, latitude);
|
||||
} catch (NumberFormatException e) {
|
||||
return AjaxResult.error("经纬度格式错误");
|
||||
}
|
||||
|
||||
String url = baiduConfig.getUrl().replace("#AK#", baiduConfig.getAk()) + location;
|
||||
String result = HttpUtils.sendGet(url);
|
||||
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||
JSONObject resultJson = JSONObject.parseObject(result);
|
||||
if("0".equals(resultJson.getString("status"))){
|
||||
JSONObject addressJson = resultJson.getJSONObject("result").getJSONObject("addressComponent");
|
||||
logger.info("百度地图获取到的地址 :" + addressJson);
|
||||
String provinceName = addressJson.getString("province");
|
||||
logger.info("provinceName :" + provinceName);
|
||||
SysArea provinceArea = iSysAreaService.selectByName(provinceName, null);
|
||||
|
|
@ -78,25 +53,9 @@ public class BaiduController extends BaseController {
|
|||
String countryName = addressJson.getString("district");
|
||||
logger.info("countryName :" + countryName);
|
||||
SysArea countryArea = iSysAreaService.selectByName(countryName, cityArea.getAreaCode());
|
||||
String streetName = addressJson.getString("town");
|
||||
logger.info("streetName :" + streetName);
|
||||
SysArea streetArea = iSysAreaService.selectByName(streetName, countryArea.getAreaCode());
|
||||
|
||||
// 添加地址信息
|
||||
json.put("provinceArea", provinceArea);
|
||||
json.put("cityArea", cityArea);
|
||||
json.put("countryArea", countryArea);
|
||||
json.put("streetArea", streetArea);
|
||||
|
||||
// 添加完整地址
|
||||
String fullAddress = (provinceName != null ? provinceName : "") +
|
||||
(cityName != null ? cityName : "") +
|
||||
(countryName != null ? countryName : "") +
|
||||
(streetName != null ? streetName : "");
|
||||
json.put("fullAddress", fullAddress);
|
||||
|
||||
// 保留原有的location字段用于兼容
|
||||
json.put("location", location);
|
||||
}else {
|
||||
return AjaxResult.error("Api服务异常!");
|
||||
}
|
||||
|
|
@ -108,56 +67,6 @@ public class BaiduController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度地图正向地理编码(地址 -> 经纬度)
|
||||
* 接收JSON:{provinceName, cityName, countryName, streetName, address}
|
||||
*/
|
||||
@PostMapping("/geocode")
|
||||
@ResponseBody
|
||||
public AjaxResult geocode(@RequestBody JSONObject jsonObject) {
|
||||
try {
|
||||
String provinceName = jsonObject.getString("provinceName");
|
||||
String cityName = jsonObject.getString("cityName");
|
||||
String countryName = jsonObject.getString("countryName");
|
||||
String streetName = jsonObject.getString("streetName");
|
||||
String detailAddress = jsonObject.getString("address");
|
||||
|
||||
StringBuilder full = new StringBuilder();
|
||||
if (provinceName != null) { full.append(provinceName); }
|
||||
if (cityName != null) { full.append(cityName); }
|
||||
if (countryName != null) { full.append(countryName); }
|
||||
if (streetName != null) { full.append(streetName); }
|
||||
if (detailAddress != null) { full.append(detailAddress); }
|
||||
|
||||
String address = full.toString();
|
||||
if (address == null || address.trim().isEmpty()) {
|
||||
return AjaxResult.error("地址不能为空");
|
||||
}
|
||||
|
||||
String encoded = URLEncoder.encode(address, StandardCharsets.UTF_8.name());
|
||||
// 使用百度正向地理编码API
|
||||
String url = "https://api.map.baidu.com/geocoding/v3/?output=json&ak="
|
||||
+ baiduConfig.getAk() + "&address=" + encoded;
|
||||
|
||||
String result = HttpUtils.sendGet(url);
|
||||
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||
JSONObject resultJson = JSONObject.parseObject(result);
|
||||
if ("0".equals(resultJson.getString("status"))) {
|
||||
JSONObject location = resultJson.getJSONObject("result").getJSONObject("location");
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("longitude", location.getBigDecimal("lng"));
|
||||
data.put("latitude", location.getBigDecimal("lat"));
|
||||
data.put("fullAddress", address);
|
||||
return AjaxResult.success(data);
|
||||
} else {
|
||||
return AjaxResult.error("百度地理编码失败:" + resultJson.getString("msg"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return AjaxResult.error(ExceptionUtil.getExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String result = " {\"status\":0,\"result\":{\"location\":{\"lng\":113.29950224957551,\"lat\":23.019282373814027},\"formatted_address\":\"广东省广州市番禺区安平路\",\"business\":\"大石\",\"addressComponent\":{\"country\":\"中国\",\"country_code\":0,\"country_code_iso\":\"CHN\",\"country_code_iso2\":\"CN\",\"province\":\"广东省\",\"city\":\"广州市\",\"city_level\":2,\"district\":\"番禺区\",\"town\":\"\",\"town_code\":\"\",\"distance\":\"\",\"direction\":\"\",\"adcode\":\"440113\",\"street\":\"安平路\",\"street_number\":\"\"},\"pois\":[],\"roads\":[],\"poiRegions\":[],\"sematic_description\":\"\",\"cityCode\":257}}\n";
|
||||
|
|
|
|||
|
|
@ -57,135 +57,38 @@ public class WorkerAreaController extends BaseController {
|
|||
* 查询某个师傅的所有接单地区
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
* @param serviceType 服务类型:1=服务, 2=商品,不传则查询所有
|
||||
*/
|
||||
@GetMapping("worker")
|
||||
@ResponseBody
|
||||
public AjaxResult getByWorker(Long workerId, Integer serviceType) {
|
||||
if (serviceType != null) {
|
||||
// 根据服务类型查询
|
||||
return AjaxResult.success(workerAreaService.getByWorkerIdAndType(workerId, serviceType));
|
||||
} else {
|
||||
// 查询所有区域(保持向后兼容)
|
||||
return AjaxResult.success(workerAreaService.getByWorker(workerId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个师傅的服务区域(类型1)
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
*/
|
||||
@GetMapping("worker/service")
|
||||
@ResponseBody
|
||||
public AjaxResult getServiceAreasByWorker(Long workerId) {
|
||||
return AjaxResult.success(workerAreaService.getByWorkerIdAndType(workerId, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个师傅的商品区域(类型2)
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
*/
|
||||
@GetMapping("worker/goods")
|
||||
@ResponseBody
|
||||
public AjaxResult getGoodsAreasByWorker(Long workerId) {
|
||||
return AjaxResult.success(workerAreaService.getByWorkerIdAndType(workerId, 2));
|
||||
public AjaxResult getByWorker(Long workerId) {
|
||||
return AjaxResult.success(workerAreaService.getByWorker(workerId));
|
||||
}
|
||||
|
||||
@GetMapping("worker/edit")
|
||||
@ResponseBody
|
||||
public AjaxResult getEditWorker(Long workerId, Integer type){
|
||||
public AjaxResult getEditWorker(Long workerId){
|
||||
Map<Long, List<String>> countryMap = new HashMap<>();
|
||||
|
||||
// 需求1:根据类型查询对应的区域数据
|
||||
List<WorkerArea> byWorker;
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
// 服务类型:查询服务区域
|
||||
byWorker = workerAreaService.getByWorkerIdAndType(workerId, 1);
|
||||
} else if (Integer.valueOf(2).equals(type)) {
|
||||
// 商品类型:查询商品区域
|
||||
byWorker = workerAreaService.getByWorkerIdAndType(workerId, 2);
|
||||
} else {
|
||||
// 不传类型:查询所有区域
|
||||
byWorker = workerAreaService.getByWorker(workerId);
|
||||
}
|
||||
|
||||
// 收集只有cityId没有districtId的城市ID集合(用于单独处理)
|
||||
Set<Long> cityOnlyIds = new HashSet<>();
|
||||
// 按districtId分组收集cityIds
|
||||
Map<Long, Set<Long>> districtCityMap = new HashMap<>();
|
||||
|
||||
List<WorkerArea> byWorker = workerAreaService.getByWorker(workerId);
|
||||
for (WorkerArea area : byWorker){
|
||||
Long districtId = area.getDistrictId();
|
||||
|
||||
// 如果有districtId,按districtId分组处理
|
||||
if (districtId != null) {
|
||||
List<String> ids;
|
||||
if(countryMap.containsKey(districtId)){
|
||||
ids = countryMap.get(districtId);
|
||||
} else {
|
||||
ids = new ArrayList<>();
|
||||
}
|
||||
|
||||
// 收集该district下的城市ID
|
||||
if (area.getCityId() != null) {
|
||||
districtCityMap.computeIfAbsent(districtId, k -> new HashSet<>()).add(area.getCityId());
|
||||
}
|
||||
|
||||
// 添加街道ID
|
||||
if (area.getStreetId() != null) {
|
||||
ids.add(String.valueOf(area.getStreetId()));
|
||||
}
|
||||
|
||||
countryMap.put(districtId, ids);
|
||||
} else {
|
||||
// 如果只有cityId没有districtId,收集到cityOnlyIds
|
||||
if (area.getCityId() != null) {
|
||||
cityOnlyIds.add(area.getCityId());
|
||||
}
|
||||
List<String> ids;
|
||||
if(countryMap.containsKey(area.getDistrictId())){
|
||||
ids = countryMap.get(area.getDistrictId());
|
||||
}else {
|
||||
ids = new ArrayList<>();
|
||||
}
|
||||
ids.add(String.valueOf(area.getStreetId()));
|
||||
countryMap.put(area.getDistrictId(), ids);
|
||||
}
|
||||
|
||||
List<WorkerArea> result = new ArrayList<>();
|
||||
|
||||
// 处理有districtId的情况(正常流程)
|
||||
for (Map.Entry<Long, List<String>> longListEntry : countryMap.entrySet()) {
|
||||
WorkerArea model = new WorkerArea();
|
||||
Long districtId = longListEntry.getKey();
|
||||
SysArea countryArea = sysAreaService.selectById(districtId);
|
||||
SysArea countryArea = sysAreaService.selectById(longListEntry.getKey());
|
||||
model.setDistrictArea(countryArea);
|
||||
SysArea cityArea = sysAreaService.selectById(Long.valueOf(countryArea.getParentCode()));
|
||||
model.setCityArea(cityArea);
|
||||
SysArea provinceArea = sysAreaService.selectById(Long.valueOf(cityArea.getParentCode()));
|
||||
model.setProvinceArea(provinceArea);
|
||||
|
||||
// 设置该district下的cityIds
|
||||
Set<Long> districtCityIds = districtCityMap.getOrDefault(districtId, new HashSet<>());
|
||||
List<Long> cityIdList = new ArrayList<>(districtCityIds);
|
||||
model.setCityIds(cityIdList);
|
||||
|
||||
// 设置街道ID列表
|
||||
model.setStreetIds(longListEntry.getValue());
|
||||
|
||||
result.add(model);
|
||||
}
|
||||
|
||||
// 处理只有cityId没有districtId的情况(只返回省市信息)
|
||||
for (Long cityId : cityOnlyIds) {
|
||||
WorkerArea model = new WorkerArea();
|
||||
SysArea cityArea = sysAreaService.selectById(cityId);
|
||||
model.setCityArea(cityArea);
|
||||
// 获取省份信息
|
||||
SysArea provinceArea = sysAreaService.selectById(Long.valueOf(cityArea.getParentCode()));
|
||||
model.setProvinceArea(provinceArea);
|
||||
|
||||
// 只设置城市ID列表
|
||||
List<Long> cityIdList = new ArrayList<>();
|
||||
cityIdList.add(cityId);
|
||||
model.setCityIds(cityIdList);
|
||||
|
||||
// 不设置district和street信息
|
||||
result.add(model);
|
||||
}
|
||||
return AjaxResult.success(result);
|
||||
|
|
|
|||
|
|
@ -162,8 +162,6 @@ public class WorkerCertificationController extends BaseController
|
|||
@ResponseBody
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult appAddCertify(@RequestBody WorkerCertification request) {
|
||||
Worker worker = workerService.selectById(request.getWorkerId());
|
||||
request.setWorkerPhone(worker != null ? worker.getPhone() : null);
|
||||
// 将师傅状态设置为冻结
|
||||
// Worker worker = new Worker();
|
||||
// worker.setWorkerId(request.getWorkerId());
|
||||
|
|
|
|||
|
|
@ -85,16 +85,7 @@ public class WorkerController extends BaseController {
|
|||
Assert.notNull(worker.getPhone(), "手机号码为空");
|
||||
List<Worker> workerList = workerService.getWorkByPhoneAndPwd(worker);
|
||||
if(workerList.size() > 0){
|
||||
Worker loginWorker = workerList.get(0);
|
||||
// 检查师傅状态:0=生效,1=冻结,2=删除
|
||||
if(loginWorker.getStatus() == WorkerStatus.DELETED.getCode()){
|
||||
return AjaxResult.error("账户已被删除,无法登录!");
|
||||
}
|
||||
// 检查登录状态:0=允许登录,1=禁止登录
|
||||
if(loginWorker.getLoginStatus() != null && loginWorker.getLoginStatus() == 1){
|
||||
return AjaxResult.error("账户登录已被禁用,无法登录!");
|
||||
}
|
||||
return AjaxResult.success(loginWorker);
|
||||
return AjaxResult.success(workerList.get(0));
|
||||
}else {
|
||||
return AjaxResult.error("用户名或密码错误!");
|
||||
}
|
||||
|
|
@ -336,7 +327,6 @@ public class WorkerController extends BaseController {
|
|||
startPage();
|
||||
// worker.setWorkerIds(CollectionUtils.isNotEmpty(resWorkerIds) ? resWorkerIds : null);
|
||||
worker.setName(workerListRequest.getWorkerName());
|
||||
worker.setStatus(0);
|
||||
List<Worker> list = workerService.getWorkList(worker);
|
||||
list.forEach(w -> {
|
||||
Goods goods = new Goods();
|
||||
|
|
@ -421,106 +411,19 @@ public class WorkerController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
@PostMapping("/changeLoginStatus")
|
||||
@ResponseBody
|
||||
public AjaxResult changeLoginStatus(Worker worker){
|
||||
try {
|
||||
workerService.updateWorker(worker);
|
||||
return AjaxResult.success("登录状态修改成功");
|
||||
}catch (Exception e){
|
||||
logger.error(ExceptionUtil.getExceptionMessage(e));
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/settled")
|
||||
@ResponseBody
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult settled(@RequestBody WorkerSettledRequest request) {
|
||||
// 入驻服务区域信息持久化(类型1)
|
||||
workerAreaService.updateWorkerAreaByType(request.getWorkerId(), request.getWorkerAreas(), 1);
|
||||
|
||||
// 入驻商品区域信息持久化(类型2)
|
||||
workerAreaService.updateWorkerAreaByType(request.getWorkerId(), request.getWorkerGoodsAreas(), 2);
|
||||
|
||||
// 入驻服务类目信息持久化(类型1)
|
||||
if (request.getServiceCategories() != null && !request.getServiceCategories().isEmpty()) {
|
||||
// 使用对象数组方式
|
||||
workerGoodsCategoryService.updateWorkerGoodsCategoryByType(request.getWorkerId(), request.getServiceCategories(), 1);
|
||||
} else if (request.getServiceCategoryIds() != null && !request.getServiceCategoryIds().isEmpty()) {
|
||||
// 使用ID数组方式(兼容旧版本)
|
||||
List<WorkerGoodsCategory> serviceCategories = request.getServiceCategoryIds().stream()
|
||||
.map(id -> {
|
||||
WorkerGoodsCategory category = new WorkerGoodsCategory();
|
||||
category.setGoodsCategoryId(id);
|
||||
return category;
|
||||
}).collect(Collectors.toList());
|
||||
workerGoodsCategoryService.updateWorkerGoodsCategoryByType(request.getWorkerId(), serviceCategories, 1);
|
||||
}
|
||||
|
||||
// 入驻商品类目信息持久化(类型2)
|
||||
if (request.getGoodsCategories() != null && !request.getGoodsCategories().isEmpty()) {
|
||||
// 使用对象数组方式
|
||||
workerGoodsCategoryService.updateWorkerGoodsCategoryByType(request.getWorkerId(), request.getGoodsCategories(), 2);
|
||||
} else if (request.getGoodsCategoryIds() != null && !request.getGoodsCategoryIds().isEmpty()) {
|
||||
// 使用ID数组方式(兼容旧版本)
|
||||
List<WorkerGoodsCategory> goodsCategories = request.getGoodsCategoryIds().stream()
|
||||
.map(id -> {
|
||||
WorkerGoodsCategory category = new WorkerGoodsCategory();
|
||||
category.setGoodsCategoryId(id);
|
||||
return category;
|
||||
}).collect(Collectors.toList());
|
||||
workerGoodsCategoryService.updateWorkerGoodsCategoryByType(request.getWorkerId(), goodsCategories, 2);
|
||||
}
|
||||
|
||||
// 入驻区域信息持久化
|
||||
workerAreaService.updateWorkerServArea(request.getWorkerId(), request.getWorkerAreas());
|
||||
// 入驻服务品类信息持久化
|
||||
workerGoodsCategoryService.updateWorkerGoodsCategory(request.getWorkerId(), request.getGoodsCategories());
|
||||
// 更新师傅入驻类型为服务商
|
||||
Worker worker = new Worker();
|
||||
worker.setWorkerId(request.getWorkerId());
|
||||
worker.setType(WorkerType.SP.getCode());
|
||||
workerService.updateWorker(worker);
|
||||
|
||||
return AjaxResult.success("保存成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过师傅ID获取师傅详情
|
||||
* @param workerId 师傅ID
|
||||
* @return 师傅实体对象
|
||||
*/
|
||||
@GetMapping("/detail/{workerId}")
|
||||
@ResponseBody
|
||||
public AjaxResult getWorkerDetailById(@PathVariable("workerId") Long workerId) {
|
||||
try {
|
||||
// 参数校验
|
||||
if (workerId == null) {
|
||||
return AjaxResult.error("师傅ID不能为空");
|
||||
}
|
||||
|
||||
// 查询师傅基本信息
|
||||
Worker worker = workerService.selectById(workerId);
|
||||
if (worker == null) {
|
||||
return AjaxResult.error("师傅不存在");
|
||||
}
|
||||
|
||||
return AjaxResult.success(worker);
|
||||
} catch (Exception e) {
|
||||
logger.error("获取师傅详情失败: " + ExceptionUtil.getExceptionMessage(e));
|
||||
return AjaxResult.error("获取师傅详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除师傅(软删除)
|
||||
*/
|
||||
@RequiresPermissions("worker:worker:remove")
|
||||
@PostMapping("/remove")
|
||||
@ResponseBody
|
||||
public AjaxResult remove(String ids) {
|
||||
try {
|
||||
return toAjax(workerService.deleteWorkerByIds(ids));
|
||||
} catch (Exception e) {
|
||||
logger.error(ExceptionUtil.getExceptionMessage(e));
|
||||
return AjaxResult.error("删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,20 +71,11 @@ public class WorkerGoodsCategoryController extends BaseController {
|
|||
* 查询某个师傅的所有服务类目
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
* @param serviceType 服务类型:1=服务, 2=商品,不传则查询所有
|
||||
*/
|
||||
@GetMapping("worker")
|
||||
@ResponseBody
|
||||
public AjaxResult getByWorker(Long workerId, Integer serviceType) {
|
||||
List<WorkerGoodsCategory> list;
|
||||
if (serviceType != null) {
|
||||
// 根据服务类型查询
|
||||
list = workerGoodsCategoryService.getByWorkerIdAndType(workerId, serviceType);
|
||||
} else {
|
||||
// 查询所有类目(保持向后兼容)
|
||||
list = workerGoodsCategoryService.getByWorker(workerId);
|
||||
}
|
||||
|
||||
public AjaxResult getByWorker(Long workerId) {
|
||||
List<WorkerGoodsCategory> list = workerGoodsCategoryService.getByWorker(workerId);
|
||||
for (WorkerGoodsCategory item: list) {
|
||||
List<String> nameList = new ArrayList<String>();
|
||||
// 查询所有父级服务类目,拼接服务名称
|
||||
|
|
@ -98,75 +89,14 @@ public class WorkerGoodsCategoryController extends BaseController {
|
|||
}
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个师傅的服务类目(类型1)
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
*/
|
||||
@GetMapping("worker/service")
|
||||
@ResponseBody
|
||||
public AjaxResult getServiceCategoriesByWorker(Long workerId) {
|
||||
List<WorkerGoodsCategory> list = workerGoodsCategoryService.getByWorkerIdAndType(workerId, 1);
|
||||
for (WorkerGoodsCategory item: list) {
|
||||
List<String> nameList = new ArrayList<String>();
|
||||
// 查询所有父级服务类目,拼接服务名称
|
||||
GoodsCategory goodsCategory = goodsCategoryService.selectById(item.getGoodsCategoryId());
|
||||
while (goodsCategory.getParentCategoryId() != null) {
|
||||
nameList.add(goodsCategory.getGoodsCategoryName());
|
||||
goodsCategory = goodsCategoryService.selectById(goodsCategory.getParentCategoryId());
|
||||
}
|
||||
Collections.reverse(nameList);
|
||||
item.setMergeName(StringUtils.join(nameList, Constants.JOIN_SYMBOL));
|
||||
}
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个师傅的商品类目(类型2)
|
||||
*
|
||||
* @param workerId 师傅ID
|
||||
*/
|
||||
@GetMapping("worker/goods")
|
||||
@ResponseBody
|
||||
public AjaxResult getGoodsCategoriesByWorker(Long workerId) {
|
||||
List<WorkerGoodsCategory> list = workerGoodsCategoryService.getByWorkerIdAndType(workerId, 2);
|
||||
for (WorkerGoodsCategory item: list) {
|
||||
List<String> nameList = new ArrayList<String>();
|
||||
// 查询所有父级服务类目,拼接服务名称
|
||||
GoodsCategory goodsCategory = goodsCategoryService.selectById(item.getGoodsCategoryId());
|
||||
while (goodsCategory.getParentCategoryId() != null) {
|
||||
nameList.add(goodsCategory.getGoodsCategoryName());
|
||||
goodsCategory = goodsCategoryService.selectById(goodsCategory.getParentCategoryId());
|
||||
}
|
||||
Collections.reverse(nameList);
|
||||
item.setMergeName(StringUtils.join(nameList, Constants.JOIN_SYMBOL));
|
||||
}
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
|
||||
@GetMapping("worker/edit")
|
||||
@ResponseBody
|
||||
public AjaxResult getEditWorker(Long workerId, Integer type) {
|
||||
public AjaxResult getEditWorker(Long workerId) {
|
||||
Map<Long, List<String>> twoMap = new HashMap<>();
|
||||
|
||||
// 根据类型查询对应的商品分类数据
|
||||
List<WorkerGoodsCategory> list;
|
||||
if (Integer.valueOf(1).equals(type)) {
|
||||
// 服务类型:查询服务商品分类
|
||||
list = workerGoodsCategoryService.getByWorkerIdAndType(workerId, 1);
|
||||
} else if (Integer.valueOf(2).equals(type)) {
|
||||
// 商品类型:查询商品分类
|
||||
list = workerGoodsCategoryService.getByWorkerIdAndType(workerId, 2);
|
||||
} else {
|
||||
// 不传类型:查询所有商品分类
|
||||
list = workerGoodsCategoryService.getByWorker(workerId);
|
||||
}
|
||||
|
||||
List<WorkerGoodsCategory> list = workerGoodsCategoryService.getByWorker(workerId);
|
||||
if(CollectionUtils.isEmpty(list)){
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
for (WorkerGoodsCategory item: list) {
|
||||
List<String> ids;
|
||||
GoodsCategory goodsCategory = goodsCategoryService.selectById(item.getGoodsCategoryId());
|
||||
|
|
@ -178,7 +108,6 @@ public class WorkerGoodsCategoryController extends BaseController {
|
|||
ids.add(String.valueOf(goodsCategory.getGoodsCategoryId()));
|
||||
twoMap.put(goodsCategory.getParentCategoryId(), ids);
|
||||
}
|
||||
|
||||
List<WorkerGoodsCategory> result = new ArrayList<>();
|
||||
for (Map.Entry<Long, List<String>> longListEntry : twoMap.entrySet()) {
|
||||
WorkerGoodsCategory model = new WorkerGoodsCategory();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import java.math.BigDecimal;
|
|||
|
||||
/**
|
||||
* 改价请求实体
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
|
|
@ -22,10 +21,4 @@ public class OrderChangePriceRequest implements Serializable {
|
|||
private Integer type;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String urls;
|
||||
|
||||
private String fileNames;
|
||||
|
||||
private String reason;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,4 @@ public class OrderListRequest {
|
|||
private Integer timeout;
|
||||
|
||||
private Boolean needImgs = true;
|
||||
|
||||
private Integer orderType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@ package com.ghy.web.pojo.vo;
|
|||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ghy.common.annotation.Excel;
|
||||
import com.ghy.goods.domain.Goods;
|
||||
import com.ghy.goods.domain.GoodsArea;
|
||||
import com.ghy.goods.domain.InsuranceManager;
|
||||
import com.ghy.order.domain.AfterServiceRecord;
|
||||
import com.ghy.payment.domain.FinancialChangeRecord;
|
||||
import com.ghy.payment.domain.OrderTimeoutRecord;
|
||||
import com.ghy.shop.domain.Shop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -33,16 +30,12 @@ public class OrderListResponse {
|
|||
|
||||
private String orderMasterCode;
|
||||
|
||||
private String originalOrderMasterCode;
|
||||
|
||||
private Long orderDetailId;
|
||||
|
||||
private String orderDetailCode;
|
||||
|
||||
private Long workerId;
|
||||
|
||||
private Long masterWorkerId;
|
||||
|
||||
private String workerName;
|
||||
|
||||
private String workerPhone;
|
||||
|
|
@ -53,8 +46,6 @@ public class OrderListResponse {
|
|||
|
||||
private String masterCompanyName;
|
||||
|
||||
private String masterCompanyPhone;
|
||||
|
||||
private String customerName;
|
||||
|
||||
private String customerPhone;
|
||||
|
|
@ -91,11 +82,6 @@ public class OrderListResponse {
|
|||
|
||||
private Integer payStatus;
|
||||
|
||||
/**
|
||||
* 退款支付状态:0=未支付,1=已支付
|
||||
*/
|
||||
private Integer refundPayStatus;
|
||||
|
||||
private Integer payType;
|
||||
|
||||
private BigDecimal totalMoney;
|
||||
|
|
@ -177,12 +163,6 @@ public class OrderListResponse {
|
|||
*/
|
||||
private Integer timeoutFineTimes;
|
||||
|
||||
/**
|
||||
* 超时时间(用于排序)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date timeoutTime;
|
||||
|
||||
private Integer afterTimeout;
|
||||
|
||||
private String orderMode;
|
||||
|
|
@ -218,122 +198,4 @@ public class OrderListResponse {
|
|||
|
||||
private Long countryId;
|
||||
|
||||
/**
|
||||
* 是否为监控但
|
||||
* */
|
||||
private Boolean isMonitoredOrder;
|
||||
|
||||
private BigDecimal addMoney;
|
||||
|
||||
private String addMoneyRemark;
|
||||
|
||||
private BigDecimal paymentMoney;
|
||||
|
||||
/**
|
||||
* 是否已派发服务订单:0=未派发,1=已派发
|
||||
*/
|
||||
private Integer hasServiceOrder;
|
||||
|
||||
/**
|
||||
* 下单图片
|
||||
*/
|
||||
@Excel(name = "下单图片", cellType = Excel.ColumnType.STRING)
|
||||
private String orderImages;
|
||||
|
||||
/**
|
||||
* 是否发货到服务店:0=否,1=是
|
||||
*/
|
||||
@Excel(name = "是否发货到服务店", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Integer isDeliveryToStore;
|
||||
|
||||
/**
|
||||
* 是否已开票:0=是,1=否
|
||||
*/
|
||||
@Excel(name = "是否已开票", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=是,1=否")
|
||||
private Integer isInvoiced;
|
||||
|
||||
/**
|
||||
* 是否需要开票:0=不需要,1=需要
|
||||
*/
|
||||
@Excel(name = "是否需要开票", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=不需要,1=需要")
|
||||
private Integer isNeedBill;
|
||||
|
||||
/**
|
||||
* 原师傅id(转单前的师傅)
|
||||
*/
|
||||
@Excel(name = "原师傅id", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Long originalWorkerId;
|
||||
|
||||
|
||||
private Long serverGoodsId;
|
||||
|
||||
/**
|
||||
* 服务店铺ID
|
||||
*/
|
||||
private Long serviceShopId;
|
||||
|
||||
private Date confirmStartTime;
|
||||
|
||||
private Integer deliveryType;
|
||||
|
||||
private Long goodsId;
|
||||
|
||||
|
||||
/**
|
||||
* 发货备注
|
||||
*/
|
||||
@Excel(name = "发货备注", cellType = Excel.ColumnType.STRING)
|
||||
private String deliveryRemark;
|
||||
|
||||
/**
|
||||
* 发货图片
|
||||
*/
|
||||
@Excel(name = "发货图片", cellType = Excel.ColumnType.STRING)
|
||||
private String deliveryImages;
|
||||
|
||||
@Excel(name = "交货图片", cellType = Excel.ColumnType.STRING)
|
||||
private String handoverImages;
|
||||
|
||||
@Excel(name = "交货备注", cellType = Excel.ColumnType.STRING)
|
||||
private String handoverRemark;
|
||||
|
||||
/**
|
||||
* 快递单号
|
||||
*/
|
||||
private String trackingNumber;
|
||||
|
||||
|
||||
/**
|
||||
* 分账倒计时结束时间
|
||||
*/
|
||||
@Excel(name = "分账倒计时结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date shareAccountCountdownEndTime;
|
||||
|
||||
/**
|
||||
* 分账倒计时时长(小时)
|
||||
*/
|
||||
@Excel(name = "分账倒计时时长(小时)")
|
||||
private Integer shareAccountCountdownDuration;
|
||||
|
||||
private Shop shop;
|
||||
|
||||
private Shop serviceShop;
|
||||
private Long goodsOrderMasterId;
|
||||
|
||||
/**
|
||||
* 是否已撤销服务主单:0=未撤销,1=已撤销
|
||||
*/
|
||||
@Excel(name = "是否已撤销服务主单", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=未撤销,1=已撤销")
|
||||
private Integer serviceCancelled;
|
||||
|
||||
/**
|
||||
* 是否显示售后记录:0=不显示,1=显示
|
||||
*/
|
||||
private Integer showAfterServiceRecord = 0;
|
||||
|
||||
@Excel(name = "是否显示在监控单", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=不显示,1=显示在监控单")
|
||||
private Integer showInMonitor;
|
||||
|
||||
@Excel(name = "售后状态:0-无售后,1-售后纠纷,2-售后已完成,3-售后已取消")
|
||||
private Integer afterPlatformServiceStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,56 +82,4 @@ public class OrderStandardDetail {
|
|||
|
||||
private Integer afterTimeout;
|
||||
|
||||
private BigDecimal addMoney;
|
||||
|
||||
/**
|
||||
* 退单原因
|
||||
*/
|
||||
private String returnReason;
|
||||
|
||||
/**
|
||||
* 退单原因详情
|
||||
*/
|
||||
private String returnReasonDetail;
|
||||
|
||||
/**
|
||||
* 退单图片
|
||||
*/
|
||||
private String returnImages;
|
||||
|
||||
|
||||
/**
|
||||
* 发货类型 - 订单的发货方式
|
||||
*/
|
||||
private Integer deliveryType;
|
||||
|
||||
/**
|
||||
* 发货备注 - 发货相关备注信息
|
||||
*/
|
||||
private String deliveryRemark;
|
||||
|
||||
/**
|
||||
* 发货图片 - 发货凭证图片
|
||||
*/
|
||||
private String deliveryImages;
|
||||
|
||||
/**
|
||||
* 快递单号 - 物流跟踪单号
|
||||
*/
|
||||
private String trackingNumber;
|
||||
|
||||
|
||||
|
||||
private String handoverImages;
|
||||
|
||||
|
||||
private String handoverRemark;
|
||||
|
||||
private String orderImages;
|
||||
//= "售后状态:0-无售后,1-售后纠纷,2-售后已完成,3-售后已取消"
|
||||
private Integer afterPlatformServiceStatus;
|
||||
|
||||
private Integer payStatus;
|
||||
|
||||
private Integer refundPayStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,4 @@ public class OrderStatisticsRequest {
|
|||
private Long workerId;
|
||||
|
||||
private Long deptId;
|
||||
|
||||
private Integer orderType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,21 +21,9 @@ public class WorkerSettledRequest {
|
|||
// 入驻区域
|
||||
private List<WorkerArea> workerAreas;
|
||||
|
||||
// 入驻商品区域
|
||||
private List<WorkerArea> workerGoodsAreas;
|
||||
|
||||
// 入驻服务类目
|
||||
private List<WorkerGoodsCategory> serviceCategories;
|
||||
|
||||
// 入驻商品类目
|
||||
// 服务品类
|
||||
private List<WorkerGoodsCategory> goodsCategories;
|
||||
|
||||
// 入驻服务类目ID列表(兼容旧版本)
|
||||
private List<Long> serviceCategoryIds;
|
||||
|
||||
// 入驻商品类目ID列表(兼容旧版本)
|
||||
private List<Long> goodsCategoryIds;
|
||||
|
||||
// 特殊技能
|
||||
// private List<WorkerSpecialSkill> specialSkills;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ qiniu:
|
|||
accessKey: 'QTNOppkvtufxTxLjt1V7YZwvzV2Rc6WLD5yXLBVY'
|
||||
secretKey: 'V8SM9nkbO-dft4JmG7UaCH6RYxXdqzrvQ0zWO2W3'
|
||||
bucketName: 'gqz'
|
||||
mediaUrl: 'https://gqz.opsoul.com/'
|
||||
mediaUrl: 'http://gqz.opsoul.com/'
|
||||
|
||||
adapay:
|
||||
debug: true
|
||||
|
|
@ -130,7 +130,7 @@ jim:
|
|||
|
||||
# 百度地图应用api
|
||||
baidu:
|
||||
ak: 'i0sdnIsMmJVik7vJhfqMHA6DmS6d0fMB'
|
||||
ak: 'ZQTgMW7W0GTuE7Ripb0HDp5TqRaOI6PZ'
|
||||
url: 'https://api.map.baidu.com/reverse_geocoding/v3/?ak=#AK#&output=json&coordtype=wgs84ll&location='
|
||||
|
||||
sms:
|
||||
|
|
@ -145,24 +145,3 @@ aliyun:
|
|||
accessSecret: EV4dzWRfKTQaPRjf3tFziMuVBCsThU
|
||||
endpoint: dytnsapi.aliyuncs.com
|
||||
authCode: od2FgE9a9g
|
||||
|
||||
# 物流API配置
|
||||
# logistics:
|
||||
# kdniao:
|
||||
# # 快递鸟 API配置
|
||||
# appId: '1889454' # 快递鸟应用ID,需要到快递鸟官网申请
|
||||
# appKey: 'b2483529-807d-49af-b0e1-1ed218baa4db' # 快递鸟API密钥,需要到快递鸟官网申请
|
||||
# url: 'https://api.kdniao.com/api/dist' # 快递鸟即时查询接口地址
|
||||
# 快递鸟支持两种接口:
|
||||
# 1. 即时查询接口:https://api.kdniao.com/api/dist
|
||||
# 2. 物流跟踪接口:https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx
|
||||
|
||||
logistics:
|
||||
# 阿里云快递查询API配置
|
||||
aliyun:
|
||||
# 阿里云API网关AppCode,需要在阿里云市场购买快递查询服务后获取
|
||||
appCode: "78064b8aaa294f64ab07a285f129aea6"
|
||||
# API主机地址
|
||||
host: "https://kzexpress.market.alicloudapi.com"
|
||||
# API路径
|
||||
path: "/api-mall/api/express/query"
|
||||
|
|
@ -273,7 +273,8 @@
|
|||
var _defaultRootFlag = item[options.parentCode] == '0' ||
|
||||
item[options.parentCode] == 0 ||
|
||||
item[options.parentCode] == null ||
|
||||
item[options.parentCode] == '';
|
||||
item[options.parentCode] == '' ||
|
||||
$.inArray(item[options.code], parentCodes) > 0 && !rootFlag;
|
||||
if (!item[options.parentCode] || (_root ? (item[options.parentCode] == options.rootIdValue) : _defaultRootFlag)) {
|
||||
rootFlag = true;
|
||||
if (!target.data_list["_root_"]) {
|
||||
|
|
|
|||
|
|
@ -99,39 +99,10 @@
|
|||
|
||||
function submitHandler() {
|
||||
if ($.validate.form()) {
|
||||
// 方法一:使用saveModal,新增成功后不刷新表格,只显示提示信息
|
||||
//$.operate.saveModal(prefix + "/add", $('#form-dept-add').serialize());
|
||||
|
||||
// 方法二:使用自定义Ajax保存(如果需要更多控制)
|
||||
customSave();
|
||||
$.operate.save(prefix + "/add", $('#form-dept-add').serialize());
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义保存方法(可选)
|
||||
function customSave() {
|
||||
$.ajax({
|
||||
url: prefix + "/add",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: $('#form-dept-add').serialize(),
|
||||
beforeSend: function () {
|
||||
$.modal.loading("正在处理中,请稍候...");
|
||||
},
|
||||
success: function(result) {
|
||||
$.modal.closeLoading();
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.alertSuccess(result.msg);
|
||||
// 如果需要关闭窗口,取消注释下面这行
|
||||
$.modal.close();
|
||||
} else if (result.code == web_status.WARNING) {
|
||||
$.modal.alertWarning(result.msg);
|
||||
} else {
|
||||
$.modal.alertError(result.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*部门管理-新增-选择父部门树*/
|
||||
function selectDeptTree() {
|
||||
var treeId = $("#treeId").val();
|
||||
|
|
|
|||
|
|
@ -124,29 +124,8 @@
|
|||
|
||||
function remove(id) {
|
||||
$.modal.confirm("确认要删除吗", function () {
|
||||
customRemove(id);
|
||||
});
|
||||
}
|
||||
|
||||
function customRemove(id) {
|
||||
$.ajax({
|
||||
url: prefix + '/remove/' + id,
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
beforeSend: function () {
|
||||
$.modal.loading("正在处理中,请稍候...");
|
||||
},
|
||||
success: function(result) {
|
||||
$.modal.closeLoading();
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.alertSuccess(result.msg);
|
||||
} else if (result.code == web_status.WARNING) {
|
||||
$.modal.alertWarning(result.msg);
|
||||
} else {
|
||||
$.modal.alertError(result.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
$.operate.post(prefix + '/remove/' + id);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -48,8 +48,7 @@
|
|||
<label class="col-sm-3 control-label">类目类别:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="radio-box" th:each="dict : ${@dict.getType('goods_category_type')}">
|
||||
<input type="radio" th:id="${dict.dictCode}" name="type" th:value="${dict.dictValue}"
|
||||
th:checked="${goodsCategory.type != null ? goodsCategory.type.toString() == dict.dictValue : dict.default}">
|
||||
<input type="radio" th:id="${dict.dictCode}" name="type" th:value="${dict.dictValue}" th:checked="${dict.default}">
|
||||
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}"></label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -119,36 +118,10 @@
|
|||
var data = $("#form-dept-edit").serializeArray();
|
||||
var insuranceIds = $.form.selectCheckeds("insurance");
|
||||
data.push({"name": "insuranceIds", "value": insuranceIds});
|
||||
|
||||
// 自定义保存,不刷新表格
|
||||
customEditSave(data);
|
||||
$.operate.save(prefix + "/edit", data);
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义编辑保存方法
|
||||
function customEditSave(data) {
|
||||
$.ajax({
|
||||
url: prefix + "/edit",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: data,
|
||||
beforeSend: function () {
|
||||
$.modal.loading("正在处理中,请稍候...");
|
||||
},
|
||||
success: function(result) {
|
||||
$.modal.closeLoading();
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.alertSuccess(result.msg);
|
||||
$.modal.close();
|
||||
} else if (result.code == web_status.WARNING) {
|
||||
$.modal.alertWarning(result.msg);
|
||||
} else {
|
||||
$.modal.alertError(result.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*部门管理-修改-选择部门树*/
|
||||
function selectDeptTree() {
|
||||
var treeId = $("#treeId").val();
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:include="include :: header('选择服务类目')" />
|
||||
<th:block th:include="include :: ztree-css" />
|
||||
</head>
|
||||
<body class="white-bg">
|
||||
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
|
||||
<form class="form-horizontal m" id="form-tree">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">服务类目:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" id="treeName" readonly="true">
|
||||
<input type="hidden" id="treeId">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label"></label>
|
||||
<div class="col-sm-8">
|
||||
<div id="tree" class="ztree"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<div class="form-control-static col-sm-offset-9">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</form>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: ztree-js" />
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var url = ctx + "goods/category/serviceTreeData";
|
||||
var options = {
|
||||
url: url,
|
||||
expandLevel: 2,
|
||||
onClick : zOnClick
|
||||
};
|
||||
$.tree.init(options);
|
||||
});
|
||||
|
||||
function zOnClick(event, treeId, treeNode) {
|
||||
$("#treeId").val(treeNode.id);
|
||||
$("#treeName").val(treeNode.name);
|
||||
}
|
||||
|
||||
function submitHandler() {
|
||||
var treeId = $("#treeId").val();
|
||||
var treeName = $("#treeName").val();
|
||||
if (treeId == "" || treeName == "") {
|
||||
$.modal.alertWarning("请选择服务类目");
|
||||
return;
|
||||
}
|
||||
var index = parent.layer.getFrameIndex(window.name);
|
||||
parent.layer.close(index);
|
||||
}
|
||||
|
||||
function closeItem() {
|
||||
var index = parent.layer.getFrameIndex(window.name);
|
||||
parent.layer.close(index);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -145,7 +145,9 @@
|
|||
formatter: function (value, row, index) {
|
||||
if (row.parentId !== 0) {
|
||||
var actions = [];
|
||||
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.deptGoodsCategoryId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
|
||||
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.editTab(\'' + row.deptGoodsCategoryId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
|
||||
// actions.push('<a class="btn btn-info btn-xs ' + addFlag + '" href="javascript:void(0)" onclick="$.operate.add(\'' + row.deptGoodsCategoryId + '\')"><i class="fa fa-plus"></i>新增</a> ');
|
||||
// actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="remove(\'' + row.deptGoodsCategoryId + '\')"><i class="fa fa-trash"></i>删除</a>');
|
||||
return actions.join('');
|
||||
} else {
|
||||
return "";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
<div class="main-content">
|
||||
<form class="form-horizontal" id="form-deptGoodsCategory-edit" th:object="${deptGoodsCategory}">
|
||||
<input id="deptGoodsCategoryId" name="deptGoodsCategoryId" type="hidden" th:field="*{deptGoodsCategoryId}"/>
|
||||
<input id="type" name="type" type="hidden" th:field="*{type}"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
|
|
@ -24,19 +23,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6" th:if="*{type != 1}">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-6 control-label">服务类目:</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" onclick="selectServiceCategoryTree()" id="treeName" readonly="true" th:value="*{serviceCategoryName}">
|
||||
<input type="hidden" id="treeId" name="serviceCategoryId" th:value="*{serviceCategoryId}">
|
||||
<input type="hidden" id="serviceCategoryName" name="serviceCategoryName" th:value="*{serviceCategoryName}">
|
||||
<span class="input-group-addon"><i class="fa fa-search"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
|
|
@ -252,15 +238,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-6 control-label">倒计时小时数:</label>
|
||||
<div class="col-sm-6">
|
||||
<input name="countdownHours" placeholder="请输入倒计时小时数" class="form-control" type="number" min="1" max="168"
|
||||
th:field="*{countdownHours}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
|
|
@ -398,26 +375,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
/*选择服务类目树*/
|
||||
function selectServiceCategoryTree() {
|
||||
var treeId = $("#treeId").val();
|
||||
var options = {
|
||||
title: '服务类目选择',
|
||||
width: "380",
|
||||
url: ctx + "goods/category/selectServiceCategoryTree",
|
||||
callBack: doSubmit
|
||||
};
|
||||
$.modal.openOptions(options);
|
||||
}
|
||||
|
||||
function doSubmit(index, layero) {
|
||||
var body = $.modal.getChildFrame(index);
|
||||
$("#treeId").val(body.find('#treeId').val());
|
||||
$("#treeName").val(body.find('#treeName').val());
|
||||
$("#serviceCategoryName").val(body.find('#treeName').val());
|
||||
$.modal.close(index);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<script th:src="@{/ajax/libs/validate/jquery.validate.extend.js?v=1.19.3}"></script>
|
||||
<script th:src="@{/ajax/libs/validate/messages_zh.js?v=1.19.3}"></script>
|
||||
<!-- bootstrap-table 表格树插件 -->
|
||||
<script th:src="@{/ajax/libs/bootstrap-table/extensions/tree/bootstrap-table-tree.js?v=1.18.3}"></script>
|
||||
<script th:src="@{/ajax/libs/bootstrap-table/extensions/tree/bootstrap-table-tree.min.js?v=1.18.3}"></script>
|
||||
<!-- 遮罩层 -->
|
||||
<script th:src="@{/ajax/libs/blockUI/jquery.blockUI.js?v=2.70.0}"></script>
|
||||
<script th:src="@{/ajax/libs/iCheck/icheck.min.js?v=1.0.3}"></script>
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@
|
|||
</a>
|
||||
<a class="btn btn-default btn-outline" onclick="selectConditionBtn(this, {orderStatusName : '售后纠纷', orderStatus: -1})">
|
||||
售后纠纷
|
||||
(<span id="afterServiceDisputeNum">0</span>)
|
||||
(<span>0</span>)
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-board">
|
||||
|
|
@ -553,20 +553,6 @@
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 售后纠纷数量统计
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType:"json",
|
||||
url: prefix + '/after/dispute/count',
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$('#afterServiceDisputeNum').text(result.data);
|
||||
} else {
|
||||
$.modal.msgError("售后纠纷数量加载错误,请重试!")
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function changeOrderMode(orderMode) {
|
||||
|
|
@ -701,14 +687,10 @@
|
|||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="callDetail(\'' + row.orderMasterId + '\')"><i class="fa fa-info"></i>拨号详情</a> ');
|
||||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="orderDetailReject(\'' + row.id + '\')"></i>师傅退单</a> ');
|
||||
// actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="orderMasterReject(\'' + row.orderMasterId + '\')"></i>商家退单</a> ');
|
||||
// actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="showOrderWorker(\'' + row.id + '\')"></i>指派</a> ');
|
||||
<!-- actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="showOrderWorker(\'' + row.id + '\')"></i>指派</a> ');-->
|
||||
if (row.payStatus == 0) {
|
||||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="showPayQrcode(\'' + row.id + '\')"></i>付款</a> ');
|
||||
}
|
||||
// 售后纠纷处理按钮
|
||||
if (row.afterServiceStatus == 1) {
|
||||
actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" onclick="handleAfterServiceDispute(\'' + row.id + '\')"><i class="fa fa-gavel"></i>售后纠纷处理</a> ');
|
||||
}
|
||||
return actions.join('');
|
||||
}
|
||||
},
|
||||
|
|
@ -721,29 +703,6 @@
|
|||
field: 'orderImgs',
|
||||
title: '完单图片'
|
||||
},
|
||||
{
|
||||
field: 'orderImages',
|
||||
title: '下单图片',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
if (value && value.trim() !== '') {
|
||||
return '<img src="' + value + '" style="width: 50px; height: 50px; object-fit: cover;" onclick="showImagePreview(\'' + value + '\')">';
|
||||
}
|
||||
return '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'isDeliveryToStore',
|
||||
title: '是否发货到服务店',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
if (value === 1) {
|
||||
return '<span class="label label-success">是</span>';
|
||||
} else {
|
||||
return '<span class="label label-default">否</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'orderType',
|
||||
title: '订单类型',
|
||||
|
|
@ -973,276 +932,12 @@
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 设置定时器,每隔一分钟执行一次 fetchData 函数
|
||||
setInterval(fetchData, 60000);
|
||||
|
||||
// 图片预览功能
|
||||
function showImagePreview(imageUrl) {
|
||||
if (!imageUrl || imageUrl.trim() === '') {
|
||||
$.modal.msgError("图片地址不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '图片预览',
|
||||
shadeClose: true,
|
||||
shade: 0.8,
|
||||
area: ['auto', 'auto'],
|
||||
content: '<div style="text-align: center; padding: 20px;"><img src="' + imageUrl + '" style="max-width: 800px; max-height: 600px; object-fit: contain;" /></div>'
|
||||
});
|
||||
}
|
||||
|
||||
// 售后纠纷处理
|
||||
function handleAfterServiceDispute(orderDetailId) {
|
||||
if (!orderDetailId) {
|
||||
$.modal.msgError("子单ID不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询该子单的售后纠纷记录
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: prefix + "/after/records/byDetailId",
|
||||
data: {orderDetailId: orderDetailId},
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
showAfterServiceDisputeModal(orderDetailId, result.data);
|
||||
} else {
|
||||
$.modal.msgError("查询售后纠纷记录失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$.modal.msgError("查询售后纠纷记录失败,请重试");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示售后纠纷处理弹窗
|
||||
function showAfterServiceDisputeModal(orderDetailId, disputeRecords) {
|
||||
if (!disputeRecords || disputeRecords.length === 0) {
|
||||
$.modal.msgError("没有找到售后纠纷记录");
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接获取订单详情来判断订单类型
|
||||
getOrderDetailInfo(orderDetailId, function(orderDetail) {
|
||||
showDisputeModalWithOrderInfo(orderDetailId, disputeRecords, orderDetail);
|
||||
});
|
||||
}
|
||||
|
||||
// 获取订单详情信息
|
||||
function getOrderDetailInfo(orderDetailId, callback) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
url: prefix + "/app/detail",
|
||||
data: JSON.stringify({id: orderDetailId}),
|
||||
success: function(result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
callback(result.data);
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 根据订单信息显示售后纠纷处理弹窗
|
||||
function showDisputeModalWithOrderInfo(orderDetailId, disputeRecords, orderDetail) {
|
||||
var isGoodsOrder = orderDetail && orderDetail.orderType === 1; // 1表示商品订单
|
||||
|
||||
var modalContent = '<div class="modal-body">';
|
||||
modalContent += '<div class="table-responsive">';
|
||||
modalContent += '<table class="table table-bordered table-striped">';
|
||||
modalContent += '<thead><tr>';
|
||||
modalContent += '<th>子单号</th>';
|
||||
modalContent += '<th>申请退款金额</th>';
|
||||
modalContent += '<th>师傅反馈</th>';
|
||||
modalContent += '<th>客户确认</th>';
|
||||
modalContent += '<th style="width: 280px;">操作</th>';
|
||||
modalContent += '</tr></thead><tbody>';
|
||||
|
||||
disputeRecords.forEach(function(record) {
|
||||
modalContent += '<tr>';
|
||||
modalContent += '<td>' + (record.orderDetailCode || record.id) + '</td>';
|
||||
modalContent += '<td>¥' + (record.refund || record.agreedRefund || 0) + '</td>';
|
||||
|
||||
// 师傅反馈列 - 如果是商品订单则增加workerAgreeType显示
|
||||
var workerFeedbackContent = getWorkerFeedbackText(record.workerFeedbackResult) + '<br/><span style="color: #666; font-size: 12px;">反馈金额:¥' + (record.agreedRefund || 0) + '</span>';
|
||||
if (isGoodsOrder && record.workerAgreeType) {
|
||||
workerFeedbackContent += '<br/><span style="color: #007bff; font-size: 12px;">处理方式:' + getWorkerAgreeTypeText(record.workerAgreeType) + '</span>';
|
||||
}
|
||||
modalContent += '<td>' + workerFeedbackContent + '</td>';
|
||||
|
||||
modalContent += '<td>' + getCustomerCheckText(record.customerFinalCheck) + '</td>';
|
||||
modalContent += '<td>';
|
||||
modalContent += '<div style="margin-bottom: 8px;">';
|
||||
modalContent += '<label style="font-size: 12px; color: #666; margin-bottom: 2px; display: block;">退款金额:</label>';
|
||||
modalContent += '<input type="number" class="form-control" id="refundAmount_' + record.id + '" placeholder="输入退款金额" value="' + (record.agreedRefund || 0) + '" style="width: 100%; font-size: 12px; height: 28px;">';
|
||||
modalContent += '</div>';
|
||||
modalContent += '<div style="margin-bottom: 8px;">';
|
||||
modalContent += '<label style="font-size: 12px; color: #666; margin-bottom: 2px; display: block;">处理原因:</label>';
|
||||
modalContent += '<input type="text" class="form-control" id="processReason_' + record.id + '" placeholder="请输入处理原因" style="width: 100%; font-size: 12px; height: 28px;">';
|
||||
modalContent += '</div>';
|
||||
modalContent += '<button class="btn btn-primary btn-xs" onclick="processRefund(' + record.id + ', ' + orderDetailId + ')" style="width: 100%; font-size: 12px; height: 28px;">处理退款</button>';
|
||||
modalContent += '<button class="btn btn-warning btn-xs" onclick="cancelAfterService(' + record.id + ', ' + orderDetailId + ')" style="width: 100%; font-size: 12px; height: 28px; margin-top: 4px;">取消售后</button>';
|
||||
modalContent += '</td>';
|
||||
modalContent += '</tr>';
|
||||
});
|
||||
|
||||
modalContent += '</tbody></table>';
|
||||
|
||||
// 如果是商品订单,在表格下方增加提示文字
|
||||
if (isGoodsOrder) {
|
||||
modalContent += '<div style="margin-top: 15px; padding: 10px; background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px;">';
|
||||
modalContent += '<i class="fa fa-exclamation-triangle" style="color: #856404; margin-right: 5px;"></i>';
|
||||
modalContent += '<span style="color: #856404; font-weight: bold;">商品退款需确认是否存在退货,并确认退完与否再操作退款!同时确认子单款项金额是否符合退款要求!</span>';
|
||||
modalContent += '</div>';
|
||||
}
|
||||
|
||||
modalContent += '</div>';
|
||||
modalContent += '</div>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '售后纠纷处理 - 子单ID: ' + orderDetailId,
|
||||
shadeClose: true,
|
||||
shade: 0.8,
|
||||
area: ['90%', '80%'],
|
||||
content: modalContent
|
||||
});
|
||||
}
|
||||
|
||||
// 获取师傅同意处理方式文本
|
||||
function getWorkerAgreeTypeText(type) {
|
||||
var typeMap = {
|
||||
1: '即时退单退款',
|
||||
2: '货物拦截后退单退款',
|
||||
3: '快递返回货物后退单退款',
|
||||
4: '退回货物后退单退款'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
}
|
||||
|
||||
// 处理退款
|
||||
function processRefund(recordId, orderDetailId) {
|
||||
var refundAmount = $('#refundAmount_' + recordId).val();
|
||||
var processReason = $('#processReason_' + recordId).val();
|
||||
|
||||
if (!refundAmount || refundAmount <= 0) {
|
||||
$.modal.msgError("请输入有效的退款金额");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!processReason || processReason.trim() === '') {
|
||||
$.modal.msgError("请输入处理原因");
|
||||
return;
|
||||
}
|
||||
|
||||
$.modal.confirm("确认处理退款吗?<br/>退款金额:¥" + refundAmount + "<br/>处理原因:" + processReason, function() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
url: ctx + "order/master/after/dispute/handle",
|
||||
data: JSON.stringify({
|
||||
recordId: recordId,
|
||||
orderDetailId: orderDetailId,
|
||||
refundAmount: refundAmount,
|
||||
platformHandleReason: processReason
|
||||
}),
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.msgSuccess("退款处理成功");
|
||||
// 关闭弹窗
|
||||
layer.closeAll();
|
||||
// 刷新表格
|
||||
$.table.refresh();
|
||||
} else {
|
||||
$.modal.msgError("退款处理失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$.modal.msgError("退款处理失败,请重试");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 取消售后
|
||||
function cancelAfterService(recordId, orderDetailId) {
|
||||
// 获取处理原因输入框的值
|
||||
var processReason = $('#processReason_' + recordId).val();
|
||||
|
||||
if (!processReason || processReason.trim() === '') {
|
||||
$.modal.msgError("请输入处理原因");
|
||||
return;
|
||||
}
|
||||
|
||||
$.modal.confirm("确认取消该售后申请吗?", function() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: ctx + "order/master/after/cancel",
|
||||
data: {
|
||||
afterServiceRecordId: recordId,
|
||||
platformHandleReason: processReason.trim()
|
||||
},
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.msgSuccess("取消售后成功");
|
||||
// 关闭弹窗
|
||||
layer.closeAll();
|
||||
// 刷新表格
|
||||
$.table.refresh();
|
||||
} else {
|
||||
$.modal.msgError("取消售后失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$.modal.msgError("取消售后失败,请重试");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 获取售后类型文本
|
||||
function getAfterServiceTypeText(type) {
|
||||
var typeMap = {
|
||||
1: '未收到货',
|
||||
2: '已收到货',
|
||||
3: '质量问题',
|
||||
4: '其他'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
}
|
||||
|
||||
// 获取师傅反馈文本
|
||||
function getWorkerFeedbackText(result) {
|
||||
var resultMap = {
|
||||
0: '拒绝',
|
||||
1: '同意',
|
||||
2: '上门补做/重做',
|
||||
3: '重做/补做完成'
|
||||
};
|
||||
return resultMap[result] || '未处理';
|
||||
}
|
||||
|
||||
// 获取客户确认文本
|
||||
function getCustomerCheckText(check) {
|
||||
var checkMap = {
|
||||
0: '不同意',
|
||||
1: '同意',
|
||||
2: '未处理'
|
||||
};
|
||||
return checkMap[check] || '未处理';
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -56,37 +56,6 @@
|
|||
border-color: #1c84c6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 表格滚动条样式 */
|
||||
.table-scroll-container {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
max-height: 600px;
|
||||
}
|
||||
|
||||
.table-scroll-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.table-scroll-container::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-scroll-container::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-scroll-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 表格最小宽度 */
|
||||
#bootstrap-table {
|
||||
min-width: 1000px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="gray-bg">
|
||||
|
|
@ -165,7 +134,7 @@
|
|||
</a>
|
||||
<a class="btn btn-default btn-outline" onclick="selectConditionBtn(this, {orderStatusName : '售后纠纷', orderStatus: -1})">
|
||||
售后纠纷
|
||||
(<span id="afterServiceDisputeNum">0</span>)
|
||||
(<span>0</span>)
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-board">
|
||||
|
|
@ -362,7 +331,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 select-table table-striped table-scroll-container">
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -573,22 +542,6 @@
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
<!-- 售后纠纷订单数量统计-->
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType:"json",
|
||||
url: prefix + '/after/dispute/count',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({}),
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$('#afterServiceDisputeNum').text(result.data);
|
||||
} else {
|
||||
$.modal.msgError("售后纠纷数据加载错误,请重试!")
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function changeOrderMode(orderMode) {
|
||||
|
|
@ -692,18 +645,17 @@
|
|||
field: 'goods',
|
||||
title: '订单信息',
|
||||
formatter: function (value, row) {
|
||||
var imgUrl = (value && value.goodsImgUrl) ? value.goodsImgUrl : '';
|
||||
return '<div style="display:flex;justify-content: center;align-items: center;">'
|
||||
+ '<img decoding="async" src="' + imgUrl + '" width="100" height="100" />'
|
||||
+ '<img decoding="async" src="' + value.goodsImgUrl + '" width="100" height="100" />'
|
||||
+ '<div>'
|
||||
+ '<p>' + (row.code || '') + '</p>'
|
||||
+ '<p>' + (row.consoleGoodsName || '') + '</p>'
|
||||
+ '<p>联系人:' + (row.addressName || '') + '</p>'
|
||||
+ '<p>联系电话:' + (row.addressPhone || '') + '</p>'
|
||||
+ '<p>联系地址:' + (row.address || '') + '</p>'
|
||||
+ '<p>下单时间:' + (row.createTime || '') + '</p>'
|
||||
+ '<p>预约时间:' + (row.mixExpectTime || '') + '</p>'
|
||||
+ '<p>下单总金额:' + (row.financialMasterMoney || '') + '元,师傅实收金额: ' + (row.financialMasterPayMoney || '') + '</p>'
|
||||
+ '<p>' + row.code + ' + <p/>'
|
||||
+ '<p> ' + row.consoleGoodsName + '<p/>'
|
||||
+ '<p> 联系人:' + row.addressName + '</p>'
|
||||
+ '<p> 联系电话:' + row.addressPhone + '</p>'
|
||||
+ '<p> 联系地址:' + row.address + '</p>'
|
||||
+ '<p> 下单时间:' + row.createTime + '</p>'
|
||||
+ '<p> 预约时间:' + row.mixExpectTime + '</p>'
|
||||
+ '<p> 下单总金额:' + row.financialMasterMoney + '元,师傅实收金额: '+ row.financialMasterPayMoney + ' </p>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
}
|
||||
|
|
@ -773,10 +725,6 @@
|
|||
} else {
|
||||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="pcOrderInsurance(\'' + row.id + '\')"></i>选择保险</a> ');
|
||||
}
|
||||
// 售后纠纷处理按钮
|
||||
if (row.afterServiceStatus == 1) {
|
||||
actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" onclick="handleAfterServiceDispute(\'' + row.id + '\')"><i class="fa fa-gavel"></i>售后纠纷处理</a> ');
|
||||
}
|
||||
return actions.join('');
|
||||
}
|
||||
},
|
||||
|
|
@ -842,44 +790,6 @@
|
|||
{
|
||||
field: 'payTime',
|
||||
title: '付款时间'
|
||||
},
|
||||
{
|
||||
field: 'returnReason',
|
||||
title: '退单原因',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
return value || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'returnReasonDetail',
|
||||
title: '退单原因详情',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
if (value && value.length > 20) {
|
||||
return value.substring(0, 20) + '...';
|
||||
}
|
||||
return value || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'returnImages',
|
||||
title: '退单图片',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
if (value) {
|
||||
var images = value.split(',');
|
||||
var html = '';
|
||||
for (var i = 0; i < Math.min(images.length, 3); i++) {
|
||||
html += '<img src="' + images[i] + '" style="width:30px;height:30px;margin:2px;" onclick="showImage(\'' + images[i] + '\')" />';
|
||||
}
|
||||
if (images.length > 3) {
|
||||
html += '<span style="color:#999;">+' + (images.length - 3) + '</span>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
return '-';
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
|
|
@ -975,21 +885,12 @@
|
|||
}
|
||||
|
||||
function orderMasterReject(id) {
|
||||
$.modal.confirm("确定要退单吗?", function() {
|
||||
const url = "console/cancel";
|
||||
const data = { "id": id };
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: url,
|
||||
data: JSON.stringify(data),
|
||||
contentType: 'application/json',
|
||||
success: function (result) {
|
||||
$.operate.ajaxSuccess(result);
|
||||
}
|
||||
$.modal.confirm("确定要退单吗?", function() {
|
||||
const url = "console/cancel";
|
||||
const data = { "id": id };
|
||||
$.operate.post(url, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function changeConditionBtnChosenStyle(e) {
|
||||
$('.condition-btn .btn').removeClass('active-condition-btn');
|
||||
|
|
@ -1133,26 +1034,6 @@
|
|||
showPayQrcode(rows.join());
|
||||
}
|
||||
|
||||
// 显示退单图片
|
||||
function showImage(imageUrl) {
|
||||
if (!imageUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '<div style="text-align:center;">' +
|
||||
'<img src="' + imageUrl + '" style="max-width:100%;max-height:500px;" />' +
|
||||
'</div>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '退单图片',
|
||||
area: ['600px', '500px'],
|
||||
content: html,
|
||||
shadeClose: true,
|
||||
btn: ['关闭']
|
||||
});
|
||||
}
|
||||
|
||||
// 主订单全部
|
||||
var orderMasterHaveReleasedUrl = "";
|
||||
// 主订单新订单
|
||||
|
|
@ -1241,252 +1122,6 @@
|
|||
// 设置定时器,每隔一分钟执行一次 fetchData 函数
|
||||
setInterval(fetchData, 60000);
|
||||
|
||||
// 售后纠纷处理弹窗
|
||||
function handleAfterServiceDispute(orderMasterId) {
|
||||
// 查询该主单的售后记录
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: prefix + '/after/records/byMasterId',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({id: orderMasterId}),
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
showAfterServiceDisputeModal(orderMasterId, result.data);
|
||||
} else {
|
||||
$.modal.msgError("查询售后记录失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.modal.msgError("查询售后记录失败,请重试!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取订单详情信息
|
||||
function getOrderDetailInfo(orderMasterId, callback) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: prefix + "/app/detail",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({id: orderMasterId}),
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
callback(result.data);
|
||||
} else {
|
||||
$.modal.msgError("获取订单详情失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.modal.msgError("获取订单详情失败,请重试!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取师傅同意类型文本
|
||||
function getWorkerAgreeTypeText(type) {
|
||||
var types = {
|
||||
1: '同意',
|
||||
2: '不同意',
|
||||
3: '部分同意'
|
||||
};
|
||||
return types[type] || '未处理';
|
||||
}
|
||||
|
||||
// 显示售后纠纷处理弹窗
|
||||
function showAfterServiceDisputeModal(orderMasterId, afterServiceRecords) {
|
||||
// 先获取订单详情以判断订单类型
|
||||
getOrderDetailInfo(orderMasterId, function(orderDetail) {
|
||||
showDisputeModalWithOrderInfo(orderMasterId, afterServiceRecords, orderDetail);
|
||||
});
|
||||
}
|
||||
|
||||
// 根据订单信息显示售后纠纷处理弹窗
|
||||
function showDisputeModalWithOrderInfo(orderMasterId, afterServiceRecords, orderDetail) {
|
||||
var isGoodsOrder = orderDetail && orderDetail.orderType === 1;
|
||||
|
||||
var html = '<div class="modal fade" id="afterServiceDisputeModal" tabindex="-1" role="dialog">';
|
||||
html += '<div class="modal-dialog modal-lg" role="document">';
|
||||
html += '<div class="modal-content">';
|
||||
html += '<div class="modal-header">';
|
||||
html += '<h4 class="modal-title">售后纠纷处理</h4>';
|
||||
html += '<button type="button" class="close" data-dismiss="modal">×</button>';
|
||||
html += '</div>';
|
||||
html += '<div class="modal-body">';
|
||||
|
||||
if (afterServiceRecords && afterServiceRecords.length > 0) {
|
||||
html += '<div class="table-responsive">';
|
||||
html += '<table class="table table-bordered table-striped">';
|
||||
// 根据订单类型决定表头
|
||||
if (isGoodsOrder) {
|
||||
html += '<thead><tr><th>子单号</th><th>申请退款金额</th><th>师傅反馈</th><th>师傅同意类型</th><th>客户确认</th><th>操作</th></tr></thead>';
|
||||
} else {
|
||||
html += '<thead><tr><th>子单号</th><th>申请退款金额</th><th>师傅反馈</th><th>客户确认</th><th>操作</th></tr></thead>';
|
||||
}
|
||||
html += '<tbody>';
|
||||
|
||||
afterServiceRecords.forEach(function(record) {
|
||||
html += '<tr>';
|
||||
html += '<td>' + (record.orderDetailCode || '') + '</td>';
|
||||
html += '<td>¥' + (record.refund || record.agreedRefund || 0) + '</td>';
|
||||
html += '<td>' + getWorkerFeedbackText(record.workerFeedbackResult) + '<br><small style="color: #999;">反馈金额:¥' + (record.agreedRefund || 0) + '</small></td>';
|
||||
// 如果是商品订单,添加师傅同意类型列
|
||||
if (isGoodsOrder) {
|
||||
html += '<td>' + getWorkerAgreeTypeText(record.workerAgreeType) + '</td>';
|
||||
}
|
||||
html += '<td>' + getCustomerCheckText(record.customerFinalCheck) + '</td>';
|
||||
html += '<td style="width: 280px;">';
|
||||
html += '<div style="margin-bottom: 8px;">';
|
||||
html += '<label style="font-size: 12px; color: #666; margin-bottom: 2px; display: block;">退款金额:</label>';
|
||||
html += '<input type="number" class="form-control" id="refundAmount_' + record.id + '" placeholder="输入退款金额" value="' + (record.refund || 0) + '" style="width: 100%; font-size: 12px; height: 28px;" min="0" step="0.01">';
|
||||
html += '</div>';
|
||||
html += '<div style="margin-bottom: 8px;">';
|
||||
html += '<label style="font-size: 12px; color: #666; margin-bottom: 2px; display: block;">处理原因:</label>';
|
||||
html += '<input type="text" class="form-control" id="processReason_' + record.id + '" placeholder="请输入处理原因" style="width: 100%; font-size: 12px; height: 28px;">';
|
||||
html += '</div>';
|
||||
html += '<button class="btn btn-primary btn-xs" onclick="processRefund(\'' + record.id + '\', \'' + orderMasterId + '\')" style="width: 100%; font-size: 12px; height: 28px;">处理退款</button>';
|
||||
html += '<button class="btn btn-warning btn-xs" onclick="cancelAfterService(\'' + record.id + '\', \'' + orderMasterId + '\')" style="width: 100%; font-size: 12px; height: 28px; margin-top: 4px;">取消售后</button>';
|
||||
html += '</td>';
|
||||
html += '</tr>';
|
||||
});
|
||||
|
||||
html += '</tbody></table></div>';
|
||||
|
||||
// 如果是商品订单,添加提示文字
|
||||
if (isGoodsOrder) {
|
||||
html += '<div class="alert alert-warning" style="margin-top: 15px;">';
|
||||
html += '<strong>提示:</strong>商品退款需确认是否存在退货,并确认退完与否再操作退款!同时确认子单款项金额是否符合退款要求!';
|
||||
html += '</div>';
|
||||
}
|
||||
} else {
|
||||
html += '<div class="alert alert-info">该订单暂无售后纠纷记录</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
html += '<div class="modal-footer">';
|
||||
html += '<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>';
|
||||
html += '</div>';
|
||||
html += '</div></div></div>';
|
||||
|
||||
// 移除已存在的弹窗
|
||||
$('#afterServiceDisputeModal').remove();
|
||||
|
||||
// 添加弹窗到页面
|
||||
$('body').append(html);
|
||||
|
||||
// 显示弹窗
|
||||
$('#afterServiceDisputeModal').modal('show');
|
||||
}
|
||||
|
||||
// 处理退款
|
||||
function processRefund(recordId, orderMasterId) {
|
||||
var refundAmount = $('#refundAmount_' + recordId).val();
|
||||
var processReason = $('#processReason_' + recordId).val();
|
||||
|
||||
if (!refundAmount || refundAmount <= 0) {
|
||||
$.modal.msgError("请输入有效的退款金额");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!processReason || processReason.trim() === '') {
|
||||
$.modal.msgError("请输入处理原因");
|
||||
return;
|
||||
}
|
||||
|
||||
$.modal.confirm("确认处理退款 ¥" + refundAmount + " 吗?", function() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: prefix + '/after/dispute/handle',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
recordId: recordId,
|
||||
orderMasterId: orderMasterId,
|
||||
refundAmount: parseFloat(refundAmount),
|
||||
platformHandleReason: processReason.trim()
|
||||
}),
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.msgSuccess("退款处理成功");
|
||||
$('#afterServiceDisputeModal').modal('hide');
|
||||
// 刷新页面数据
|
||||
$.table.refresh();
|
||||
} else {
|
||||
$.modal.msgError("退款处理失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.modal.msgError("退款处理失败,请重试!");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 取消售后
|
||||
function cancelAfterService(recordId, orderMasterId) {
|
||||
// 获取处理原因输入框的值
|
||||
var processReason = $('#processReason_' + recordId).val();
|
||||
|
||||
if (!processReason || processReason.trim() === '') {
|
||||
$.modal.msgError("请输入处理原因");
|
||||
return;
|
||||
}
|
||||
|
||||
$.modal.confirm("确认取消该售后申请吗?", function() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: prefix + '/after/cancel',
|
||||
data: {
|
||||
afterServiceRecordId: recordId,
|
||||
platformHandleReason: processReason.trim()
|
||||
},
|
||||
success: function (result) {
|
||||
if (result.code == web_status.SUCCESS) {
|
||||
$.modal.msgSuccess("取消售后成功");
|
||||
$('#afterServiceDisputeModal').modal('hide');
|
||||
// 刷新页面数据
|
||||
$.table.refresh();
|
||||
} else {
|
||||
$.modal.msgError("取消售后失败:" + result.msg);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.modal.msgError("取消售后失败,请重试!");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 获取售后类型文本
|
||||
function getAfterServiceTypeText(type) {
|
||||
var types = {
|
||||
1: '未收到货退单退款',
|
||||
2: '未收到货退款',
|
||||
3: '已收到货退款退货'
|
||||
};
|
||||
return types[type] || '未知';
|
||||
}
|
||||
|
||||
// 获取师傅反馈文本
|
||||
function getWorkerFeedbackText(result) {
|
||||
var results = {
|
||||
0: '拒绝',
|
||||
1: '同意',
|
||||
2: '上门补做/重做',
|
||||
3: '重做/补做完成'
|
||||
};
|
||||
return results[result] || '未处理';
|
||||
}
|
||||
|
||||
// 获取客户确认文本
|
||||
function getCustomerCheckText(check) {
|
||||
if (check === 1) return '同意';
|
||||
if (check === 0) return '不同意';
|
||||
return '未确认';
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<script id="importTpl" type="text/template">
|
||||
|
|
|
|||
|
|
@ -585,21 +585,12 @@
|
|||
}
|
||||
|
||||
function orderMasterReject(id) {
|
||||
$.modal.confirm("确定要退单吗?", function() {
|
||||
const url = "console/cancel";
|
||||
const data = { "id": id };
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: url,
|
||||
data: JSON.stringify(data),
|
||||
contentType: 'application/json',
|
||||
success: function (result) {
|
||||
$.operate.ajaxSuccess(result);
|
||||
}
|
||||
$.modal.confirm("确定要退单吗?", function() {
|
||||
const url = "console/cancel";
|
||||
const data = { "id": id };
|
||||
$.operate.post(url, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function changeConditionBtnChosenStyle(e) {
|
||||
$('.condition-btn .btn').removeClass('active-condition-btn');
|
||||
|
|
|
|||
|
|
@ -77,10 +77,6 @@
|
|||
title: '师傅者id',
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: 'workerPhone',
|
||||
title: '手机号'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '真实姓名'
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@
|
|||
</div>
|
||||
|
||||
<div class="btn-group-sm" id="toolbar" role="group">
|
||||
<!-- <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="worker:worker:remove">
|
||||
<i class="fa fa-remove"></i> 禁止登录
|
||||
</a> -->
|
||||
<a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="worker:worker:remove">
|
||||
<i class="fa fa-remove"></i> 删除
|
||||
</a>
|
||||
<a class="btn btn-info" onclick="$.table.importExcel()" shiro:hasPermission="worker:worker:import">
|
||||
<i class="fa fa-upload"></i> 导入
|
||||
</a>
|
||||
|
|
@ -232,14 +232,6 @@
|
|||
return statusTools(row);
|
||||
}
|
||||
},
|
||||
{
|
||||
visible: editFlag == 'hidden' ? false : true,
|
||||
title: '登录状态',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
return loginStatusTools(row);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
|
|
@ -253,7 +245,7 @@
|
|||
var actions = [];
|
||||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="areaDetail(\'' + row.workerId + '\')"><i class="fa fa-info"></i>服务区域</a> ');
|
||||
actions.push('<a class="btn btn-success btn-xs " href="javascript:void(0)" onclick="categoryDetail(\'' + row.workerId + '\')"><i class="fa fa-info"></i>服务技能</a> ');
|
||||
// actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.workerId + '\')><i class="fa fa-remove"></i>禁止登录</a> ');
|
||||
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.workerId + '\')"><i class="fa fa-remove"></i>删除</a> ');
|
||||
return actions.join('');
|
||||
} else {
|
||||
return "";
|
||||
|
|
@ -284,9 +276,9 @@
|
|||
/* 用户状态显示 */
|
||||
function statusTools(row) {
|
||||
if (row.status == 0) {
|
||||
return '<i class="fa fa-toggle-on text-info fa-2x" onclick="disable(\'' + row.workerId + '\')" title="点击停用"></i> ';
|
||||
return '<i class=\"fa fa-toggle-off text-info fa-2x\" onclick="enable(\'' + row.workerId + '\')"></i> ';
|
||||
} else {
|
||||
return '<i class="fa fa-toggle-off text-info fa-2x" onclick="enable(\'' + row.workerId + '\')" title="点击启用"></i> ';
|
||||
return '<i class=\"fa fa-toggle-on text-info fa-2x\" onclick="disable(\'' + row.workerId + '\')"></i> ';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,29 +296,6 @@
|
|||
})
|
||||
}
|
||||
|
||||
/* 登录状态显示 */
|
||||
function loginStatusTools(row) {
|
||||
if (row.loginStatus == null || row.loginStatus == 0) {
|
||||
return '<i class="fa fa-toggle-on text-success fa-2x" onclick="disableLogin(\'' + row.workerId + '\')" title="点击禁用登录"></i> ';
|
||||
} else {
|
||||
return '<i class="fa fa-toggle-off text-danger fa-2x" onclick="enableLogin(\'' + row.workerId + '\')" title="点击启用登录"></i> ';
|
||||
}
|
||||
}
|
||||
|
||||
/* 禁用登录 */
|
||||
function disableLogin(workerId) {
|
||||
$.modal.confirm("确认要禁用该师傅的登录权限吗?", function() {
|
||||
$.operate.post(prefix + "/changeLoginStatus", { "workerId": workerId, "loginStatus": 1 });
|
||||
})
|
||||
}
|
||||
|
||||
/* 启用登录 */
|
||||
function enableLogin(workerId) {
|
||||
$.modal.confirm("确认要启用该师傅的登录权限吗?", function() {
|
||||
$.operate.post(prefix + "/changeLoginStatus", { "workerId": workerId, "loginStatus": 0 });
|
||||
})
|
||||
}
|
||||
|
||||
// 区域联动处理
|
||||
function areaChange(obj, nextId) {
|
||||
var parentCode = $(obj).val();
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ public enum FinancialDetailType {
|
|||
PLACE_FEE(3, "分销金额,可能存在多级"),
|
||||
RETURN_FEE(4, "退款金额"),
|
||||
FINE_FEE(5, "超时罚金"),
|
||||
COMMISSION_FEE(6,"手续费"),
|
||||
TRANSFER_DIFFERENCE_FEE(7, "转单差价");
|
||||
COMMISSION_FEE(6,"手续费");
|
||||
|
||||
private final Integer code;
|
||||
private final String desc;
|
||||
|
|
|
|||
|
|
@ -23,11 +23,7 @@ public enum OrderBehaviorEnum {
|
|||
/**
|
||||
* 派单
|
||||
*/
|
||||
ASSIGN_ORDER("ASSIGN_ORDER", "派单"),
|
||||
/**
|
||||
* 拒绝验收
|
||||
*/
|
||||
REJECT_ACCEPTANCE("REJECT_ACCEPTANCE", "拒绝验收");
|
||||
ASSIGN_ORDER("ASSIGN_ORDER", "派单");
|
||||
|
||||
public final String code;
|
||||
public final String desc;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ public enum OrderStatus implements IEnumType {
|
|||
SERVER(3, "服务中"),
|
||||
FINISH_CHECK(4, "待确认"),
|
||||
FINISH(5, "已完成"),
|
||||
CANCEL(6, "已取消"),
|
||||
Pending(7,"待提现");
|
||||
CANCEL(6, "已取消");
|
||||
|
||||
private final Integer code;
|
||||
private final String desc;
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
package com.ghy.common.enums;
|
||||
|
||||
/**
|
||||
* 微信消息通知枚举
|
||||
* @author clunt
|
||||
*/
|
||||
|
||||
public enum WxStatusEnum {
|
||||
|
||||
/** 任务大厅订单通知 */
|
||||
ORDER_PLAN("", "yqd3p4qsqn1RiyUb8kO4dPqoGKipRQg_y99nGw0jtLE"),
|
||||
/** 新订单通知 */
|
||||
NEW_ORDER("","/pages/order-manage/order-manage?stateCur=6"),
|
||||
/** 商城端消息通知 */
|
||||
CUSTOMER_ORDER("", "JtsGFPDjYhL2GbHfKxvTJaR_lLp8xLyw8VeR01Y0JHM"),
|
||||
/** 测试 **/
|
||||
TEXT("", "dnjTqmqr7OsnOXJR_SikVNNQOPSZLJ6pcDoysOksjNQ"),
|
||||
|
||||
NORMAL_ORDER("", "/pages/order-manage/order-manage"),
|
||||
/** 拒单 **/
|
||||
REJECT_ORDER("", "/pages/order-manage/order-manage"),
|
||||
/** 新工单 默认 **/
|
||||
DEFAULT_ORDER("", "/pages/order-manage/order-manage?stateCur=6"),
|
||||
/**售后工单待处理提醒**/
|
||||
AFTER_SALES_ORDER("", "/pages/order-manage/order-manage?tabCur=4"),
|
||||
/**售后工单待处理提醒**/
|
||||
AFTER_SALES_TIMEOUT_ORDER("", "/pages/order-manage/order-manage?stateCur=2"),
|
||||
/**服务工单超时通知**/
|
||||
TIMEOUT_ORDER("", "/pages/order-manage/order-manage?stateCur=2"),
|
||||
/** 一小时订单 工单处理提醒*/
|
||||
ONEHOUR_ORDER("", "/pages/order-manage/order-manage"),
|
||||
/** 急报 **/
|
||||
WARN_ORDER("", "0RSuVHHP_okErJ1acQmIirBU7TrQYR0xPBgBHyt_azA"),
|
||||
|
||||
/** 默认工单处理提醒 **/
|
||||
DEFAULT_HANDLE_ORDER("", "/pages/order-manage/order-manage"),
|
||||
/** 超时消息通知 */
|
||||
// OVER_TIME("","8I5BnJMfwj-Z7udhNm9Z-kdjdg4__MV5ug1x8KKsbc0"),
|
||||
/** 今日单消息通知 */
|
||||
// TODAY_ORDER("", "yqd3p4qsqn1RiyUb8kO4dPqoGKipRQg_y99nGw0jtLE"),
|
||||
/** 明日单通知 工单处理提醒*/
|
||||
TOMORROW_ORDER("", " /pages/order-manage/order-manage?stateCur=4");
|
||||
/** 不同意排单通知 */
|
||||
// NOT_AGREE_PLAIN("", "yqd3p4qsqn1RiyUb8kO4dPqoGKipRQg_y99nGw0jtLE"),
|
||||
/** 不同意完单通知 */
|
||||
// NOT_AGREE_FINISH("","Yd2PJIdgBhEadi3EkAGyS4DiFp1Rd5ErsEs_jEt-HX4"),
|
||||
/** 子师傅拒绝接单/退单通知 */
|
||||
// DETAIL_REJECT("", "Yd2PJIdgBhEadi3EkAGyS4DiFp1Rd5ErsEs_jEt-HX4")
|
||||
|
||||
|
||||
private String type;
|
||||
private String tempCode;
|
||||
|
||||
WxStatusEnum(String type, String tempCode) {
|
||||
this.type = type;
|
||||
this.tempCode = tempCode;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getTempCode() {
|
||||
return tempCode;
|
||||
}
|
||||
|
||||
public void setTempCode(String tempCode) {
|
||||
this.tempCode = tempCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
package com.ghy.common.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ghy.common.utils.http.HttpUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 百度地图API工具类
|
||||
* 专门用于地址转经纬度等地理编码功能
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Component
|
||||
public class BaiduMapUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BaiduMapUtils.class);
|
||||
|
||||
@Value("${baidu.ak}")
|
||||
private String baiduAk;
|
||||
|
||||
/**
|
||||
* 通过详细地址获取经纬度坐标
|
||||
*
|
||||
* @param provinceName 省份名称
|
||||
* @param cityName 城市名称
|
||||
* @param countryName 区县名称
|
||||
* @param streetName 街道名称
|
||||
* @param detailAddress 详细地址
|
||||
* @return 包含经纬度的Map,失败返回null
|
||||
*/
|
||||
public Map<String, Double> getCoordinatesByAddress(String provinceName, String cityName,
|
||||
String countryName, String streetName,
|
||||
String detailAddress) {
|
||||
try {
|
||||
// 构建完整地址
|
||||
StringBuilder fullAddress = new StringBuilder();
|
||||
if (provinceName != null && !provinceName.trim().isEmpty()) {
|
||||
fullAddress.append(provinceName);
|
||||
}
|
||||
if (cityName != null && !cityName.trim().isEmpty()) {
|
||||
fullAddress.append(cityName);
|
||||
}
|
||||
if (countryName != null && !countryName.trim().isEmpty()) {
|
||||
fullAddress.append(countryName);
|
||||
}
|
||||
if (streetName != null && !streetName.trim().isEmpty()) {
|
||||
fullAddress.append(streetName);
|
||||
}
|
||||
if (detailAddress != null && !detailAddress.trim().isEmpty()) {
|
||||
fullAddress.append(detailAddress);
|
||||
}
|
||||
|
||||
String address = fullAddress.toString();
|
||||
if (address.trim().isEmpty()) {
|
||||
logger.warn("地址信息为空,无法获取坐标");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("开始调用百度地图API解析地址: {}", address);
|
||||
|
||||
// 调用百度地图正向地理编码API
|
||||
String encoded = URLEncoder.encode(address, StandardCharsets.UTF_8.name());
|
||||
String url = "https://api.map.baidu.com/geocoding/v3/?output=json&ak=" + baiduAk + "&address=" + encoded;
|
||||
|
||||
// 使用HttpUtils发送GET请求
|
||||
String result = HttpUtils.sendGet(url);
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
logger.error("百度地图API返回结果为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||
|
||||
// 解析返回结果
|
||||
JSONObject resultJson = JSONObject.parseObject(result);
|
||||
if ("0".equals(resultJson.getString("status"))) {
|
||||
JSONObject location = resultJson.getJSONObject("result").getJSONObject("location");
|
||||
Double lng = location.getDouble("lng");
|
||||
Double lat = location.getDouble("lat");
|
||||
|
||||
Map<String, Double> coordinates = new HashMap<>();
|
||||
coordinates.put("longitude", lng);
|
||||
coordinates.put("latitude", lat);
|
||||
|
||||
logger.info("百度地图API解析成功: {} -> 经度={}, 纬度={}", address, lng, lat);
|
||||
return coordinates;
|
||||
} else {
|
||||
logger.error("百度地图API解析失败: {} - {}", address, resultJson.getString("msg"));
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地图API失败: {} - {}",
|
||||
String.format("省:%s,市:%s,区:%s,街道:%s,详细:%s",
|
||||
provinceName, cityName, countryName, streetName, detailAddress),
|
||||
e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过完整地址字符串获取经纬度坐标
|
||||
*
|
||||
* @param fullAddress 完整地址字符串
|
||||
* @return 包含经纬度的Map,失败返回null
|
||||
*/
|
||||
public Map<String, Double> getCoordinatesByFullAddress(String fullAddress) {
|
||||
try {
|
||||
if (fullAddress == null || fullAddress.trim().isEmpty()) {
|
||||
logger.warn("完整地址为空,无法获取坐标");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("开始调用百度地图API解析完整地址: {}", fullAddress);
|
||||
|
||||
// 调用百度地图正向地理编码API
|
||||
String encoded = URLEncoder.encode(fullAddress, StandardCharsets.UTF_8.name());
|
||||
String url = "https://api.map.baidu.com/geocoding/v3/?output=json&ak=" + baiduAk + "&address=" + encoded;
|
||||
|
||||
// 使用HttpUtils发送GET请求
|
||||
String result = HttpUtils.sendGet(url);
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
logger.error("百度地图API返回结果为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||
|
||||
// 解析返回结果
|
||||
JSONObject resultJson = JSONObject.parseObject(result);
|
||||
if ("0".equals(resultJson.getString("status"))) {
|
||||
JSONObject location = resultJson.getJSONObject("result").getJSONObject("location");
|
||||
Double lng = location.getDouble("lng");
|
||||
Double lat = location.getDouble("lat");
|
||||
|
||||
Map<String, Double> coordinates = new HashMap<>();
|
||||
coordinates.put("longitude", lng);
|
||||
coordinates.put("latitude", lat);
|
||||
|
||||
logger.info("百度地图API解析成功: {} -> 经度={}, 纬度={}", fullAddress, lng, lat);
|
||||
return coordinates;
|
||||
} else {
|
||||
logger.error("百度地图API解析失败: {} - {}", fullAddress, resultJson.getString("msg"));
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地图API失败: {} - {}", fullAddress, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过经纬度获取地址信息(逆地理编码)
|
||||
*
|
||||
* @param longitude 经度
|
||||
* @param latitude 纬度
|
||||
* @return 包含地址信息的JSONObject,失败返回null
|
||||
*/
|
||||
public JSONObject getAddressByCoordinates(Double longitude, Double latitude) {
|
||||
try {
|
||||
if (longitude == null || latitude == null) {
|
||||
logger.warn("经纬度参数为空,无法获取地址");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("开始调用百度地图API逆解析坐标: 经度={}, 纬度={}", longitude, latitude);
|
||||
|
||||
// 调用百度地图逆地理编码API
|
||||
String location = longitude + "," + latitude;
|
||||
String url = "https://api.map.baidu.com/reverse_geocoding/v3/?ak=" + baiduAk +
|
||||
"&output=json&coordtype=wgs84ll&location=" + location;
|
||||
|
||||
// 使用HttpUtils发送GET请求
|
||||
String result = HttpUtils.sendGet(url);
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
logger.error("百度地图API返回结果为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||
|
||||
// 解析返回结果
|
||||
JSONObject resultJson = JSONObject.parseObject(result);
|
||||
if ("0".equals(resultJson.getString("status"))) {
|
||||
logger.info("百度地图API逆解析成功: 经度={}, 纬度={}", longitude, latitude);
|
||||
return resultJson;
|
||||
} else {
|
||||
logger.error("百度地图API逆解析失败: 经度={}, 纬度={} - {}",
|
||||
longitude, latitude, resultJson.getString("msg"));
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地图API逆解析失败: 经度={}, 纬度={} - {}",
|
||||
longitude, latitude, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
package com.ghy.common.utils;
|
||||
|
||||
/**
|
||||
* 地理位置计算工具类
|
||||
* 提供经纬度之间的距离计算功能
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
public class LocationUtils {
|
||||
|
||||
/** 地球半径(千米) */
|
||||
private static final double EARTH_RADIUS_KM = 6371.0;
|
||||
|
||||
/** 地球半径(米) */
|
||||
private static final double EARTH_RADIUS_M = 6371000.0;
|
||||
|
||||
/**
|
||||
* 使用Haversine公式计算两点间的距离(米)
|
||||
*
|
||||
* @param lat1 第一个点的纬度
|
||||
* @param lng1 第一个点的经度
|
||||
* @param lat2 第二个点的纬度
|
||||
* @param lng2 第二个点的经度
|
||||
* @return 距离(米)
|
||||
*/
|
||||
public static double getDistanceInMeters(double lat1, double lng1, double lat2, double lng2) {
|
||||
return getDistanceInKilometers(lat1, lng1, lat2, lng2) * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Haversine公式计算两点间的距离(千米)
|
||||
*
|
||||
* @param lat1 第一个点的纬度
|
||||
* @param lng1 第一个点的经度
|
||||
* @param lat2 第二个点的纬度
|
||||
* @param lng2 第二个点的经度
|
||||
* @return 距离(千米)
|
||||
*/
|
||||
public static double getDistanceInKilometers(double lat1, double lng1, double lat2, double lng2) {
|
||||
// 将角度转换为弧度
|
||||
double lat1Rad = Math.toRadians(lat1);
|
||||
double lng1Rad = Math.toRadians(lng1);
|
||||
double lat2Rad = Math.toRadians(lat2);
|
||||
double lng2Rad = Math.toRadians(lng2);
|
||||
|
||||
// 计算经纬度差值
|
||||
double deltaLat = lat2Rad - lat1Rad;
|
||||
double deltaLng = lng2Rad - lng1Rad;
|
||||
|
||||
// Haversine公式
|
||||
double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
|
||||
Math.cos(lat1Rad) * Math.cos(lat2Rad) *
|
||||
Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
|
||||
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
return EARTH_RADIUS_KM * c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断某个点是否在指定范围内
|
||||
*
|
||||
* @param centerLat 中心点纬度
|
||||
* @param centerLng 中心点经度
|
||||
* @param targetLat 目标点纬度
|
||||
* @param targetLng 目标点经度
|
||||
* @param radiusKm 半径(千米)
|
||||
* @return 是否在范围内
|
||||
*/
|
||||
public static boolean isWithinRadius(double centerLat, double centerLng,
|
||||
double targetLat, double targetLng,
|
||||
double radiusKm) {
|
||||
double distance = getDistanceInKilometers(centerLat, centerLng, targetLat, targetLng);
|
||||
return distance <= radiusKm;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化距离显示
|
||||
*
|
||||
* @param distanceInMeters 距离(米)
|
||||
* @return 格式化后的距离字符串
|
||||
*/
|
||||
public static String formatDistance(double distanceInMeters) {
|
||||
if (distanceInMeters < 1000) {
|
||||
return String.format("%.0f米", distanceInMeters);
|
||||
} else {
|
||||
double km = distanceInMeters / 1000;
|
||||
if (km < 10) {
|
||||
return String.format("%.1f公里", km);
|
||||
} else {
|
||||
return String.format("%.0f公里", km);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个坐标点的方位角(以正北为0度,顺时针)
|
||||
*
|
||||
* @param lat1 起点纬度
|
||||
* @param lng1 起点经度
|
||||
* @param lat2 终点纬度
|
||||
* @param lng2 终点经度
|
||||
* @return 方位角(度数,0-360)
|
||||
*/
|
||||
public static double getBearing(double lat1, double lng1, double lat2, double lng2) {
|
||||
double lat1Rad = Math.toRadians(lat1);
|
||||
double lat2Rad = Math.toRadians(lat2);
|
||||
double deltaLngRad = Math.toRadians(lng2 - lng1);
|
||||
|
||||
double y = Math.sin(deltaLngRad) * Math.cos(lat2Rad);
|
||||
double x = Math.cos(lat1Rad) * Math.sin(lat2Rad) -
|
||||
Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(deltaLngRad);
|
||||
|
||||
double bearingRad = Math.atan2(y, x);
|
||||
double bearingDeg = Math.toDegrees(bearingRad);
|
||||
|
||||
// 转换为0-360度
|
||||
return (bearingDeg + 360) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将方位角转换为方向描述
|
||||
*
|
||||
* @param bearing 方位角(度数)
|
||||
* @return 方向描述
|
||||
*/
|
||||
public static String getDirectionDescription(double bearing) {
|
||||
String[] directions = {"北", "东北", "东", "东南", "南", "西南", "西", "西北"};
|
||||
int index = (int) Math.round(bearing / 45) % 8;
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证经纬度的有效性
|
||||
*
|
||||
* @param latitude 纬度
|
||||
* @param longitude 经度
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean isValidCoordinate(Double latitude, Double longitude) {
|
||||
if (latitude == null || longitude == null) {
|
||||
return false;
|
||||
}
|
||||
return latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算中心点坐标(多个点的几何中心)
|
||||
*
|
||||
* @param coordinates 坐标点数组,每个元素为[纬度, 经度]
|
||||
* @return 中心点坐标 [纬度, 经度]
|
||||
*/
|
||||
public static double[] getCenterPoint(double[][] coordinates) {
|
||||
if (coordinates == null || coordinates.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double sumLat = 0, sumLng = 0;
|
||||
for (double[] coord : coordinates) {
|
||||
sumLat += coord[0];
|
||||
sumLng += coord[1];
|
||||
}
|
||||
|
||||
return new double[]{sumLat / coordinates.length, sumLng / coordinates.length};
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ public class QiniuUtils {
|
|||
//解析上传成功的结果
|
||||
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
|
||||
|
||||
return "https://gqz.opsoul.com/" + putRet.key;
|
||||
return "http://gqz.opsoul.com/" + putRet.key;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ghy.common.config.WxConfig;
|
||||
import com.ghy.common.enums.WxMsgEnum;
|
||||
import com.ghy.common.enums.WxStatusEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -14,9 +13,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStreamWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
|
@ -64,12 +61,6 @@ public class WechatMsgUtils {
|
|||
* @param token 鉴权token信息
|
||||
*/
|
||||
public static void sendWeChatMsg(String token, String userOpenId, WxMsgEnum mesType, Map<String, Object> dataMap) {
|
||||
String type=mesType+"";
|
||||
String wxStatusByWxMsgUrl=getWxStatusByWxMsg(WxStatusEnum.valueOf(type));
|
||||
if (StringUtils.isEmpty(wxStatusByWxMsgUrl)){
|
||||
wxStatusByWxMsgUrl="/pages/order-manage/order-manage";
|
||||
}
|
||||
log.info("传入的参数值{},获取到的url{}",type,wxStatusByWxMsgUrl);
|
||||
// 接口地址
|
||||
String sendMsgApi = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token;
|
||||
//整体参数map
|
||||
|
|
@ -78,10 +69,10 @@ public class WechatMsgUtils {
|
|||
paramMap.put("page", "index");
|
||||
paramMap.put("template_id", mesType.getTempCode());
|
||||
paramMap.put("url","");
|
||||
Map<String, Object> miniprogram=new HashMap<>();
|
||||
miniprogram.put("appid","wx105ce607b514ff2a");
|
||||
miniprogram.put("pagepath",wxStatusByWxMsgUrl);
|
||||
paramMap.put("miniprogram",miniprogram);
|
||||
Map<String, Object> miniprogram=new HashMap<>();
|
||||
miniprogram.put("appid","");
|
||||
miniprogram.put("pagepath","");
|
||||
paramMap.put("miniprogram",miniprogram);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
for (Map.Entry<String, Object> objectEntry : dataMap.entrySet()) {
|
||||
JSONObject model = new JSONObject();
|
||||
|
|
@ -152,7 +143,4 @@ public class WechatMsgUtils {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
public static String getWxStatusByWxMsg(WxStatusEnum type){
|
||||
return type.getTempCode();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.ghy.common.annotation.Excel;
|
|||
import com.ghy.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -65,16 +64,6 @@ public class CustomerAddress extends BaseEntity {
|
|||
|
||||
private Boolean needNameFlag = false;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private BigDecimal longitude;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private BigDecimal latitude;
|
||||
|
||||
/*是否删除id*/
|
||||
private Long isDelete;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,10 +42,6 @@ public class CustomerSelection extends BaseEntity
|
|||
@Excel(name = "选品类型 1.选取 2.屏蔽")
|
||||
private Long selectionType;
|
||||
|
||||
/** 类型 */
|
||||
@Excel(name = "类型")
|
||||
private Integer type;
|
||||
|
||||
public List<Long> getDeptCategoryIds() {
|
||||
return deptCategoryIds;
|
||||
}
|
||||
|
|
@ -109,16 +105,6 @@ public class CustomerSelection extends BaseEntity
|
|||
return selectionType;
|
||||
}
|
||||
|
||||
public void setType(Integer type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Integer getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
|
|
@ -128,7 +114,6 @@ public class CustomerSelection extends BaseEntity
|
|||
.append("customerId", getCustomerId())
|
||||
.append("deptCategoryId", getDeptCategoryId())
|
||||
.append("selectionType", getSelectionType())
|
||||
.append("type", getType())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
|
|
|
|||
|
|
@ -55,11 +55,4 @@ public interface CustomerAddressMapper {
|
|||
@Param("cityId") Long cityId,
|
||||
@Param("countryId") Long countryId,
|
||||
@Param("address") String address);
|
||||
|
||||
/**
|
||||
* 批量查询地址信息
|
||||
* @param customerAddressIds 地址ID列表
|
||||
* @return 地址列表
|
||||
*/
|
||||
List<CustomerAddress> selectByIds(@Param("customerAddressIds") List<Long> customerAddressIds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,11 +51,4 @@ public interface CustomerAddressService {
|
|||
int updateCustomerAddress(CustomerAddress customerAddress);
|
||||
|
||||
CustomerAddress selectByCustomerAndAddress(Long customerId, Long provinceId, Long cityId, Long countryId, String address);
|
||||
|
||||
/**
|
||||
* 批量查询地址信息
|
||||
* @param customerAddressIds 地址ID列表
|
||||
* @return 地址列表
|
||||
*/
|
||||
List<CustomerAddress> selectByIds(List<Long> customerAddressIds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,7 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ghy.common.utils.http.HttpUtils;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
|
@ -25,7 +21,6 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
|
|||
|
||||
@Resource
|
||||
private ISysAreaService iSysAreaService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CustomerAddressServiceImpl.class);
|
||||
|
||||
@Override
|
||||
|
|
@ -67,27 +62,16 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
|
|||
|
||||
@Override
|
||||
public CustomerAddress selectByCustomerAddressId(Long customerAddressId) {
|
||||
CustomerAddress address= customerAddressMapper.selectByCustomerAddressId(customerAddressId);
|
||||
address.setCountryName(iSysAreaService.selectById(address.getCountryId()).getAreaName());
|
||||
address.setCityName(iSysAreaService.selectById(address.getCityId()).getAreaName());
|
||||
address.setProvinceName(iSysAreaService.selectById(address.getProvinceId()).getAreaName());
|
||||
if(address.getStreetId()!=null){
|
||||
address.setStreetName(iSysAreaService.selectById(address.getStreetId()).getAreaName());
|
||||
}
|
||||
return address;
|
||||
return customerAddressMapper.selectByCustomerAddressId(customerAddressId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertCustomerAddress(CustomerAddress customerAddress) {
|
||||
// 在插入前自动获取经纬度
|
||||
setGeocodingForAddress(customerAddress);
|
||||
return customerAddressMapper.insertCustomerAddress(customerAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateCustomerAddress(CustomerAddress customerAddress) {
|
||||
// 在更新前自动获取经纬度
|
||||
setGeocodingForAddress(customerAddress);
|
||||
return customerAddressMapper.updateCustomerAddress(customerAddress);
|
||||
}
|
||||
|
||||
|
|
@ -95,97 +79,4 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
|
|||
public CustomerAddress selectByCustomerAndAddress(Long customerId, Long provinceId, Long cityId, Long countryId, String address) {
|
||||
return customerAddressMapper.selectByCustomerAndAddress(customerId, provinceId, cityId, countryId, address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CustomerAddress> selectByIds(List<Long> customerAddressIds) {
|
||||
if (customerAddressIds == null || customerAddressIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return customerAddressMapper.selectByIds(customerAddressIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为地址设置经纬度信息
|
||||
*
|
||||
* @param customerAddress 客户地址对象
|
||||
*/
|
||||
private void setGeocodingForAddress(CustomerAddress customerAddress) {
|
||||
try {
|
||||
// 如果已经有经纬度信息,则跳过
|
||||
if (customerAddress.getLongitude() != null && customerAddress.getLatitude() != null) {
|
||||
logger.info("地址已有经纬度信息,跳过获取");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取地址名称信息
|
||||
String provinceName = null;
|
||||
String cityName = null;
|
||||
String countryName = null;
|
||||
String streetName = null;
|
||||
|
||||
if (customerAddress.getProvinceId() != null) {
|
||||
try {
|
||||
provinceName = iSysAreaService.selectById(customerAddress.getProvinceId()).getAreaName();
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取省份名称失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (customerAddress.getCityId() != null) {
|
||||
try {
|
||||
cityName = iSysAreaService.selectById(customerAddress.getCityId()).getAreaName();
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取城市名称失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (customerAddress.getCountryId() != null) {
|
||||
try {
|
||||
countryName = iSysAreaService.selectById(customerAddress.getCountryId()).getAreaName();
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取区县名称失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (customerAddress.getStreetId() != null) {
|
||||
try {
|
||||
streetName = iSysAreaService.selectById(customerAddress.getStreetId()).getAreaName();
|
||||
} catch (Exception e) {
|
||||
logger.warn("获取街道名称失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 调用百度地理编码接口获取经纬度
|
||||
try {
|
||||
JSONObject requestBody = new JSONObject();
|
||||
requestBody.put("provinceName", provinceName);
|
||||
requestBody.put("cityName", cityName);
|
||||
requestBody.put("countryName", countryName);
|
||||
requestBody.put("streetName", streetName);
|
||||
requestBody.put("address", customerAddress.getAddress());
|
||||
|
||||
// 调用百度地理编码接口
|
||||
String url = "https://gmhl.gmjlb.com/tool/baidu/geocode";
|
||||
String result = HttpUtils.sendPost(url, requestBody.toJSONString());
|
||||
|
||||
JSONObject responseJson = JSONObject.parseObject(result);
|
||||
if (responseJson.getInteger("code") == 200) {
|
||||
JSONObject data = responseJson.getJSONObject("data");
|
||||
BigDecimal longitude = data.getBigDecimal("longitude");
|
||||
BigDecimal latitude = data.getBigDecimal("latitude");
|
||||
|
||||
customerAddress.setLongitude(longitude);
|
||||
customerAddress.setLatitude(latitude);
|
||||
logger.info("成功获取地址经纬度: 经度={}, 纬度={}", longitude, latitude);
|
||||
} else {
|
||||
logger.warn("百度地理编码接口调用失败: {}", responseJson.getString("msg"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("调用百度地理编码接口异常: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取地址经纬度失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ public class CustomerServiceImpl implements CustomerService {
|
|||
|
||||
@Override
|
||||
public Customer selectByCustomerId(Long customerId) {
|
||||
if (customerId == null) {
|
||||
return null;
|
||||
}
|
||||
return customerMapper.selectByCustomerId(customerId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@
|
|||
<result property="cityName" column="city_name" />
|
||||
<result property="countryName" column="country_name" />
|
||||
<result property="streetName" column="street_name" />
|
||||
<result property="longitude" column="longitude" />
|
||||
<result property="latitude" column="latitude" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerAddress">
|
||||
SELECT customer_address_id, customer_id, name, phone, province_id, city_id, country_id, street_id, status,
|
||||
address, create_by, create_time, remark, is_default, longitude, latitude
|
||||
address, create_by, create_time, remark, is_default
|
||||
FROM customer_address
|
||||
</sql>
|
||||
|
||||
|
|
@ -116,8 +114,6 @@
|
|||
<if test="isDefault != null">is_default,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="longitude != null">longitude,</if>
|
||||
<if test="latitude != null">latitude,</if>
|
||||
deleted,
|
||||
create_time
|
||||
)values(
|
||||
|
|
@ -133,8 +129,6 @@
|
|||
<if test="isDefault != null">#{isDefault},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="longitude != null">#{longitude},</if>
|
||||
<if test="latitude != null">#{latitude},</if>
|
||||
0,
|
||||
sysdate()
|
||||
)
|
||||
|
|
@ -153,21 +147,10 @@
|
|||
<if test="isDefault != null">is_default = #{isDefault},</if>
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="longitude != null">longitude = #{longitude},</if>
|
||||
<if test="latitude != null">latitude = #{latitude},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where customer_address_id = #{customerAddressId}
|
||||
</update>
|
||||
|
||||
<select id="selectByIds" resultMap="CustomerAddressResult">
|
||||
<include refid="selectCustomerAddress"/>
|
||||
WHERE customer_address_id IN
|
||||
<foreach collection="customerAddressIds" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
<result property="customerId" column="customer_id" />
|
||||
<result property="deptCategoryId" column="dept_category_id" />
|
||||
<result property="selectionType" column="selection_type" />
|
||||
<result property="type" column="type" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
|
|
@ -20,7 +19,7 @@
|
|||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerSelectionVo">
|
||||
select id, work_id, dept_id, customer_id, dept_category_id, selection_type, type, create_by, create_time, update_by, update_time, remark from customer_selection
|
||||
select id, work_id, dept_id, customer_id, dept_category_id, selection_type, create_by, create_time, update_by, update_time, remark from customer_selection
|
||||
</sql>
|
||||
|
||||
<select id="selectCustomerSelectionList" parameterType="CustomerSelection" resultMap="CustomerSelectionResult">
|
||||
|
|
@ -31,7 +30,6 @@
|
|||
<if test="customerId != null "> and customer_id = #{customerId}</if>
|
||||
<if test="deptCategoryId != null "> and dept_category_id = #{deptCategoryId}</if>
|
||||
<if test="selectionType != null "> and selection_type = #{selectionType}</if>
|
||||
<if test="type != null and type != ''"> and type = #{type}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
|
@ -48,7 +46,6 @@
|
|||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="deptCategoryId != null">dept_category_id,</if>
|
||||
<if test="selectionType != null">selection_type,</if>
|
||||
<if test="type != null">type,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
|
|
@ -61,7 +58,6 @@
|
|||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="deptCategoryId != null">#{deptCategoryId},</if>
|
||||
<if test="selectionType != null">#{selectionType},</if>
|
||||
<if test="type != null">#{type},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
|
|
@ -78,7 +74,6 @@
|
|||
<if test="customerId != null">customer_id = #{customerId},</if>
|
||||
<if test="deptCategoryId != null">dept_category_id = #{deptCategoryId},</if>
|
||||
<if test="selectionType != null">selection_type = #{selectionType},</if>
|
||||
<if test="type != null">type = #{type},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
|
|
|
|||
|
|
@ -283,7 +283,6 @@ public class ShiroConfig
|
|||
filterChainDefinitionMap.put("/special/skill/**", "anon");
|
||||
filterChainDefinitionMap.put("/customer/**", "anon");
|
||||
filterChainDefinitionMap.put("/goods/**", "anon");
|
||||
filterChainDefinitionMap.put("/shop/**", "anon");
|
||||
filterChainDefinitionMap.put("/financial/**", "anon");
|
||||
filterChainDefinitionMap.put("/tool/**", "anon");
|
||||
filterChainDefinitionMap.put("/adapay/**", "anon");
|
||||
|
|
|
|||
|
|
@ -22,14 +22,6 @@
|
|||
<groupId>com.ghy</groupId>
|
||||
<artifactId>ghy-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ghy</groupId>
|
||||
<artifactId>ghy-custom</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ghy</groupId>
|
||||
<artifactId>ghy-shop</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
|||
|
|
@ -105,16 +105,4 @@ public class DeptGoodsCategory extends GoodsCategory {
|
|||
|
||||
private String shopInsuranceIds;
|
||||
|
||||
private Long customerId;
|
||||
|
||||
private Integer isSetting;
|
||||
|
||||
@Excel(name = "服务类目ID", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Long serviceCategoryId; // 服务类目ID
|
||||
|
||||
@Excel(name = "服务类目名称", cellType = Excel.ColumnType.STRING)
|
||||
private String serviceCategoryName; // 服务类目名称
|
||||
|
||||
@Excel(name = "倒计时小时数", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Integer countdownHours; // 倒计时小时数
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.ghy.goods.domain;
|
|||
|
||||
import com.ghy.common.annotation.Excel;
|
||||
import com.ghy.common.core.domain.BaseEntity;
|
||||
import com.ghy.shop.domain.Shop;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
|
|
@ -29,12 +28,6 @@ public class Goods extends BaseEntity {
|
|||
@Excel(name = "部门id")
|
||||
private Long deptId;
|
||||
|
||||
@Excel(name = "店铺id")
|
||||
private Long shopId;
|
||||
|
||||
@Excel(name = "店铺名称")
|
||||
private String shopName;
|
||||
|
||||
@Excel(name = "名称")
|
||||
private String goodsName;
|
||||
|
||||
|
|
@ -97,36 +90,6 @@ public class Goods extends BaseEntity {
|
|||
|
||||
private String keyword;
|
||||
|
||||
/*类型:服务商品 1和 配件商品 2*/
|
||||
private Integer type;
|
||||
|
||||
private List<InsuranceManager> insuranceManagers;
|
||||
|
||||
private String distance;
|
||||
|
||||
/** 纬度 */
|
||||
private Double latitude;
|
||||
|
||||
/** 经度 */
|
||||
private Double longitude;
|
||||
|
||||
/** 到店服务 1-到店服务 2-到店服务+配送 3-到店服务+上门 */
|
||||
@Excel(name = "到店服务")
|
||||
private Integer storeService;
|
||||
|
||||
/** 安装服务 1-包安装 2-不包安装 3-自费安装 */
|
||||
@Excel(name = "安装服务", readConverterExp = "1=包安装,2=不包安装,3=自费安装")
|
||||
private Integer installService;
|
||||
|
||||
/** 配送服务 1-包邮 2-同城包送 3-邮费自付 4-自提 */
|
||||
@Excel(name = "配送服务", readConverterExp = "1=包邮,2=同城包送,3=邮费自付,4=自提")
|
||||
private Integer deliveryService;
|
||||
|
||||
private Shop shop;
|
||||
|
||||
private Shop serviceShop;
|
||||
|
||||
private List<Long> goodsIds;
|
||||
|
||||
private String goodsStandard;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,4 @@ public class GoodsStandard extends BaseEntity {
|
|||
@Excel(name = "截留金额", cellType = Excel.ColumnType.STRING)
|
||||
private BigDecimal retainMoney;
|
||||
|
||||
@Excel(name = "图片地址")
|
||||
private String imageUrl;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,18 +64,4 @@ public interface GoodsStandardMapper {
|
|||
|
||||
int update(GoodsStandard goodsStandard);
|
||||
|
||||
/**
|
||||
* 根据规格名称模糊查询商品规格
|
||||
* @param standardName 规格名称(支持模糊查询)
|
||||
* @return 符合条件的商品规格列表
|
||||
*/
|
||||
List<GoodsStandard> selectByStandardNameLike(@Param("standardName") String standardName);
|
||||
|
||||
/**
|
||||
* 根据部门商品分类ID查询商品规格
|
||||
* @param deptGoodsCategoryId 部门商品分类ID
|
||||
* @return 符合条件的商品规格列表
|
||||
*/
|
||||
List<GoodsStandard> selectByDeptGoodsCategoryId(@Param("deptGoodsCategoryId") Long deptGoodsCategoryId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,12 +81,4 @@ public interface DeptGoodsCategoryService {
|
|||
*/
|
||||
AjaxResult batchAdd(Long parentId, Long[] checked);
|
||||
|
||||
/**
|
||||
* 递归查找类目的倒计时小时数,如果当前类目没有设置则查找上级类目
|
||||
*
|
||||
* @param deptGoodsCategoryId 分公司类目ID
|
||||
* @return 倒计时小时数,如果所有上级类目都没有设置则返回null
|
||||
*/
|
||||
Integer findCountdownHoursRecursively(Long deptGoodsCategoryId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,12 +95,4 @@ public interface GoodsService {
|
|||
|
||||
int edit(Goods goods);
|
||||
|
||||
/**
|
||||
* 通过商品ID查找其服务类目,然后通过服务类目的四级类目查询所有相关商品
|
||||
*
|
||||
* @param goodsId 商品ID
|
||||
* @return 相关商品列表(只包含上架商品)
|
||||
*/
|
||||
List<Goods> selectGoodsByServiceCategory(Long goodsId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,18 +59,4 @@ public interface GoodsStandardService {
|
|||
|
||||
int update(GoodsStandard goodsStandard);
|
||||
|
||||
/**
|
||||
* 根据规格名称模糊查询商品规格
|
||||
* @param standardName 规格名称(支持模糊查询)
|
||||
* @return 符合条件的商品规格列表
|
||||
*/
|
||||
List<GoodsStandard> selectByStandardNameLike(String standardName);
|
||||
|
||||
/**
|
||||
* 根据部门商品分类ID获取所有商品
|
||||
* @param deptGoodsCategoryId 部门商品分类ID
|
||||
* @return 商品列表
|
||||
*/
|
||||
List<Goods> selectGoodsByDeptGoodsCategoryId(Long deptGoodsCategoryId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ import com.ghy.common.core.domain.Ztree;
|
|||
import com.ghy.common.core.text.Convert;
|
||||
import com.ghy.common.utils.StringUtils;
|
||||
import com.ghy.common.utils.bean.BeanUtils;
|
||||
import com.ghy.customer.domain.Customer;
|
||||
import com.ghy.customer.domain.CustomerSelection;
|
||||
import com.ghy.customer.service.CustomerService;
|
||||
import com.ghy.customer.service.ICustomerSelectionService;
|
||||
import com.ghy.goods.domain.DeptGoodsCategory;
|
||||
import com.ghy.goods.domain.GoodsCategory;
|
||||
import com.ghy.goods.domain.GoodsImgs;
|
||||
|
|
@ -40,11 +36,6 @@ public class DeptGoodsCategoryServiceImpl implements DeptGoodsCategoryService {
|
|||
private DeptGoodsCategoryMapper deptGoodsCategoryMapper;
|
||||
@Resource
|
||||
private GoodsImgsService goodsImgsService;
|
||||
@Resource
|
||||
private CustomerService customerService;
|
||||
@Resource
|
||||
private ICustomerSelectionService customerSelectionService;
|
||||
|
||||
|
||||
@Override
|
||||
public List<DeptGoodsCategory> list(DeptGoodsCategory category) {
|
||||
|
|
@ -94,33 +85,8 @@ public class DeptGoodsCategoryServiceImpl implements DeptGoodsCategoryService {
|
|||
|
||||
@Override
|
||||
public List<DeptGoodsCategory> appList(DeptGoodsCategory deptGoodsCategory) {
|
||||
Customer customer= customerService.selectByCustomerId(deptGoodsCategory.getCustomerId());
|
||||
List<DeptGoodsCategory> goodsCategoryList = new ArrayList<>();
|
||||
if (customer == null) {
|
||||
log.warn("未找到客户,customerId: {}", deptGoodsCategory.getCustomerId());
|
||||
// 返回全部类目或默认类目
|
||||
goodsCategoryList = deptGoodsCategoryMapper.appList(deptGoodsCategory);
|
||||
} else {
|
||||
Long customerId = customer.getCustomerPlace();
|
||||
if (customer.getPlaceStatus() == 2) {
|
||||
customerId = customer.getCustomerId();
|
||||
}
|
||||
CustomerSelection customerSelection = new CustomerSelection();
|
||||
customerSelection.setCustomerId(customerId);
|
||||
List<CustomerSelection> customerSelections = customerSelectionService.selectCustomerSelectionList(customerSelection);
|
||||
log.info("用户id{}获取到的类目列表{}", customerId, customerSelections);
|
||||
if (deptGoodsCategory.getIsSetting() == 1) {
|
||||
goodsCategoryList = deptGoodsCategoryMapper.appList(deptGoodsCategory);
|
||||
} else {
|
||||
for (CustomerSelection customerSelection1 : customerSelections) {
|
||||
DeptGoodsCategory deptGoodsCategory1 = deptGoodsCategoryMapper.selectOneByGoodsCategoryId(customerSelection1.getDeptCategoryId());
|
||||
if (customerSelection1.getSelectionType() == 1) {
|
||||
goodsCategoryList.add(deptGoodsCategory1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("筛选完的类目列表{}",goodsCategoryList);
|
||||
// 第一层
|
||||
List<DeptGoodsCategory> goodsCategoryList = deptGoodsCategoryMapper.appList(deptGoodsCategory);
|
||||
// 第二层
|
||||
this.fillChild(goodsCategoryList);
|
||||
// 第三层
|
||||
|
|
@ -164,11 +130,8 @@ public class DeptGoodsCategoryServiceImpl implements DeptGoodsCategoryService {
|
|||
GoodsCategory goodsCategory = goodsCategoryService.selectById(checked[0]);
|
||||
Assert.notNull(goodsCategory, "不存在该类目: " + checked[0]);
|
||||
List<DeptGoodsCategory> oldList = deptGoodsCategoryMapper.selectByDeptId(parentId);
|
||||
|
||||
Map<Long, Long> oldMap = oldList.stream()
|
||||
.filter(x -> x.getType() != null && goodsCategory.getType() != null && x.getType().equals(goodsCategory.getType()))
|
||||
.collect(Collectors.toMap(DeptGoodsCategory::getGoodsCategoryId, DeptGoodsCategory::getDeptGoodsCategoryId));
|
||||
|
||||
Map<Long, Long> oldMap = oldList.stream().filter(x -> x.getType().equals(goodsCategory.getType()))
|
||||
.collect(Collectors.toMap(GoodsCategory::getGoodsCategoryId, DeptGoodsCategory::getDeptGoodsCategoryId));
|
||||
HashSet<Long> newSet = new HashSet<>(Arrays.asList(checked));
|
||||
Set<Long> intersection = oldList.stream().map(DeptGoodsCategory::getGoodsCategoryId).collect(Collectors.toSet());
|
||||
intersection.addAll(Arrays.asList(checked));
|
||||
|
|
@ -214,42 +177,4 @@ public class DeptGoodsCategoryServiceImpl implements DeptGoodsCategoryService {
|
|||
}
|
||||
return ztrees;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer findCountdownHoursRecursively(Long deptGoodsCategoryId) {
|
||||
if (deptGoodsCategoryId == null) {
|
||||
log.warn("分公司类目ID为空,无法查找倒计时小时数");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 查询当前类目
|
||||
DeptGoodsCategory currentCategory = deptGoodsCategoryMapper.selectById(deptGoodsCategoryId);
|
||||
if (currentCategory == null) {
|
||||
log.warn("未找到分公司类目,ID: {}", deptGoodsCategoryId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果当前类目有设置倒计时小时数,直接返回
|
||||
if (currentCategory.getCountdownHours() != null) {
|
||||
log.info("找到类目[{}]的倒计时小时数: {}", currentCategory.getGoodsCategoryName(), currentCategory.getCountdownHours());
|
||||
return currentCategory.getCountdownHours();
|
||||
}
|
||||
|
||||
// 如果当前类目没有设置,查找上级类目
|
||||
if (currentCategory.getParentCategoryId() != null) {
|
||||
log.info("类目[{}]未设置倒计时小时数,查找上级类目ID: {}", currentCategory.getGoodsCategoryName(), currentCategory.getParentCategoryId());
|
||||
|
||||
// 通过商品类目ID查找对应的分公司类目
|
||||
DeptGoodsCategory parentDeptCategory = deptGoodsCategoryMapper.selectOneByGoodsCategoryId(currentCategory.getParentCategoryId());
|
||||
if (parentDeptCategory != null) {
|
||||
return findCountdownHoursRecursively(parentDeptCategory.getDeptGoodsCategoryId());
|
||||
} else {
|
||||
log.warn("未找到上级类目对应的分公司类目,商品类目ID: {}", currentCategory.getParentCategoryId());
|
||||
}
|
||||
} else {
|
||||
log.info("类目[{}]已是顶级类目,无上级类目可查找", currentCategory.getGoodsCategoryName());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,8 @@ import com.ghy.goods.service.GoodsAreaService;
|
|||
import com.ghy.goods.service.GoodsImgsService;
|
||||
import com.ghy.goods.service.GoodsService;
|
||||
import com.ghy.goods.service.GoodsStandardService;
|
||||
import com.ghy.goods.service.DeptGoodsCategoryService;
|
||||
import com.ghy.goods.domain.DeptGoodsCategory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
|
|
@ -44,8 +40,6 @@ public class GoodsServiceImpl implements GoodsService {
|
|||
private GoodsImgsService goodsImgsService;
|
||||
@Resource
|
||||
private GoodsStandardService goodsStandardService;
|
||||
@Resource
|
||||
private DeptGoodsCategoryService deptGoodsCategoryService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
|
|
@ -216,40 +210,4 @@ public class GoodsServiceImpl implements GoodsService {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Goods> selectGoodsByServiceCategory(Long goodsId) {
|
||||
// Get goods info by goodsId
|
||||
Goods goods = selectById(goodsId);
|
||||
if (goods == null || goods.getDeptGoodsCategoryId() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Get DeptGoodsCategory by deptGoodsCategoryId to obtain serviceCategory
|
||||
DeptGoodsCategory deptGoodsCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(goods.getDeptGoodsCategoryId());
|
||||
if (deptGoodsCategory == null || deptGoodsCategory.getServiceCategoryId() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Query all goods using this service category ID
|
||||
Goods queryGoods = new Goods();
|
||||
queryGoods.setDeptGoodsCategoryId(deptGoodsCategory.getServiceCategoryId());
|
||||
queryGoods.setStatus(0); // Only query active goods
|
||||
List<Goods> goodsList = selectGoodsList(queryGoods);
|
||||
|
||||
// If no goods found, try using fourth-level category query
|
||||
if (goodsList.isEmpty()) {
|
||||
DeptGoodsCategory serviceDeptCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(deptGoodsCategory.getServiceCategoryId());
|
||||
if (serviceDeptCategory != null) {
|
||||
// Use GoodsStandardService method to get goods list
|
||||
goodsList = goodsStandardService.selectGoodsByDeptGoodsCategoryId(serviceDeptCategory.getDeptGoodsCategoryId());
|
||||
// Filter to keep only active goods
|
||||
goodsList = goodsList.stream()
|
||||
.filter(item -> item.getStatus() != null && item.getStatus() == 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
return goodsList;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package com.ghy.goods.service.impl;
|
||||
|
||||
import com.ghy.goods.domain.Goods;
|
||||
import com.ghy.goods.domain.GoodsStandard;
|
||||
import com.ghy.goods.mapper.GoodsStandardMapper;
|
||||
import com.ghy.goods.request.AppGoodsRequest;
|
||||
import com.ghy.goods.service.GoodsService;
|
||||
import com.ghy.goods.service.GoodsStandardService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -29,9 +27,6 @@ public class GoodsStandardServiceImpl implements GoodsStandardService {
|
|||
@Resource
|
||||
private GoodsStandardMapper goodsStandardMapper;
|
||||
|
||||
@Resource
|
||||
private GoodsService goodsService;
|
||||
|
||||
@Override
|
||||
public GoodsStandard selectById(Long goodsStandardId) {
|
||||
return goodsStandardMapper.selectById(goodsStandardId);
|
||||
|
|
@ -91,24 +86,4 @@ public class GoodsStandardServiceImpl implements GoodsStandardService {
|
|||
public int update(GoodsStandard goodsStandard) {
|
||||
return goodsStandardMapper.update(goodsStandard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GoodsStandard> selectByStandardNameLike(String standardName) {
|
||||
return goodsStandardMapper.selectByStandardNameLike(standardName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Goods> selectGoodsByDeptGoodsCategoryId(Long deptGoodsCategoryId) {
|
||||
// 1. 根据deptGoodsCategoryId查询所有商品规格
|
||||
List<GoodsStandard> goodsStandardList = goodsStandardMapper.selectByDeptGoodsCategoryId(deptGoodsCategoryId);
|
||||
|
||||
// 2. 提取所有不重复的goodsId
|
||||
Set<Long> goodsIds = goodsStandardList.stream()
|
||||
.map(GoodsStandard::getGoodsId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
||||
// 4. 通过goodsId批量查询Goods实体
|
||||
return goodsService.selectByIds(goodsIds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@
|
|||
<result property="deptMoney" column="dept_money"/>
|
||||
<result property="retainRate" column="retain_rate"/>
|
||||
<result property="retainMoney" column="retain_money"/>
|
||||
<result property="serviceCategoryId" column="service_category_id"/>
|
||||
<result property="serviceCategoryName" column="service_category_name"/>
|
||||
<result property="countdownHours" column="countdown_hours"/>
|
||||
<!-- pc扣点部分 start-->
|
||||
<result property="pcOneRate" column="pc_one_rate"/>
|
||||
<result property="pcTwoRate" column="pc_two_rate"/>
|
||||
|
|
@ -48,8 +45,7 @@
|
|||
SELECT dept_goods_category_id, dept_id, goods_category_id, category_sort, is_hot, cover, hot_name,
|
||||
simple_goods_category_name, is_sure, is_cert, one_rate, two_rate, three_rate, dept_rate,
|
||||
pc_one_rate, pc_two_rate, pc_three_rate, pc_dept_rate, pc_dept_money, pc_retain_rate, pc_retain_money,
|
||||
dept_money, retain_rate, retain_money, create_by, create_time, remark, service_category_id, service_category_name,
|
||||
countdown_hours
|
||||
dept_money, retain_rate, retain_money, create_by, create_time, remark
|
||||
FROM dept_goods_category
|
||||
</sql>
|
||||
|
||||
|
|
@ -59,7 +55,6 @@
|
|||
dgc.is_hot, dgc.cover, dgc.hot_name, dgc.is_sure, dgc.is_cert, dgc.dept_rate, dgc.dept_money,
|
||||
dgc.retain_rate, dgc.retain_money, dgc.simple_goods_category_name,
|
||||
dgc.pc_one_rate, dgc.pc_two_rate, dgc.pc_three_rate, dgc.pc_dept_rate, dgc.pc_dept_money, dgc.pc_retain_rate, dgc.pc_retain_money,
|
||||
dgc.service_category_id, dgc.service_category_name, dgc.countdown_hours,
|
||||
gc.goods_category_name, gc.level, gc.parent_category_id, gc.type, gc.status
|
||||
FROM dept_goods_category dgc LEFT JOIN goods_category gc ON dgc.goods_category_id = gc.goods_category_id
|
||||
</sql>
|
||||
|
|
@ -97,12 +92,6 @@
|
|||
<if test="deptMoney != null">dept_money = #{deptMoney},</if>
|
||||
<if test="retainRate != null and retainRate != ''">retain_rate = #{retainRate},</if>
|
||||
<if test="retainMoney != null">retain_money = #{retainMoney},</if>
|
||||
<if test="serviceCategoryId != null">service_category_id = #{serviceCategoryId},</if>
|
||||
<if test="serviceCategoryName != null and serviceCategoryName != ''">service_category_name = #{serviceCategoryName},</if>
|
||||
<choose>
|
||||
<when test="countdownHours != null">countdown_hours = #{countdownHours},</when>
|
||||
<otherwise>countdown_hours = NULL,</otherwise>
|
||||
</choose>
|
||||
|
||||
<if test="pcDeptRate != null and pcDeptRate != ''">pc_dept_rate = #{pcDeptRate},</if>
|
||||
<if test="pcDeptMoney != null">pc_dept_money = #{pcDeptMoney},</if>
|
||||
|
|
@ -144,9 +133,6 @@
|
|||
<if test="deptMoney != null and deptMoney != ''">dept_money,</if>
|
||||
<if test="retainRate != null and retainRate != ''">retain_rate,</if>
|
||||
<if test="retainMoney != null and retainMoney != ''">retain_money,</if>
|
||||
<if test="serviceCategoryId != null">service_category_id,</if>
|
||||
<if test="serviceCategoryName != null and serviceCategoryName != ''">service_category_name,</if>
|
||||
countdown_hours,
|
||||
|
||||
<if test="pcDeptRate != null and pcDeptRate != ''">pc_dept_rate,</if>
|
||||
<if test="pcDeptMoney != null and pcDeptMoney != ''">pc_dept_money,</if>
|
||||
|
|
@ -172,9 +158,6 @@
|
|||
<if test="deptMoney != null and deptMoney != ''">#{deptMoney},</if>
|
||||
<if test="retainRate != null and retainRate != ''">#{retainRate},</if>
|
||||
<if test="retainMoney != null and retainMoney != ''">#{retainMoney},</if>
|
||||
<if test="serviceCategoryId != null">#{serviceCategoryId},</if>
|
||||
<if test="serviceCategoryName != null and serviceCategoryName != ''">#{serviceCategoryName},</if>
|
||||
#{countdownHours},
|
||||
|
||||
<if test="pcDeptRate != null and pcDeptRate != ''">#{pcDeptRate},</if>
|
||||
<if test="pcDeptMoney != null and pcDeptMoney != ''">#{pcDeptMoney},</if>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
<id property="goodsId" column="goods_id" />
|
||||
<result property="goodsCode" column="goods_code" />
|
||||
<result property="deptId" column="dept_id" />
|
||||
<result property="shopId" column="shop_id" />
|
||||
<result property="shopName" column="shop_name" />
|
||||
<result property="goodsName" column="goods_name" />
|
||||
<result property="goodsDesc" column="goods_desc" />
|
||||
<result property="goodsUnit" column="goods_unit" />
|
||||
|
|
@ -28,21 +26,17 @@
|
|||
<result property="remark" column="remark" />
|
||||
<result property="areaDesc" column="area_desc" />
|
||||
<result property="keyword" column="keyword" />
|
||||
<result property="type" column="type" />
|
||||
<result property="storeService" column="store_service" />
|
||||
<result property="installService" column="install_service" />
|
||||
<result property="deliveryService" column="delivery_service" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectGoods">
|
||||
SELECT goods_id, goods_code, dept_id, shop_id, shop_name, goods_name, goods_desc, warranty_period, serv_activity, goods_unit, expect_duration, goods_sort, worker_id,
|
||||
dept_goods_category_id, goods_img_url, goods_video_url, status, create_by, create_time, remark, area_desc, keyword,type,store_service,install_service,delivery_service
|
||||
SELECT goods_id, goods_code, dept_id, goods_name, goods_desc, warranty_period, serv_activity, goods_unit, expect_duration, goods_sort, worker_id,
|
||||
dept_goods_category_id, goods_img_url, goods_video_url, status, create_by, create_time, remark, area_desc, keyword
|
||||
FROM goods
|
||||
</sql>
|
||||
|
||||
<sql id="selectGoodsWithArea">
|
||||
SELECT DISTINCT g.goods_id, goods_code, dept_id, shop_id, shop_name, goods_name, goods_desc, goods_sort, worker_id, goods_unit, warranty_period, serv_activity, expect_duration,
|
||||
dept_goods_category_id, goods_img_url, goods_video_url, status, g.create_by, g.create_time, g.remark, g.area_desc, g.keyword,g.type,g.store_service,g.install_service,g.delivery_service
|
||||
SELECT DISTINCT g.goods_id, goods_code, dept_id, goods_name, goods_desc, goods_sort, worker_id, goods_unit, warranty_period, serv_activity, expect_duration,
|
||||
dept_goods_category_id, goods_img_url, goods_video_url, status, g.create_by, g.create_time, g.remark, g.area_desc, g.keyword
|
||||
FROM goods g
|
||||
LEFT JOIN goods_area ga ON g.goods_id = ga.goods_id
|
||||
</sql>
|
||||
|
|
@ -50,26 +44,20 @@
|
|||
<update id="updateGoods" parameterType="com.ghy.goods.domain.Goods">
|
||||
UPDATE goods
|
||||
<set>
|
||||
<if test="goodsName != null">goods_name = #{goodsName},</if>
|
||||
<if test="goodsDesc != null">goods_desc = #{goodsDesc},</if>
|
||||
<if test="goodsSort != null">goods_sort = #{goodsSort},</if>
|
||||
<if test="goodsName != null and goodsName != ''">goods_name = #{goodsName},</if>
|
||||
<if test="goodsDesc != null and goodsDesc != ''">goods_desc = #{goodsDesc},</if>
|
||||
<if test="goodsSort != null and goodsSort != ''">goods_sort = #{goodsSort},</if>
|
||||
<if test="deptGoodsCategoryId != null and deptGoodsCategoryId != ''">dept_goods_category_id = #{deptGoodsCategoryId},</if>
|
||||
<if test="goodsImgUrl != null">goods_img_url = #{goodsImgUrl},</if>
|
||||
<if test="goodsVideoUrl != null">goods_video_url = #{goodsVideoUrl},</if>
|
||||
<if test="goodsImgUrl != null and goodsImgUrl != ''">goods_img_url = #{goodsImgUrl},</if>
|
||||
<if test="goodsVideoUrl != null and goodsVideoUrl != ''">goods_video_url = #{goodsVideoUrl},</if>
|
||||
<if test="status != null">`status` = #{status},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="goodsUnit != null">goods_unit = #{goodsUnit},</if>
|
||||
<if test="warrantyPeriod != null">warranty_period = #{warrantyPeriod},</if>
|
||||
<if test="servActivity != null">serv_activity = #{servActivity},</if>
|
||||
<if test="expectDuration != null">expect_duration = #{expectDuration},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="areaDesc != null">area_desc = #{areaDesc},</if>
|
||||
<if test="type != null">type = #{type},</if>
|
||||
store_service = #{storeService},
|
||||
install_service = #{installService},
|
||||
delivery_service = #{deliveryService},
|
||||
shop_id = #{shopId},
|
||||
<if test="shopName != null">shop_name = #{shopName},</if>
|
||||
<if test="goodsUnit != null and goodsUnit != ''">goods_unit = #{goodsUnit},</if>
|
||||
<if test="warrantyPeriod != null and warrantyPeriod != ''">warranty_period = #{warrantyPeriod},</if>
|
||||
<if test="servActivity != null and servActivity!=''">serv_activity = #{servActivity},</if>
|
||||
<if test="expectDuration != null and expectDuration!=''">expect_duration = #{expectDuration},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
<if test="areaDesc != null and areaDesc != ''">area_desc = #{areaDesc},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
WHERE goods_id = #{goodsId}
|
||||
|
|
@ -85,8 +73,6 @@
|
|||
insert into goods(
|
||||
<if test="goodsCode != null and goodsCode != ''">goods_code,</if>
|
||||
<if test="deptId != null and deptId != ''">dept_id,</if>
|
||||
<if test="shopId != null">shop_id,</if>
|
||||
<if test="shopName != null and shopName != ''">shop_name,</if>
|
||||
<if test="goodsName != null and goodsName != ''">goods_name,</if>
|
||||
<if test="goodsDesc != null and goodsDesc != ''">goods_desc,</if>
|
||||
<if test="goodsUnit != null and goodsUnit != ''">goods_unit,</if>
|
||||
|
|
@ -102,16 +88,10 @@
|
|||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="areaDesc != null and areaDesc != ''">area_desc,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="type != null">type,</if>
|
||||
<if test="storeService != null">store_service,</if>
|
||||
<if test="installService != null">install_service,</if>
|
||||
<if test="deliveryService != null">delivery_service,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="goodsCode != null and goodsCode != ''">#{goodsCode},</if>
|
||||
<if test="deptId != null and deptId != ''">#{deptId},</if>
|
||||
<if test="shopId != null">#{shopId},</if>
|
||||
<if test="shopName != null and shopName != ''">#{shopName},</if>
|
||||
<if test="goodsName != null and goodsName != ''">#{goodsName},</if>
|
||||
<if test="goodsDesc != null and goodsDesc != ''">#{goodsDesc},</if>
|
||||
<if test="goodsUnit != null and goodsUnit != ''">#{goodsUnit},</if>
|
||||
|
|
@ -127,10 +107,6 @@
|
|||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="areaDesc != null and areaDesc != ''">#{areaDesc},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="type != null">#{type},</if>
|
||||
<if test="storeService != null">#{storeService},</if>
|
||||
<if test="installService != null">#{installService},</if>
|
||||
<if test="deliveryService != null">#{deliveryService},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
|
@ -167,38 +143,15 @@
|
|||
AND (goods_name like concat('%', #{goodsName}, '%')
|
||||
OR g.goods_desc LIKE concat('%', #{goodsName}, '%'))
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
g.goods_name like concat('%', #{keyword}, '%')
|
||||
OR g.goods_desc LIKE concat('%', #{keyword}, '%')
|
||||
OR sa.area_name LIKE concat('%', #{keyword}, '%')
|
||||
OR sa.merger_name LIKE concat('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="workerId != null">
|
||||
AND worker_id = #{workerId}
|
||||
</if>
|
||||
<if test="type != null">
|
||||
AND g.type = #{type}
|
||||
</if>
|
||||
<if test="goodsId != null">
|
||||
AND g.goods_id = #{goodsId}
|
||||
</if>
|
||||
<if test="shopId != null">
|
||||
AND g.shop_id = #{shopId}
|
||||
</if>
|
||||
<if test="storeService != null">
|
||||
AND g.store_service = #{storeService}
|
||||
</if>
|
||||
<if test="goodsIds != null and goodsIds.size > 0">
|
||||
AND g.goods_id in
|
||||
<foreach collection="goodsIds" item="goodsIdItem" open="(" separator="," close=")">
|
||||
#{goodsIdItem}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
/* 默认生成时间排序 */
|
||||
order by create_time
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="imageUrl" column="image_url"/>
|
||||
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectGoodsStandard">
|
||||
|
|
@ -40,8 +40,7 @@
|
|||
status,
|
||||
update_by,
|
||||
update_time,
|
||||
remark,
|
||||
image_url
|
||||
remark
|
||||
FROM goods_standard
|
||||
</sql>
|
||||
|
||||
|
|
@ -93,7 +92,6 @@
|
|||
<if test="goodsNum != null and goodsNum != ''">goods_num,</if>
|
||||
<if test="status != null and status != ''">status,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="imageUrl != null and imageUrl != ''">image_url,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
|
|
@ -108,7 +106,6 @@
|
|||
<if test="goodsNum != null and goodsNum != ''">#{goodsNum},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="imageUrl != null and imageUrl != ''">#{imageUrl},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
|
|
@ -117,12 +114,12 @@
|
|||
<insert id="batchInsert" parameterType="list">
|
||||
INSERT INTO goods_standard (
|
||||
goods_standard_name, goods_id, dept_goods_category_id, goods_price, ext_money, discount_price, group_price, goods_unit, goods_num,
|
||||
sale_num, status, remark, image_url, create_by, create_time )
|
||||
sale_num, status, remark, create_by, create_time )
|
||||
VALUES
|
||||
<foreach collection="goodsStandards" separator="," item="goodsStandard">
|
||||
(
|
||||
#{goodsStandard.goodsStandardName}, #{goodsStandard.goodsId}, #{goodsStandard.deptGoodsCategoryId}, #{goodsStandard.goodsPrice}, #{goodsStandard.extMoney}, #{goodsStandard.discountPrice},
|
||||
#{goodsStandard.groupPrice}, #{goodsStandard.goodsUnit}, #{goodsStandard.goodsNum}, 0, #{goodsStandard.status}, #{goodsStandard.remark}, #{goodsStandard.imageUrl}, #{goodsStandard.createBy}, sysdate()
|
||||
#{goodsStandard.groupPrice}, #{goodsStandard.goodsUnit}, #{goodsStandard.goodsNum}, 0, #{goodsStandard.status}, #{goodsStandard.remark}, #{goodsStandard.createBy}, sysdate()
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
|
@ -161,29 +158,8 @@
|
|||
<if test="goodsNum != null">goods_num = #{goodsNum},</if>
|
||||
<if test="status != null">`status` = #{status},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="imageUrl != null">image_url = #{imageUrl},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
WHERE goods_standard_id = #{goodsStandardId}
|
||||
</update>
|
||||
|
||||
<!-- 根据规格名称模糊查询商品规格 -->
|
||||
<select id="selectByStandardNameLike" resultMap="GoodsStandardResult">
|
||||
<include refid="selectGoodsStandard"/>
|
||||
<where>
|
||||
<if test="standardName != null and standardName != ''">
|
||||
AND goods_standard_name LIKE CONCAT('%', #{standardName}, '%')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 根据部门商品分类ID查询商品规格 -->
|
||||
<select id="selectByDeptGoodsCategoryId" resultMap="GoodsStandardResult">
|
||||
<include refid="selectGoodsStandard"/>
|
||||
<where>
|
||||
<if test="deptGoodsCategoryId != null">
|
||||
AND dept_goods_category_id = #{deptGoodsCategoryId}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ public class AfterServiceRecord extends BaseEntity
|
|||
@Excel(name = "操作原因:1为申请退款,2为发起售后")
|
||||
private Long operType;
|
||||
|
||||
/** 师傅反馈结果:0为拒绝,1为同意,2为上门补做/重做,3重做/补做完成 */
|
||||
@Excel(name = "师傅反馈结果:0为拒绝,1为同意,2为上门补做/重做,3重做/补做完成")
|
||||
/** 师傅反馈结果:0为拒绝,1为同意,2为上门补做/重做 */
|
||||
@Excel(name = "师傅反馈结果:0为拒绝,1为同意,2为上门补做/重做")
|
||||
private Long workerFeedbackResult;
|
||||
|
||||
/** 师傅反馈原因类型:1为客户原因,2为师傅原因,3为其他 */
|
||||
|
|
@ -52,37 +52,16 @@ public class AfterServiceRecord extends BaseEntity
|
|||
@Excel(name = "师傅反馈原因描述")
|
||||
private String workerFeedbackReason;
|
||||
|
||||
/** 师傅反馈图片 */
|
||||
@Excel(name = "师傅反馈图片")
|
||||
private String workerFeedbackImages;
|
||||
|
||||
/** 本单退款金额 */
|
||||
@Excel(name = "本单退款金额")
|
||||
private BigDecimal refund;
|
||||
|
||||
public BigDecimal getRefund() {
|
||||
return refund;
|
||||
}
|
||||
|
||||
public void setRefund(BigDecimal refund) {
|
||||
this.refund = refund;
|
||||
}
|
||||
|
||||
/** 售后状态:0-进行中,1-已完成,2-已取消,3-已超时 */
|
||||
@Excel(name = "售后状态:0-进行中,1-已完成,2-已取消,3-已超时")
|
||||
private Integer afterServiceStatus;
|
||||
|
||||
|
||||
/** 协商后的退款金额 */
|
||||
@Excel(name = "协商后的退款金额")
|
||||
private BigDecimal agreedRefund;
|
||||
|
||||
/** 平台退款金额 */
|
||||
@Excel(name = "平台退款金额")
|
||||
private BigDecimal platformRefund;
|
||||
|
||||
/** 客户最终确认:0为不同意,1为同意 */
|
||||
@Excel(name = "客户最终确认:0为不同意,1为同意,2为取消")
|
||||
@Excel(name = "客户最终确认:0为不同意,1为同意")
|
||||
private Long customerFinalCheck;
|
||||
|
||||
@Excel(name = "最终原路返还的金额")
|
||||
|
|
@ -90,136 +69,6 @@ public class AfterServiceRecord extends BaseEntity
|
|||
|
||||
private Date refundApplyTime;
|
||||
|
||||
/** 客户是否同意上门重做方案:0-未处理,1-同意,2-不同意 */
|
||||
@Excel(name = "客户是否同意上门重做方案:0-未处理,1-同意,2-不同意")
|
||||
private Integer customerAgreeRedo;
|
||||
|
||||
/** 客户操作时间 */
|
||||
@Excel(name = "客户操作时间")
|
||||
private Date customerOperationTime;
|
||||
|
||||
/** 重做/补做完成时间 */
|
||||
@Excel(name = "重做/补做完成时间")
|
||||
private Date redoCompleteTime;
|
||||
|
||||
/** 重做/补做完成备注 */
|
||||
@Excel(name = "重做/补做完成备注")
|
||||
private String redoCompleteRemark;
|
||||
|
||||
/** 重做/补做完成图片 */
|
||||
@Excel(name = "重做/补做完成图片")
|
||||
private String redoCompleteImages;
|
||||
|
||||
/** 是否自动处理:0-否,1-是(用于防止重复处理) */
|
||||
@Excel(name = "是否自动处理:0-否,1-是")
|
||||
private Integer isAutoProcessed;
|
||||
|
||||
/** 售后大类:1-商品售后,2-服务售后 */
|
||||
@Excel(name = "售后大类:1-服务售后,2-商品售后")
|
||||
private Integer afterServiceCategory;
|
||||
|
||||
/** 售后类型:1-未收到货退单退款,2-未收到货退款,3-已收到货退款退货 */
|
||||
@Excel(name = "售后类型:1-未收到货退单退款,2-未收到货退款,3-已收到货退款退货")
|
||||
private Integer afterServiceType;
|
||||
|
||||
/** 退货状态:0-待处理,1-同意退货,2-拒绝退货,3-客户已发货,4-商家已收货,5-退款完成 */
|
||||
@Excel(name = "退货状态:0-待处理,1-同意退货,2-拒绝退货,3-客户已发货,4-商家已收货,5-退款完成")
|
||||
private Integer returnStatus;
|
||||
|
||||
/** 退货地址 */
|
||||
@Excel(name = "退货地址")
|
||||
private String returnAddress;
|
||||
|
||||
/** 退货联系人 */
|
||||
@Excel(name = "退货联系人")
|
||||
private String returnContact;
|
||||
|
||||
/** 退货联系电话 */
|
||||
@Excel(name = "退货联系电话")
|
||||
private String returnPhone;
|
||||
|
||||
/** 退货类型:1=发快递/物流,2=送货上门,3=自提 */
|
||||
@Excel(name = "退货类型:1=发快递/物流,2=送货上门,3=自提")
|
||||
private Integer returnType;
|
||||
|
||||
/** 退货备注 */
|
||||
@Excel(name = "退货备注")
|
||||
private String returnRemark;
|
||||
|
||||
/** 退货图片 */
|
||||
@Excel(name = "退货图片")
|
||||
private String returnImages;
|
||||
|
||||
/** 退货物流单号 */
|
||||
@Excel(name = "退货物流单号")
|
||||
private String returnTrackingNumber;
|
||||
|
||||
/** 退货发货时间 */
|
||||
@Excel(name = "退货发货时间")
|
||||
private Date returnShipTime;
|
||||
|
||||
/** 商家收货时间 */
|
||||
@Excel(name = "商家收货时间")
|
||||
private Date merchantReceiveTime;
|
||||
|
||||
/** 师傅重发/补发方案:1-重发/补发,您无需退货,2-重发/补发前,您需先退货,3-请您退回商品,给您换货,4-请退回商品,为您售后换货 */
|
||||
@Excel(name = "师傅重发/补发方案")
|
||||
private Integer workerResendPlan;
|
||||
|
||||
/** 师傅选择重发/补发方案时间 */
|
||||
@Excel(name = "师傅选择重发/补发方案时间")
|
||||
private Date workerResendPlanTime;
|
||||
|
||||
/** 师傅重发/补发时间 */
|
||||
@Excel(name = "师傅重发/补发时间", cellType = Excel.ColumnType.STRING)
|
||||
private Date workerResendTime;
|
||||
|
||||
/** 师傅重发/补发方式:1-快递/物流,2-送货上门,3-自提 */
|
||||
@Excel(name = "师傅重发/补发方式:1-快递/物流,2-送货上门,3-自提")
|
||||
private Integer workerResendType;
|
||||
|
||||
/** 师傅重发/补发物流单号(快递/物流时必填) */
|
||||
@Excel(name = "师傅重发/补发物流单号")
|
||||
private String workerResendTrackingNumber;
|
||||
|
||||
/** 师傅收货状态:1-未发货,2-已发货在途,3-已收货,4-售后保障期 */
|
||||
@Excel(name = "师傅收货状态:1-未发货,2-已发货在途,3-已收货,4-售后保障期")
|
||||
private Integer workerReceiveStatus;
|
||||
|
||||
/** 师傅同意处理方式:1-即时退单退款,2-货物拦截后退单退款,3-快递返回货物后退单退款,4-退回货物后退单退款 */
|
||||
@Excel(name = "师傅同意处理方式:1-即时退单退款,2-货物拦截后退单退款,3-快递返回货物后退单退款,4-退回货物后退单退款")
|
||||
private Integer workerAgreeType;
|
||||
|
||||
/** 师傅收货确认:0-未收货,1-已收货 */
|
||||
@Excel(name = "师傅收货确认:0-未收货,1-已收货")
|
||||
private Integer workerReceiveConfirm;
|
||||
|
||||
/** 师傅重发/补发备注 */
|
||||
@Excel(name = "师傅重发/补发备注")
|
||||
private String workerResendRemark;
|
||||
|
||||
/** 师傅重发/补发图片 */
|
||||
@Excel(name = "师傅重发/补发图片")
|
||||
private String workerResendImages;
|
||||
|
||||
/** 客户不同意图片 */
|
||||
@Excel(name = "客户不同意图片")
|
||||
private String customerDisagreeImages;
|
||||
|
||||
/** 客户不同意理由 */
|
||||
@Excel(name = "客户不同意理由")
|
||||
private String customerDisagreeReason;
|
||||
|
||||
/** 售后纠纷平台处理原因 */
|
||||
@Excel(name = "售后纠纷平台处理原因")
|
||||
private String platformHandleReason;
|
||||
|
||||
/** 子单号(用于显示,非数据库字段) */
|
||||
private String orderDetailCode;
|
||||
|
||||
/** 子单ID列表(用于IN查询,非数据库字段) */
|
||||
private String orderDetailIds;
|
||||
|
||||
private boolean excludeAfterServiceFinished;
|
||||
|
||||
private List<AfterServiceImgs> imgsList;
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
package com.ghy.order.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 物流信息实体类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class LogisticsInfo {
|
||||
|
||||
/**
|
||||
* 快递单号
|
||||
*/
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 快递公司编码
|
||||
*/
|
||||
private String expressCode;
|
||||
|
||||
/**
|
||||
* 快递公司名称
|
||||
*/
|
||||
private String expressName;
|
||||
|
||||
/**
|
||||
* 物流状态:0=在途,1=揽收,2=疑难,3=签收,4=退签,5=派件,6=退回
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 物流状态描述
|
||||
*/
|
||||
private String statusDesc;
|
||||
|
||||
/**
|
||||
* 物流轨迹信息
|
||||
*/
|
||||
private List<LogisticsTrace> traces;
|
||||
|
||||
/**
|
||||
* 查询时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date queryTime;
|
||||
|
||||
/**
|
||||
* 是否查询成功
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 物流轨迹详情
|
||||
*/
|
||||
@Data
|
||||
public static class LogisticsTrace {
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date time;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,4 @@ public class OrderAttachmentRecord {
|
|||
*/
|
||||
private String paymentId;
|
||||
|
||||
|
||||
private Long financialChangeRecordId;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.ghy.order.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ghy.common.annotation.Excel;
|
||||
import com.ghy.common.core.domain.BaseEntity;
|
||||
import com.ghy.common.enums.OrderStatus;
|
||||
|
|
@ -74,20 +73,6 @@ public class OrderDetail extends BaseEntity {
|
|||
|
||||
private String orderImgs;
|
||||
|
||||
@Excel(name = "交货图片", cellType = Excel.ColumnType.STRING)
|
||||
private String handoverImages;
|
||||
|
||||
@Excel(name = "交货备注", cellType = Excel.ColumnType.STRING)
|
||||
private String handoverRemark;
|
||||
|
||||
@Excel(name = "进入确认中时间", cellType = Excel.ColumnType.STRING)
|
||||
private Date confirmStartTime;
|
||||
|
||||
/**
|
||||
* 确认中倒计时剩余时间(毫秒)- 售后暂停时记录
|
||||
*/
|
||||
private Long confirmTimeoutRemainingTime;
|
||||
|
||||
// 商品归属师傅
|
||||
private Worker goodsWorker;
|
||||
// 接单师傅
|
||||
|
|
@ -192,11 +177,6 @@ public class OrderDetail extends BaseEntity {
|
|||
* */
|
||||
private Integer afterTimeout;
|
||||
|
||||
/**
|
||||
* 查询时是否包含售后超时条件
|
||||
*/
|
||||
private Boolean includeAfterTimeout;
|
||||
|
||||
/**
|
||||
* 超时扣款次数
|
||||
*/
|
||||
|
|
@ -236,79 +216,4 @@ public class OrderDetail extends BaseEntity {
|
|||
|
||||
private String isCall;
|
||||
|
||||
/**
|
||||
* 延期次数,最大2次
|
||||
*/
|
||||
private Integer delayCount;
|
||||
|
||||
/**
|
||||
* 退单原因
|
||||
*/
|
||||
private String returnReason;
|
||||
|
||||
/**
|
||||
* 退单原因详情
|
||||
*/
|
||||
private String returnReasonDetail;
|
||||
|
||||
/**
|
||||
* 退单图片
|
||||
*/
|
||||
private String returnImages;
|
||||
|
||||
/**
|
||||
* 师傅备注
|
||||
*/
|
||||
private String workerRemark;
|
||||
|
||||
/**
|
||||
* 售后状态:0-无售后,1-售后纠纷
|
||||
*/
|
||||
@Excel(name = "售后状态:0-无售后,1-售后纠纷,2-售后已完成,3-售后已取消")
|
||||
private Integer afterServiceStatus;
|
||||
|
||||
/**
|
||||
* 分账倒计时结束时间
|
||||
*/
|
||||
@Excel(name = "分账倒计时结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date shareAccountCountdownEndTime;
|
||||
|
||||
/**
|
||||
* 分账倒计时时长(小时)
|
||||
*/
|
||||
@Excel(name = "分账倒计时时长(小时)")
|
||||
private Integer shareAccountCountdownDuration;
|
||||
|
||||
/**
|
||||
* 发货类型 - 订单的发货方式
|
||||
*/
|
||||
@Excel(name = "发货类型")
|
||||
private Integer deliveryType;
|
||||
|
||||
/**
|
||||
* 发货备注 - 发货相关备注信息
|
||||
*/
|
||||
@Excel(name = "发货备注")
|
||||
private String deliveryRemark;
|
||||
|
||||
/**
|
||||
* 发货图片 - 发货凭证图片
|
||||
*/
|
||||
@Excel(name = "发货图片")
|
||||
private String deliveryImages;
|
||||
|
||||
/**
|
||||
* 快递单号 - 物流跟踪单号
|
||||
*/
|
||||
@Excel(name = "快递单号")
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 订单图片 - 客户下单时上传的图片
|
||||
*/
|
||||
@Excel(name = "订单图片")
|
||||
private String orderImages;
|
||||
|
||||
//是否立即发货
|
||||
private Integer isQuicklyDelivery;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,8 +174,6 @@ public class OrderMaster extends BaseEntity {
|
|||
|
||||
private BigDecimal serverMoney;
|
||||
|
||||
private BigDecimal serverGoodsMoney;
|
||||
|
||||
private Boolean searchAfterList;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
|
@ -237,157 +235,4 @@ public class OrderMaster extends BaseEntity {
|
|||
private String countryName;
|
||||
|
||||
private String streetName;
|
||||
|
||||
private Integer withdrawn;
|
||||
|
||||
private Long serverGoodsId;
|
||||
|
||||
private Long goodsOrderMasterId;
|
||||
|
||||
/**
|
||||
* 服务店铺ID
|
||||
*/
|
||||
private Long serviceShopId;
|
||||
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 是否已派发服务订单:0=未派发,1=已派发
|
||||
*/
|
||||
private Integer hasServiceOrder;
|
||||
|
||||
/**
|
||||
* 下单图片
|
||||
*/
|
||||
@Excel(name = "下单图片", cellType = Excel.ColumnType.STRING)
|
||||
private String orderImages;
|
||||
|
||||
/**
|
||||
* 是否发货到服务店:0=否,1=是
|
||||
*/
|
||||
@Excel(name = "是否发货到服务店", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Integer isDeliveryToStore;
|
||||
|
||||
/**
|
||||
* 发货类型:1=发快递/物流,2=送货上门,3=自提
|
||||
*/
|
||||
@Excel(name = "发货类型", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "1=发快递/物流,2=送货上门,3=自提")
|
||||
private Integer deliveryType;
|
||||
|
||||
/**
|
||||
* 发货备注
|
||||
*/
|
||||
@Excel(name = "发货备注", cellType = Excel.ColumnType.STRING)
|
||||
private String deliveryRemark;
|
||||
|
||||
/**
|
||||
* 发货图片
|
||||
*/
|
||||
@Excel(name = "发货图片", cellType = Excel.ColumnType.STRING)
|
||||
private String deliveryImages;
|
||||
|
||||
/**
|
||||
* 是否已开票:0=是,1=否
|
||||
*/
|
||||
@Excel(name = "是否已开票", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=是,1=否")
|
||||
private Integer isInvoiced;
|
||||
|
||||
/**
|
||||
* 是否需要开票:0=不需要,1=需要
|
||||
*/
|
||||
@Excel(name = "是否需要开票", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=不需要,1=需要")
|
||||
private Integer isNeedBill;
|
||||
|
||||
/**
|
||||
* 原师傅id(转单前的师傅)
|
||||
*/
|
||||
@Excel(name = "原师傅id", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Long originalWorkerId;
|
||||
|
||||
/**
|
||||
* 退单原因
|
||||
*/
|
||||
private String returnReason;
|
||||
|
||||
/**
|
||||
* 退单原因详情
|
||||
*/
|
||||
private String returnReasonDetail;
|
||||
|
||||
/**
|
||||
* 退单图片
|
||||
*/
|
||||
private String returnImages;
|
||||
|
||||
/**
|
||||
* 师傅备注
|
||||
*/
|
||||
private String workerRemark;
|
||||
|
||||
/**
|
||||
* 售后状态:0-无售后,1-售后纠纷
|
||||
*/
|
||||
@Excel(name = "售后状态:0-无售后,1-售后纠纷,2-售后已完成,3-售后已取消")
|
||||
private Integer afterServiceStatus;
|
||||
|
||||
/**
|
||||
* 分账倒计时结束时间(workFinishTime + 设置的倒计时字段)
|
||||
*/
|
||||
@Excel(name = "分账倒计时结束时间", cellType = Excel.ColumnType.STRING)
|
||||
|
||||
private Date shareAccountCountdownEndTime;
|
||||
|
||||
/**
|
||||
* 分账倒计时时长(单位:小时)
|
||||
*/
|
||||
@Excel(name = "分账倒计时时长", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Integer shareAccountCountdownDuration;
|
||||
|
||||
/**
|
||||
* 是否包含原始工人ID查询(用于orderType=0的情况)
|
||||
*/
|
||||
private Boolean includeOriginalWorker;
|
||||
|
||||
/**
|
||||
* 是否显示在监控单:0=不显示,1=显示在监控单
|
||||
*/
|
||||
@Excel(name = "是否显示在监控单", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=不显示,1=显示在监控单")
|
||||
private Integer showInMonitor;
|
||||
|
||||
/**
|
||||
* 是否已撤销服务主单:0=未撤销,1=已撤销
|
||||
*/
|
||||
@Excel(name = "是否已撤销服务主单", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=未撤销,1=已撤销")
|
||||
private Integer serviceCancelled;
|
||||
|
||||
/**
|
||||
* 更新时间别名字段(用于SQL查询中的update_time别名映射)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTimeAlias;
|
||||
|
||||
|
||||
private String from;
|
||||
|
||||
/**
|
||||
* 是否排除商品订单标识(用于Service层过滤逻辑)
|
||||
*/
|
||||
private Boolean excludeGoodsOrder;
|
||||
|
||||
/**
|
||||
* 退款时是否已支付:0=未支付,1=已支付
|
||||
*/
|
||||
@Excel(name = "退款时是否已支付", cellType = Excel.ColumnType.NUMERIC, readConverterExp = "0=未支付,1=已支付")
|
||||
private Integer refundPayStatus;
|
||||
|
||||
private Integer pageSize;
|
||||
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 仅用于前端/接口参数传递:
|
||||
* true = 查询结果要展开到当前城市下的所有街道
|
||||
* false/null = 保持原样
|
||||
*/
|
||||
private Boolean showAllStreet;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,44 +65,4 @@ public interface AfterServiceRecordMapper {
|
|||
* @return 未完成的售后记录
|
||||
*/
|
||||
AfterServiceRecord unfinished(Long orderDetailId);
|
||||
|
||||
/**
|
||||
* 查询师傅反馈超时的售后记录
|
||||
* 倒计时1:客户发起后,师傅24小时不操作任何反馈
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectWorkerFeedbackTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 查询客户确认超时的售后记录
|
||||
* 倒计时2:师傅重做完成后客户36小时不操作
|
||||
* 倒计时3:师傅拒绝后客户36小时不操作
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectCustomerConfirmTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 查询师傅重发补发超时的售后记录
|
||||
* 倒计时4:师傅重发补发后,客户6天(快递/物流)或24小时(非快递)不操作
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectWorkerResendTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 查询退货发货超时的售后记录
|
||||
* 查询退货发货时间超过6天且师傅未确认收货的售后记录
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectReturnShipTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 批量更新售后状态:当客户最终确认为1时,将售后状态设置为1
|
||||
*
|
||||
* @return 更新的记录数
|
||||
*/
|
||||
int updateAfterServiceStatusByCustomerFinalCheck();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.ghy.order.mapper;
|
|||
|
||||
import java.util.List;
|
||||
import com.ghy.order.domain.OrderAttachmentRecord;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 附件费Mapper接口
|
||||
|
|
@ -59,13 +58,4 @@ public interface OrderAttachmentRecordMapper
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteOrderAttachmentRecordByIds(String[] ids);
|
||||
|
||||
|
||||
/**
|
||||
* 删除附件费 根据子单id
|
||||
*
|
||||
* @param id 子单id
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteOrderAttachmentRecordByOrderDetailId(Long orderDetailId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,6 @@ public interface OrderDetailMapper {
|
|||
|
||||
Long countOrderDetailList(OrderDetail orderDetail);
|
||||
|
||||
/**
|
||||
* 查询符合条件的子单总数(用于分页)
|
||||
*
|
||||
* @param orderDetail 查询条件
|
||||
* @return 总数
|
||||
*/
|
||||
Long selectOrderDetailCount(OrderDetail orderDetail);
|
||||
|
||||
/**
|
||||
* @param orderDetailId 细单表id
|
||||
* @return 细单表
|
||||
|
|
@ -55,14 +47,6 @@ public interface OrderDetailMapper {
|
|||
*/
|
||||
List<OrderDetail> selectByOrderMasterId(@Param("orderMasterId") Long orderMasterId);
|
||||
|
||||
/**
|
||||
* 根据主订单ID集合批量查询详细订单
|
||||
*
|
||||
* @param orderMasterIds 主订单ID集合
|
||||
* @return 详细订单集合
|
||||
*/
|
||||
List<OrderDetail> selectByOrderMasterIds(@Param("orderMasterIds") List<Long> orderMasterIds);
|
||||
|
||||
/**
|
||||
* 批量删除细单表信息
|
||||
*
|
||||
|
|
|
|||
|
|
@ -35,14 +35,6 @@ public interface OrderGoodsMapper {
|
|||
|
||||
List<OrderGoods> selectByOrderId(@Param("orderId") Long orderId);
|
||||
|
||||
/**
|
||||
* 根据主订单ID集合批量查询订单商品
|
||||
*
|
||||
* @param orderMasterIds 主订单ID集合
|
||||
* @return 订单商品集合
|
||||
*/
|
||||
List<OrderGoods> selectByOrderMasterIds(@Param("orderMasterIds") List<Long> orderMasterIds);
|
||||
|
||||
List<OrderGoods> selectByOrderDetailId(@Param("orderDetailId") Long orderDetailId);
|
||||
|
||||
int deleteByOrderDetailId(@Param("orderDetailId") Long orderDetailId);
|
||||
|
|
|
|||
|
|
@ -39,14 +39,6 @@ public interface OrderMasterMapper {
|
|||
*/
|
||||
Long countOrderMasterList(OrderMaster orderMaster);
|
||||
|
||||
/**
|
||||
* 查询符合条件的主单总数
|
||||
*
|
||||
* @param orderMaster 主订单入参
|
||||
* @return 满足条件的主单总数
|
||||
*/
|
||||
Long selectOrderMasterCount(OrderMaster orderMaster);
|
||||
|
||||
/**
|
||||
* @param orderMasterId 主订单id
|
||||
* @return 主订单
|
||||
|
|
@ -127,12 +119,4 @@ public interface OrderMasterMapper {
|
|||
int updateCreateTime(Long id);
|
||||
|
||||
int updateOrderMasterAddressById(OrderMaster orderMaster);
|
||||
|
||||
/**
|
||||
* 根据商品主单ID查询服务主单
|
||||
*
|
||||
* @param goodsOrderMasterId 商品主单ID
|
||||
* @return 服务主单
|
||||
*/
|
||||
OrderMaster selectByGoodsOrderMasterId(Long goodsOrderMasterId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,282 +0,0 @@
|
|||
package com.ghy.order.quartz;
|
||||
|
||||
import com.ghy.common.core.domain.AjaxResult;
|
||||
import com.ghy.common.utils.DateUtils;
|
||||
import com.ghy.order.domain.AfterServiceRecord;
|
||||
import com.ghy.order.service.IAfterServiceRecordService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Date: 2024-12-19
|
||||
* @Author: 系统
|
||||
* @Version: v1.0
|
||||
* @Description: 售后倒计时定时任务
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class AfterServiceTimeoutTask {
|
||||
|
||||
private final IAfterServiceRecordService afterServiceRecordService;
|
||||
|
||||
/**
|
||||
* 售后倒计时定时任务 - 每5分钟执行一次
|
||||
* 处理3种倒计时场景:
|
||||
* 1. 师傅24小时不操作自动同意完单
|
||||
* 2. 师傅重做完成后客户36小时不操作自动同意完单
|
||||
* 3. 师傅拒绝后客户36小时不操作自动取消售后
|
||||
*/
|
||||
@Scheduled(cron = "0 0/5 * * * ?")
|
||||
public void afterServiceTimeoutProcess() {
|
||||
log.info("{}开始售后倒计时定时任务", DateUtils.timeFormat(new Date()));
|
||||
|
||||
try {
|
||||
// 处理师傅24小时不操作的情况
|
||||
processWorkerFeedbackTimeout();
|
||||
|
||||
// 处理客户36小时不操作的情况
|
||||
processCustomerConfirmTimeout();
|
||||
|
||||
log.info("{}售后倒计时定时任务执行完成", DateUtils.timeFormat(new Date()));
|
||||
} catch (Exception e) {
|
||||
log.error("售后倒计时定时任务执行异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理师傅24小时不操作的情况
|
||||
* 倒计时1:客户发起后,师傅24小时不操作任何反馈,直接按同意完单
|
||||
*/
|
||||
private void processWorkerFeedbackTimeout() {
|
||||
log.info("开始处理师傅反馈超时情况");
|
||||
|
||||
// 查询需要处理的售后记录
|
||||
List<AfterServiceRecord> timeoutRecords = afterServiceRecordService.selectWorkerFeedbackTimeoutRecords();
|
||||
|
||||
for (AfterServiceRecord record : timeoutRecords) {
|
||||
try {
|
||||
log.info("处理师傅反馈超时售后记录:{}", record.getId());
|
||||
|
||||
// 师傅24小时不操作,直接设置为师傅同意并客户同意,完成售后
|
||||
// record.setWorkerFeedbackResult(1L);
|
||||
record.setCustomerFinalCheck(1L);
|
||||
record.setRefundApplyTime(new Date());
|
||||
record.setIsAutoProcessed(1); // 自动处理
|
||||
|
||||
// 根据售后类型选择更新方法
|
||||
if (record.getAfterServiceCategory() != null && record.getAfterServiceCategory().equals(1)) {
|
||||
// 商品售后
|
||||
afterServiceRecordService.updateGoodsAfterServiceRecord(record);
|
||||
} else {
|
||||
// 服务售后或其他类型
|
||||
afterServiceRecordService.updateAfterServiceRecord(record);
|
||||
}
|
||||
|
||||
// 师傅24小时不操作,需要执行退款逻辑
|
||||
try {
|
||||
afterServiceRecordService.executeRefundLogic(record);
|
||||
log.info("师傅24小时不操作,自动执行退款逻辑完成,售后记录ID:{}", record.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("师傅24小时不操作,自动执行退款逻辑异常,售后记录ID:{}", record.getId(), e);
|
||||
}
|
||||
|
||||
log.info("师傅反馈超时自动处理完成,售后记录ID:{}", record.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("处理师傅反馈超时异常,售后记录ID:{}", record.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("师傅反馈超时处理完成,共处理{}条记录", timeoutRecords.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理客户36小时不操作的情况
|
||||
* 倒计时2:师傅重做完成后客户36小时不操作自动同意完单
|
||||
* 倒计时3:师傅拒绝或同意后客户36小时不操作自动取消售后
|
||||
* 倒计时4:师傅选择重发/补发方案后客户36小时不操作自动取消售后
|
||||
*/
|
||||
private void processCustomerConfirmTimeout() {
|
||||
log.info("开始处理客户确认超时情况");
|
||||
|
||||
// 查询需要处理的售后记录
|
||||
List<AfterServiceRecord> timeoutRecords = afterServiceRecordService.selectCustomerConfirmTimeoutRecords();
|
||||
|
||||
for (AfterServiceRecord record : timeoutRecords) {
|
||||
try {
|
||||
log.info("处理客户确认超时售后记录:{}", record.getId());
|
||||
|
||||
// 根据师傅反馈结果决定处理方式
|
||||
if (record.getWorkerFeedbackResult() != null && record.getWorkerFeedbackResult().equals(3L)) {
|
||||
// 倒计时2:师傅重做完成后客户36小时不操作自动同意完单
|
||||
record.setCustomerFinalCheck(1L);
|
||||
// 师傅重做完成,客户同意,订单完成,不需要退款,不设置refundApplyTime
|
||||
record.setIsAutoProcessed(1); // 自动处理
|
||||
|
||||
log.info("师傅重做完成后客户超时自动同意,订单完成,售后记录ID:{}", record.getId());
|
||||
} else if (record.getWorkerFeedbackResult() != null &&
|
||||
(record.getWorkerFeedbackResult().equals(0L) || record.getWorkerFeedbackResult().equals(1L))) {
|
||||
// 倒计时3:师傅拒绝或同意后客户36小时不操作自动取消售后
|
||||
record.setCustomerFinalCheck(2L);
|
||||
record.setAfterServiceStatus(2); // 设置为已取消状态
|
||||
record.setIsAutoProcessed(1); // 自动处理
|
||||
record.setWorkerReceiveConfirm(0);
|
||||
// // 检查是否需要恢复财务金额
|
||||
// if (record.getOriginalRefund() != null && record.getOriginalRefund().compareTo(java.math.BigDecimal.ZERO) > 0) {
|
||||
// log.info("售后取消,开始恢复财务金额,记录ID:{},原退款金额:{}",
|
||||
// record.getId(), record.getOriginalRefund());
|
||||
|
||||
// // 调用财务恢复逻辑
|
||||
// try {
|
||||
// afterServiceRecordService.restoreFinancialAmount(record);
|
||||
// log.info("售后取消财务金额恢复成功,记录ID:{}", record.getId());
|
||||
// } catch (Exception e) {
|
||||
// log.error("售后取消财务金额恢复失败,记录ID:{},错误:{}",
|
||||
// record.getId(), e.getMessage(), e);
|
||||
// }
|
||||
// }
|
||||
|
||||
log.info("师傅拒绝/同意后客户超时自动取消,售后单取消完成,售后记录ID:{}", record.getId());
|
||||
}else if (record.getWorkerFeedbackResult() != null && record.getWorkerFeedbackResult().equals(2L)) {
|
||||
//师傅重做补做完成时候,客户36小时不操作自动取消售后
|
||||
record.setCustomerFinalCheck(2L);
|
||||
record.setAfterServiceStatus(2); // 设置为已取消状态
|
||||
record.setIsAutoProcessed(1); // 自动处理
|
||||
record.setWorkerReceiveConfirm(0);
|
||||
|
||||
log.info("师傅重做补做完成时候,客户36小时不操作自动取消售后,订单完成,售后记录ID:{}", record.getId());
|
||||
}
|
||||
|
||||
// 更新记录
|
||||
if (record.getAfterServiceCategory() != null && record.getAfterServiceCategory().equals(1)) {
|
||||
// 商品售后
|
||||
afterServiceRecordService.updateGoodsAfterServiceRecord(record);
|
||||
} else {
|
||||
// 服务售后或其他类型
|
||||
afterServiceRecordService.updateAfterServiceRecord(record);
|
||||
}
|
||||
|
||||
// 恢复确认中倒计时
|
||||
afterServiceRecordService.resumeConfirmTimeout(record.getOrderDetailId());
|
||||
|
||||
log.info("客户确认超时自动处理完成,售后记录ID:{}", record.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("处理客户确认超时异常,售后记录ID:{}", record.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("客户确认超时处理完成,共处理{}条记录", timeoutRecords.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 师傅重发补发超时处理
|
||||
* 快递为4天 其他未1天
|
||||
*
|
||||
* 超时后自动按客户同意处理
|
||||
*/
|
||||
@Scheduled(fixedRate = 5 * 60 * 1000) // 5分钟执行一次
|
||||
public void handleWorkerResendTimeout() {
|
||||
log.info("开始处理师傅重发补发超时...");
|
||||
|
||||
// 查询需要处理的师傅重发补发超时记录
|
||||
List<AfterServiceRecord> timeoutRecords = afterServiceRecordService.selectWorkerResendTimeoutRecords();
|
||||
|
||||
for (AfterServiceRecord record : timeoutRecords) {
|
||||
try {
|
||||
log.info("处理师傅重发补发超时售后记录:{}", record.getId());
|
||||
|
||||
// 自动设置为客户同意
|
||||
record.setCustomerFinalCheck(1L);
|
||||
record.setIsAutoProcessed(1);
|
||||
record.setRefundApplyTime(new Date());
|
||||
record.setAfterServiceStatus(1);
|
||||
|
||||
// 更新记录
|
||||
afterServiceRecordService.updateGoodsAfterServiceRecord(record);
|
||||
|
||||
// 恢复确认中倒计时
|
||||
afterServiceRecordService.resumeConfirmTimeout(record.getOrderDetailId());
|
||||
|
||||
log.info("师傅重发补发超时自动同意,售后记录ID:{},重发方式:{}",
|
||||
record.getId(), record.getWorkerResendType());
|
||||
} catch (Exception e) {
|
||||
log.error("处理师傅重发补发超时异常,售后记录ID:{}", record.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("师傅重发补发超时处理完成,共处理{}条记录", timeoutRecords.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务:自动处理超时的师傅确认收货
|
||||
* ,查询退货发货时间超过6天且师傅未确认收货的售后记录
|
||||
* 自动调用师傅确认收货方法进行退款处理
|
||||
*/
|
||||
@Scheduled(cron = "0 0 8,12,18 * * ?")
|
||||
public void autoConfirmReceiveTask() {
|
||||
try {
|
||||
log.info("开始执行自动确认收货定时任务");
|
||||
|
||||
// 查询退货发货时间超过6天且师傅未确认收货的售后记录
|
||||
List<AfterServiceRecord> timeoutRecords = afterServiceRecordService.selectReturnShipTimeoutRecords();
|
||||
|
||||
if (timeoutRecords == null || timeoutRecords.isEmpty()) {
|
||||
log.info("没有找到需要自动确认收货的超时记录");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("找到{}条需要自动确认收货的超时记录", timeoutRecords.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
// 循环处理每条超时记录
|
||||
for (AfterServiceRecord record : timeoutRecords) {
|
||||
try {
|
||||
log.info("开始自动确认收货,售后记录ID:{},退货发货时间:{}",
|
||||
record.getId(), record.getReturnShipTime());
|
||||
|
||||
// 创建参数对象
|
||||
AfterServiceRecord param = new AfterServiceRecord();
|
||||
param.setId(record.getId());
|
||||
|
||||
// 调用师傅确认收货方法
|
||||
AjaxResult result = afterServiceRecordService.workerConfirmReceive(param);
|
||||
|
||||
// if (result.isSuccess()) {
|
||||
// successCount++;
|
||||
|
||||
// // 标记为自动处理
|
||||
// record.setIsAutoProcessed(1);
|
||||
// record.setAutoProcessTime(new Date());
|
||||
// record.setUpdateTime(new Date());
|
||||
// afterServiceRecordService.updateAfterServiceRecord(record);
|
||||
|
||||
// log.info("自动确认收货成功,售后记录ID:{}", record.getId());
|
||||
// } else {
|
||||
// failCount++;
|
||||
// log.error("自动确认收货失败,售后记录ID:{},错误信息:{}",
|
||||
// record.getId(), result.getMsg());
|
||||
// }
|
||||
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
log.error("自动确认收货异常,售后记录ID:{},错误:{}",
|
||||
record.getId(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("自动确认收货定时任务执行完成,总记录数:{},成功:{},失败:{}",
|
||||
timeoutRecords.size(), successCount, failCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("自动确认收货定时任务执行异常:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,9 +28,4 @@ public class AppOrderAssignRequest {
|
|||
|
||||
// 分配的商品及数量
|
||||
private List<AppGoodsRequest> goodsList;
|
||||
|
||||
//是否立即发货
|
||||
private Integer isQuicklyDelivery;
|
||||
|
||||
private Integer generateServiceOrder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,14 +45,4 @@ public class AppOrderRequest {
|
|||
private Long goodsId;
|
||||
|
||||
private Long insuranceId;
|
||||
|
||||
private Long serviceShopId;
|
||||
|
||||
private String orderImages;
|
||||
|
||||
/**
|
||||
* 是否发货到服务店:0=否,1=是
|
||||
*/
|
||||
|
||||
private Integer isDeliveryToStore;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package com.ghy.order.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 物流查询请求类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class LogisticsQueryRequest {
|
||||
|
||||
/**
|
||||
* 快递单号
|
||||
*/
|
||||
@NotBlank(message = "快递单号不能为空")
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 快递公司编码(可选,如果不传会自动识别)
|
||||
*/
|
||||
private String expressCode;
|
||||
|
||||
/**
|
||||
* 快递公司名称(可选,如果不传会自动识别)
|
||||
*/
|
||||
private String expressName;
|
||||
|
||||
/**
|
||||
* 手机号(阿里云快递查询API需要)
|
||||
*/
|
||||
private String mobile;
|
||||
}
|
||||
|
|
@ -24,9 +24,6 @@ public class SysOrderAssignRequest {
|
|||
// 图片
|
||||
private String imageUrl;
|
||||
|
||||
// 下单图片
|
||||
private String orderImages;
|
||||
|
||||
// 商品相关信息
|
||||
private Long goodsDeptCategoryId;
|
||||
// 服务地址ID
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package com.ghy.order.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 转单请求类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class TransferOrderRequest {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 新师傅ID(可以为null,表示进入接单大厅)
|
||||
*/
|
||||
private Long newWorkerId;
|
||||
|
||||
/**
|
||||
* 转单金额(新师傅承担,可以为null表示无转单金额)
|
||||
*/
|
||||
private BigDecimal transferAmount;
|
||||
|
||||
/**
|
||||
* 转单原因
|
||||
*/
|
||||
private String transferReason;
|
||||
}
|
||||
|
|
@ -61,95 +61,4 @@ public interface IAfterServiceRecordService {
|
|||
* @return 结果
|
||||
*/
|
||||
int deleteAfterServiceRecordById(String id);
|
||||
|
||||
/**
|
||||
* 查询师傅反馈超时的售后记录
|
||||
* 倒计时1:客户发起后,师傅24小时不操作任何反馈
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectWorkerFeedbackTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 查询客户确认超时的售后记录
|
||||
* 倒计时2:师傅重做完成后客户36小时不操作
|
||||
* 倒计时3:师傅拒绝后客户36小时不操作
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectCustomerConfirmTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 执行退款逻辑
|
||||
* 用于定时器自动处理退款
|
||||
*
|
||||
* @param afterServiceRecord 售后记录
|
||||
*/
|
||||
void executeRefundLogic(AfterServiceRecord afterServiceRecord);
|
||||
|
||||
/**
|
||||
* 恢复确认中倒计时
|
||||
* @param orderDetailId 子单ID
|
||||
*/
|
||||
void resumeConfirmTimeout(Long orderDetailId);
|
||||
|
||||
/**
|
||||
* 修改商品售后记录
|
||||
*
|
||||
* @param afterServiceRecord 售后记录
|
||||
* @return 结果
|
||||
*/
|
||||
AjaxResult updateGoodsAfterServiceRecord(AfterServiceRecord afterServiceRecord) throws Exception;
|
||||
|
||||
/**
|
||||
* 师傅重发/补发操作
|
||||
* 师傅端点击重发补发按钮,保存重发/补发方案
|
||||
*
|
||||
* @param afterServiceRecord 售后记录
|
||||
* @return 操作结果
|
||||
*/
|
||||
AjaxResult workerResendPlan(AfterServiceRecord afterServiceRecord);
|
||||
|
||||
/**
|
||||
* 退货操作
|
||||
* 客户或师傅端进行退货操作,保存退货信息
|
||||
*
|
||||
* @param afterServiceRecord 售后记录
|
||||
* @return 操作结果
|
||||
*/
|
||||
AjaxResult returnGoods(AfterServiceRecord afterServiceRecord);
|
||||
|
||||
AfterServiceRecord unfinished(Long id);
|
||||
|
||||
/**
|
||||
* 查询师傅重发补发超时的售后记录
|
||||
* 快递/物流:6天倒计时
|
||||
* 非快递:24小时倒计时
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectWorkerResendTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 查询退货发货时间超过6天且师傅未确认收货的售后记录
|
||||
* 用于定时任务自动处理师傅确认收货超时的情况
|
||||
*
|
||||
* @return 超时的售后记录列表
|
||||
*/
|
||||
List<AfterServiceRecord> selectReturnShipTimeoutRecords();
|
||||
|
||||
/**
|
||||
* 师傅确认收货
|
||||
* 师傅确认收到货物后,根据同意处理方式决定是否执行退款
|
||||
*
|
||||
* @param afterServiceRecord 售后记录
|
||||
* @return 操作结果
|
||||
*/
|
||||
AjaxResult workerConfirmReceive(AfterServiceRecord afterServiceRecord);
|
||||
|
||||
/**
|
||||
* 恢复财务金额
|
||||
* 当售后取消时,将之前扣减的金额恢复到原财务账单
|
||||
* @param afterServiceRecord 售后记录
|
||||
*/
|
||||
void restoreFinancialAmount(AfterServiceRecord afterServiceRecord);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,4 @@ public interface IOrderAttachmentRecordService
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteOrderAttachmentRecordById(Long id);
|
||||
|
||||
int deleteOrderAttachmentRecordByOrderDetailId(Long orderDetailId);
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package com.ghy.order.service;
|
||||
|
||||
import com.ghy.order.domain.LogisticsInfo;
|
||||
import com.ghy.order.request.LogisticsQueryRequest;
|
||||
|
||||
/**
|
||||
* 物流服务接口
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
public interface LogisticsService {
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息
|
||||
*
|
||||
* @param request 物流查询请求
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(LogisticsQueryRequest request);
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(String trackingNumber);
|
||||
|
||||
/**
|
||||
* 根据快递单号和快递公司编码查询物流信息
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param expressCode 快递公司编码
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(String trackingNumber, String expressCode);
|
||||
|
||||
/**
|
||||
* 根据快递单号、快递公司编码和手机号查询物流信息
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param expressCode 快递公司编码
|
||||
* @param mobile 手机号
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(String trackingNumber, String expressCode, String mobile);
|
||||
|
||||
/**
|
||||
* 根据订单ID查询物流信息
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogisticsByOrderId(Long orderId);
|
||||
}
|
||||
|
|
@ -48,13 +48,6 @@ public interface OrderDetailService {
|
|||
*/
|
||||
List<OrderDetail> selectByOrderMasterId(Long orderMasterId);
|
||||
|
||||
/**
|
||||
* 根据主订单ID集合批量查询详细订单
|
||||
* @param orderMasterIds 主订单ID集合
|
||||
* @return 详细订单集合
|
||||
*/
|
||||
List<OrderDetail> selectByOrderMasterIds(List<Long> orderMasterIds);
|
||||
|
||||
/**
|
||||
* @param ids 详细订单ids
|
||||
* @return 删除结果
|
||||
|
|
@ -128,14 +121,6 @@ public interface OrderDetailService {
|
|||
*/
|
||||
Long countOrderDetailList(OrderDetail orderDetail);
|
||||
|
||||
/**
|
||||
* 查询符合条件的子单总数(用于分页)
|
||||
*
|
||||
* @param orderDetail 查询条件
|
||||
* @return 总数
|
||||
*/
|
||||
Long selectOrderDetailCount(OrderDetail orderDetail);
|
||||
|
||||
/**
|
||||
* 子单改价接口
|
||||
*
|
||||
|
|
@ -145,7 +130,7 @@ public interface OrderDetailService {
|
|||
* @param remark 备注
|
||||
* @return 成功/失败
|
||||
*/
|
||||
int changePrice(Long orderDetailId, BigDecimal changeMoney, Integer type, String remark,String urls ,String fileNames,String reason) throws Exception;
|
||||
int changePrice(Long orderDetailId, BigDecimal changeMoney, Integer type, String remark) throws Exception;
|
||||
|
||||
int sureChange(Long financialChangeRecordId);
|
||||
|
||||
|
|
@ -218,36 +203,4 @@ public interface OrderDetailService {
|
|||
|
||||
// 订单详情数据统计返回 根据时间统计当前日期天
|
||||
OrderDetailStatisticsDTO orderStatisticsDisposeByNow();
|
||||
|
||||
/**
|
||||
* 延期到货
|
||||
* 如果是待确认状态则变为服务中,其他状态不变
|
||||
* 将confirm_start_time增加3天
|
||||
* 延期次数最多2次
|
||||
*
|
||||
* @param orderMasterId 主订单ID
|
||||
* @return 成功条数
|
||||
*/
|
||||
int delayOrder(Long orderMasterId);
|
||||
|
||||
/**
|
||||
* 子订单退回
|
||||
* 如果订单为服务类型(orderType=0)则退回到服务中状态
|
||||
* 如果订单为商品类型(orderType=1)则退回到待排期状态
|
||||
*
|
||||
* @param orderDetailId 子订单ID
|
||||
* @return 成功条数
|
||||
*/
|
||||
int returnOrder(Long orderDetailId);
|
||||
|
||||
int updateOrderDetailAddressById(OrderDetail orderDetail);
|
||||
|
||||
/**
|
||||
* 保存子单师傅备注
|
||||
*
|
||||
* @param orderDetailId 子订单ID
|
||||
* @param workerRemark 师傅备注
|
||||
* @return 成功条数
|
||||
*/
|
||||
int saveDetailWorkerRemark(Long orderDetailId, String workerRemark);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,6 @@ public interface OrderGoodsService {
|
|||
|
||||
List<OrderGoods> selectByOrderMasterId(Long orderMasterId);
|
||||
|
||||
/**
|
||||
* 根据主订单ID集合批量查询订单商品
|
||||
*
|
||||
* @param orderMasterIds 主订单ID集合
|
||||
* @return 订单商品集合
|
||||
*/
|
||||
List<OrderGoods> selectByOrderMasterIds(List<Long> orderMasterIds);
|
||||
|
||||
List<OrderGoods> selectByOrderDetailId(Long orderDetailId);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.ghy.order.pojo.dto.OrderMasterStatisticsDTO;
|
|||
import com.ghy.order.request.AppOrderRequest;
|
||||
import com.ghy.order.request.OrderChangePriceReq;
|
||||
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
||||
import com.ghy.order.domain.OrderDetail;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
|
@ -47,14 +46,6 @@ public interface OrderMasterService {
|
|||
*/
|
||||
Long countOrderMasterList(OrderMaster orderMaster);
|
||||
|
||||
/**
|
||||
* 查询符合条件的主单总数
|
||||
*
|
||||
* @param orderMaster 主订单入参
|
||||
* @return 符合条件的主单总数
|
||||
*/
|
||||
Long selectOrderMasterCount(OrderMaster orderMaster);
|
||||
|
||||
/**
|
||||
* @param orderMasterId 主订单id
|
||||
* @return 主订单
|
||||
|
|
@ -201,23 +192,4 @@ public interface OrderMasterService {
|
|||
|
||||
int updateOrderMasterAddressById(OrderMaster orderMaster);
|
||||
|
||||
int returnOrder(Long orderMasterId, String returnReason, String returnReasonDetail, String returnImages);
|
||||
|
||||
/**
|
||||
* 保存主单师傅备注
|
||||
*
|
||||
* @param orderMasterId 主订单ID
|
||||
* @param workerRemark 师傅备注
|
||||
* @return 成功条数
|
||||
*/
|
||||
int saveMasterWorkerRemark(Long orderMasterId, String workerRemark);
|
||||
|
||||
/**
|
||||
* 根据商品主单ID查询服务主单
|
||||
*
|
||||
* @param goodsOrderMasterId 商品主单ID
|
||||
* @return 服务主单
|
||||
*/
|
||||
OrderMaster selectByGoodsOrderMasterId(Long goodsOrderMasterId);
|
||||
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,303 +0,0 @@
|
|||
package com.ghy.order.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ghy.order.domain.LogisticsInfo;
|
||||
import com.ghy.order.domain.OrderMaster;
|
||||
import com.ghy.order.request.LogisticsQueryRequest;
|
||||
import com.ghy.order.service.LogisticsService;
|
||||
import com.ghy.order.service.OrderMasterService;
|
||||
import com.ghy.order.utils.HttpUtils;
|
||||
import com.ghy.order.utils.LogisticsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 物流服务实现类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LogisticsServiceImpl implements LogisticsService {
|
||||
|
||||
@Autowired
|
||||
private OrderMasterService orderMasterService;
|
||||
|
||||
/**
|
||||
* 阿里云快递查询 API配置
|
||||
*/
|
||||
@Value("${logistics.aliyun.appCode:}")
|
||||
private String appCode;
|
||||
|
||||
@Value("${logistics.aliyun.host:https://kzexpress.market.alicloudapi.com}")
|
||||
private String host;
|
||||
|
||||
@Value("${logistics.aliyun.path:/api-mall/api/express/query}")
|
||||
private String path;
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(LogisticsQueryRequest request) {
|
||||
return queryLogistics(request.getTrackingNumber(), request.getExpressCode(), request.getMobile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(String trackingNumber) {
|
||||
return queryLogistics(trackingNumber, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(String trackingNumber, String expressCode) {
|
||||
return queryLogistics(trackingNumber, expressCode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息(阿里云API)
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param expressCode 快递公司编码
|
||||
* @param mobile 手机号
|
||||
* @return 物流信息
|
||||
*/
|
||||
public LogisticsInfo queryLogistics(String trackingNumber, String expressCode, String mobile) {
|
||||
LogisticsInfo logisticsInfo = new LogisticsInfo();
|
||||
logisticsInfo.setTrackingNumber(trackingNumber);
|
||||
logisticsInfo.setQueryTime(new Date());
|
||||
|
||||
try {
|
||||
// 验证快递单号格式
|
||||
if (!LogisticsUtils.isValidTrackingNumber(trackingNumber)) {
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("快递单号格式不正确");
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
// 调用阿里云快递查询 API
|
||||
JSONObject result = callAliyunAPI(trackingNumber, mobile);
|
||||
|
||||
if (result.getBool("success", false)) {
|
||||
// 查询成功
|
||||
logisticsInfo.setSuccess(true);
|
||||
|
||||
// 解析快递公司信息
|
||||
JSONObject data = result.getJSONObject("data");
|
||||
if (data != null) {
|
||||
logisticsInfo.setExpressCode(data.getStr("cpCode"));
|
||||
logisticsInfo.setExpressName(data.getStr("logisticsCompanyName"));
|
||||
logisticsInfo.setStatus(getAliyunStatus(data.getStr("logisticsStatusDesc")));
|
||||
logisticsInfo.setStatusDesc(data.getStr("logisticsStatusDesc"));
|
||||
|
||||
// 解析物流轨迹
|
||||
List<LogisticsInfo.LogisticsTrace> traces = new ArrayList<>();
|
||||
JSONArray tracesArray = data.getJSONArray("logisticsTraceDetailList");
|
||||
if (tracesArray != null) {
|
||||
for (int i = 0; i < tracesArray.size(); i++) {
|
||||
JSONObject trace = tracesArray.getJSONObject(i);
|
||||
LogisticsInfo.LogisticsTrace logisticsTrace = new LogisticsInfo.LogisticsTrace();
|
||||
|
||||
// 处理时间戳
|
||||
Long timeStamp = trace.getLong("time");
|
||||
if (timeStamp != null) {
|
||||
logisticsTrace.setTime(new Date(timeStamp));
|
||||
}
|
||||
|
||||
logisticsTrace.setLocation(trace.getStr("areaName"));
|
||||
logisticsTrace.setDescription(trace.getStr("desc"));
|
||||
logisticsTrace.setStatus(trace.getStr("logisticsStatus"));
|
||||
traces.add(logisticsTrace);
|
||||
}
|
||||
}
|
||||
logisticsInfo.setTraces(traces);
|
||||
}
|
||||
} else {
|
||||
// 查询失败
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg(result.getStr("msg", "查询失败"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询物流信息失败,快递单号:{},错误:{}", trackingNumber, e.getMessage(), e);
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("查询物流信息失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogisticsByOrderId(Long orderId) {
|
||||
OrderMaster orderMaster = orderMasterService.selectById(orderId);
|
||||
if (orderMaster == null || StrUtil.isBlank(orderMaster.getTrackingNumber())) {
|
||||
LogisticsInfo logisticsInfo = new LogisticsInfo();
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("订单不存在或没有快递单号");
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
return queryLogistics(orderMaster.getTrackingNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用阿里云快递查询 API
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param mobile 手机号
|
||||
* @return API返回结果
|
||||
*/
|
||||
private JSONObject callAliyunAPI(String trackingNumber, String mobile) {
|
||||
try {
|
||||
String method = "GET";
|
||||
|
||||
// 构建请求头
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "APPCODE " + appCode);
|
||||
|
||||
// 构建查询参数
|
||||
Map<String, String> querys = new HashMap<>();
|
||||
querys.put("expressNo", trackingNumber);
|
||||
if (StrUtil.isNotBlank(mobile)) {
|
||||
querys.put("mobile", mobile);
|
||||
}
|
||||
|
||||
log.info("阿里云快递查询API请求参数: expressNo={}, mobile={}", trackingNumber, mobile);
|
||||
|
||||
// 调用API
|
||||
HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);
|
||||
String result = EntityUtils.toString(response.getEntity());
|
||||
log.info("阿里云快递查询API响应结果: {}", result);
|
||||
|
||||
// 检查响应状态码
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode != 200) {
|
||||
log.error("阿里云API返回错误状态码: {}", statusCode);
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("success", false);
|
||||
errorResult.set("message", "API返回错误状态码: " + statusCode);
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 检查响应内容是否为空
|
||||
if (StrUtil.isBlank(result)) {
|
||||
log.error("阿里云API返回空响应");
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("success", false);
|
||||
errorResult.set("message", "API返回空响应");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 检查响应内容是否以{开头(JSON格式)
|
||||
if (!result.trim().startsWith("{")) {
|
||||
log.error("阿里云API返回非JSON格式响应: {}", result.substring(0, Math.min(100, result.length())));
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("success", false);
|
||||
errorResult.set("message", "API返回非JSON格式响应");
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
// 尝试解析JSON
|
||||
try {
|
||||
return JSONUtil.parseObj(result);
|
||||
} catch (Exception jsonException) {
|
||||
log.error("解析阿里云API响应JSON失败: {}", jsonException.getMessage());
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("success", false);
|
||||
errorResult.set("message", "解析API响应失败: " + jsonException.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调用阿里云快递查询API失败: {}", e.getMessage(), e);
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("success", false);
|
||||
errorResult.set("message", "API调用失败: " + e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将阿里云状态转换为系统状态码
|
||||
*
|
||||
* @param aliyunStatus 阿里云状态
|
||||
* @return 系统状态码
|
||||
*/
|
||||
private Integer getAliyunStatus(String aliyunStatus) {
|
||||
if (StrUtil.isBlank(aliyunStatus)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (aliyunStatus) {
|
||||
case "在途":
|
||||
case "运输中":
|
||||
case "TRANSPORT":
|
||||
return 0;
|
||||
case "揽收":
|
||||
case "已揽收":
|
||||
case "ACCEPT":
|
||||
return 1;
|
||||
case "疑难":
|
||||
case "异常":
|
||||
case "EXCEPTION":
|
||||
return 2;
|
||||
case "签收":
|
||||
case "已签收":
|
||||
case "SIGN":
|
||||
return 3;
|
||||
case "退签":
|
||||
case "已退签":
|
||||
case "REJECT":
|
||||
return 4;
|
||||
case "派件":
|
||||
case "派送中":
|
||||
case "DELIVERING":
|
||||
return 5;
|
||||
case "退回":
|
||||
case "已退回":
|
||||
case "RETURN":
|
||||
return 6;
|
||||
case "到达驿站":
|
||||
case "STA_INBOUND":
|
||||
return 5; // 到达驿站也算派件状态
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态描述
|
||||
*
|
||||
* @param status 状态码
|
||||
* @return 状态描述
|
||||
*/
|
||||
private String getStatusDesc(Integer status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "在途";
|
||||
case 1:
|
||||
return "揽收";
|
||||
case 2:
|
||||
return "疑难";
|
||||
case 3:
|
||||
return "签收";
|
||||
case 4:
|
||||
return "退签";
|
||||
case 5:
|
||||
return "派件";
|
||||
case 6:
|
||||
return "退回";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue