feat: 全局数据隔离 + 响应结构统一 + 交接文档
1. 新增SysOrgService业务数据隔离工具方法(applyBusinessOrgScope/getUserOrgMpath) 2. 10个Controller接入组织数据隔离(设备/订单/退款/统计/定价/支付配置/激活码/用户) 3. 统一响应结构:写操作从R.ok()改为R.dataSuccess(data) 4. 修复5处data.data双层嵌套 5. 修复SysUserController.info()不一致 6. 修复循环依赖(@Lazy) 7. 新增交接文档HANDOVER.md
This commit is contained in:
352
HANDOVER.md
352
HANDOVER.md
@@ -1,95 +1,285 @@
|
||||
# 后端项目交接文档
|
||||
# as-daycard-openapi 项目交接文档
|
||||
|
||||
## 项目信息
|
||||
- 路径:/Users/zhaoguodong/Desktop/as-daycard-openapi
|
||||
- 启动:`./start.sh -Dspring-boot.run.profiles=dev`(端口8084,JDK17路径在start.sh里)
|
||||
- 本地打包:`export JAVA_HOME=/Users/zhaoguodong/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home && mvn clean package -DskipTests`
|
||||
- MySQL:/opt/homebrew/Cellar/mysql@8.0/8.0.42/bin/mysql -uroot -pr2356510 dev-0418
|
||||
- 接口文档:API_DOC.md(完整)+ THIRD_PARTY_API.md(第三方对接)
|
||||
- DDL:db/full-schema.sql(52个表,完整表结构,从dev-0418导出)
|
||||
## 一、项目概述
|
||||
|
||||
## 云服务器部署
|
||||
- 服务器:宝塔面板,IP见用户确认
|
||||
- 数据库:jstopenapi(已建库+导入full-schema.sql+schedule-timeout-task.sql)
|
||||
- MySQL权限:设为"所有人"(允许Docker容器通过host.docker.internal访问)
|
||||
- Docker部署:docker-compose.yml已配好(DB_URL/REDIS_HOST用host.docker.internal,JWT/API签名密钥已生成)
|
||||
- application-prod.yml:已创建(和dev相同配置,环境变量由docker-compose传入)
|
||||
- Dockerfile:已改为multi-stage(JDK17),但服务器上用的是简单版(本地打包jar上传+COPY)
|
||||
- 部署方式:本地mvn打包 -> scp上传jar -> docker-compose build/up
|
||||
- **已知问题**:MySQL的max_connect_errors封锁Docker容器IP,需 `mysql -uroot -p -e "FLUSH HOSTS;"` 解除后重启容器
|
||||
- **部署状态**:jar已上传,application-prod.yml已包含,正在解决MySQL连接封锁问题
|
||||
天卡(24小时制视频权益卡)开放API平台,为酒店电视设备提供天卡购买、支付、激活、权益管理的完整能力。设备端通过本平台完成微信支付购买天卡,支付成功后异步调用橘视厅接口激活权益,设备获取 `thirdCardNo` 凭证后直连橘视厅SDK播放内容。
|
||||
|
||||
## 已完成的功能
|
||||
**技术栈**: Java 8 / Spring Boot 2.6.6 / MyBatis-Plus / MySQL / Redis / Shiro + JWT / Quartz / wechatpay-java SDK 0.2.14
|
||||
|
||||
### 1. 橘视厅媒资接口(/manage/jetsen-media/**,JWT鉴权)
|
||||
- catalog-tree/list/search/export/nav/tags
|
||||
- 栏目树(一级+二级),list必须用二级栏目id(如472=3221条)
|
||||
- orderinfo:"1"必须带
|
||||
**部署**: Docker (eclipse-temurin:17-jre-alpine),端口 8084,Spring Profile = prod
|
||||
|
||||
### 2. 第三方开放接口(/openapi/partner/**,appKey签名鉴权)
|
||||
- PartnerSignFilter:MD5(appKey+timestamp+nonce+appSecret).toUpperCase(),5分钟有效+nonce防重放
|
||||
- 媒资接口(/openapi/partner/media/):package/catalog-tree/list/search
|
||||
- 三级授权:CONTENT(单部内容)>CATALOG(栏目)>FULL(全量),按open_api_content_package配置判定
|
||||
- 天卡代销(/openapi/partner/card/):consume(消耗天卡绑定设备)/stock(库存)
|
||||
- 调用日志:open_api_call_log
|
||||
---
|
||||
|
||||
### 3. 代销模式
|
||||
- 采购:POST /manage/partner-card/purchase(平台卡池UNUSED->RESERVED,记入partner池子AVAILABLE)
|
||||
- 消耗:POST /openapi/partner/card/consume(partner池子取1张,绑定设备,RESERVED->USED,异步激活橘视厅)
|
||||
- 回退:POST /manage/partner-card/revert(AVAILABLE的天卡回退,RESERVED->UNUSED,partner池子DISABLED)
|
||||
- 免activationCode init:/api/jst/init不传activationCode时按设备标识查找已注册设备(代销模式)
|
||||
- APK流程:consume消耗天卡 -> APK init(免activationCode) -> checkBenefits拿thirdCardNo -> 播放
|
||||
## 二、组织架构与数据隔离
|
||||
|
||||
### 4. 内容包管理(/manage/content-package/**,JWT鉴权)
|
||||
- CRUD + fetch-contents(从橘视厅拉内容预览)+ save-from-selection(勾选内容一键保存)
|
||||
- info/info-by-partner返回contentList(解析后的内容数组)
|
||||
- save-from-selection支持传id覆盖更新
|
||||
### 四层组织体系
|
||||
|
||||
### 5. 接口风格统一
|
||||
- 所有接口返回:{code:0, msg:"success", data:{...}}
|
||||
- APK设备接口原code:200已改0,sign从顶层移入data
|
||||
- UI接口原retCode:"00000000"已改{code:0,msg,data}
|
||||
- 管理后台R.ok().put()全部改R.dataSuccess()(数据在data内)
|
||||
- 路径统一:/sys/管理接口移到/manage/(仅login/refresh/logout保留/sys/)
|
||||
- 合作伙伴管理从/openapi/partner移到/manage/partner(JWT鉴权)
|
||||
- 删除冗余DayCardPoolController(和DayCardManageController冲突)
|
||||
```
|
||||
平台(Platform) > 渠道(Channel) > 代理商(Agent/SERVICE_PROVIDER) > 门店(Store)
|
||||
```
|
||||
|
||||
### 6. 审计日志
|
||||
- audit_log表已重建(字段对齐AuditLogEntity)
|
||||
- 46个@AuditAction审计点(资金/资产/设备/组织写操作)
|
||||
- AuditLogAspect切面自动记录
|
||||
- 每个组织节点有 `mpath` 物化路径(如 `1.2.3.`),用于快速判断层级关系
|
||||
- 用户通过 `sys_user.org_id` 关联到某个组织节点
|
||||
- `org_id = NULL` 为超级管理员,不限制数据范围
|
||||
|
||||
## 数据库关键表
|
||||
- 合作伙伴:open_api_partner(appKey/appSecret)
|
||||
- 内容包:open_api_content_package(partnerId/catalogIds/contentCodes)
|
||||
- partner天卡池:open_api_partner_card_pool(partnerId/poolId/cardCode/status)
|
||||
- 调用日志:open_api_call_log
|
||||
- 审计日志:audit_log(已重建)
|
||||
- 天卡池:day_card_pool(UNUSED->RESERVED->USED->REUSABLE)
|
||||
- 设备:device(含androidId/manufacturer/model/brand扩展字段)
|
||||
### 数据隔离机制
|
||||
|
||||
## 测试验证状态
|
||||
- ✅ 媒资接口(4个)+ appKey签名鉴权 + 三级授权过滤
|
||||
- ✅ 采购天卡 + 消耗天卡 + 回退天卡
|
||||
- ✅ 内容包配置(fetch-contents + save-from-selection)
|
||||
- ✅ 禁用合作伙伴 -> 鉴权失败
|
||||
- ✅ 删除内容包 -> FULL全量fallback
|
||||
- ✅ 管理后台前端已适配新路径和返回格式
|
||||
- **隔离工具**: `SysOrgService.applyBusinessOrgScope(wrapper, orgColumn, operatorId)` —— 向业务表 QueryWrapper 注入子查询,按 `sys_org.mpath` 前缀匹配过滤
|
||||
- **权限校验**: `SysOrgService.isOrgInScope(orgId, operatorId)` —— 判断目标组织是否在操作人范围内
|
||||
- **已接入隔离的模块**: 设备、订单、退款、统计、定价、支付配置、激活码、用户管理
|
||||
- **平台级资源不隔离**: 天卡池(全局库存)、系统菜单
|
||||
|
||||
## 待处理事项
|
||||
1. **服务器部署**:解决MySQL连接封锁后重启Docker容器,验证启动成功
|
||||
2. **直营模式**(方式2):EPG调开放订单接口 -> 微信支付 -> 自动发卡(未开发)
|
||||
3. **旧代码清理**:DayCardSettleController(旧分账)/MediaController(桩)/ContractServiceImpl(桩)等废弃代码
|
||||
4. **Nginx反代+HTTPS**:宝塔配反代,第三方用域名调用
|
||||
5. **前端菜单**:合作伙伴管理/内容包管理/天卡池管理的菜单页面
|
||||
---
|
||||
|
||||
## 关键配置
|
||||
- docker-compose.yml:DB_URL=jstopenapi, REDIS_HOST=host.docker.internal, JWT_SECRET=2F064BA10037477FBB3A1BF9D508AE96, API_SIGN_SECRET=5F2A2DD8BFBA4DA1B87EFBB405671B3D
|
||||
- application-prod.yml:和dev相同,环境变量传入
|
||||
- Druid wall filter:dev和prod都禁用(不兼容SKIP LOCKED)
|
||||
- Lombok:1.18.24,必须用JDK17编译(JDK21+不兼容)
|
||||
## 三、核心业务流程
|
||||
|
||||
## 合作伙伴测试数据(本地dev-0418)
|
||||
- 华住酒店:partnerId=0f91ea23961e4983, appKey=b9f9542e1e044466, appSecret=d117d67429b941c1bc0e0f6044e39d62
|
||||
- 测试合作伙伴:partnerId=1, appKey=71df57e7e47a4a88, appSecret=512f147432124bb48b313365ee31b87b
|
||||
- 内容包:id=3, name=高分3部, catalogIds=472, contentCount=3, CONTENT级别
|
||||
### 3.1 设备初始化与鉴权
|
||||
|
||||
```
|
||||
设备 App 启动
|
||||
-> POST /api/jst/init (body: activationCode + mac/deviceSn/deviceUid)
|
||||
-> 后端创建/更新设备记录,生成 sign
|
||||
-> 返回 { sign, deviceId }
|
||||
-> 后续所有请求在 header 携带 sign
|
||||
```
|
||||
|
||||
- **激活码模式**: 设备通过管理后台生成的门店激活码初始化,绑定到门店组织
|
||||
- **代销模式**: 第三方合作伙伴的设备免激活码,通过设备标识匹配已注册设备
|
||||
- **sign 机制**: 后端 `ApiDeviceSignFilter` 验证 sign,解析出 deviceId 放入 request attribute。sign 有时效,可通过 `heartBeat` 续期或 `refreshSign` 刷新
|
||||
|
||||
### 3.2 天卡购买流程
|
||||
|
||||
```
|
||||
1. 设备查询权益 -> POST /api/jst/checkBenefits -> UNBOUND(无权益)
|
||||
2. 设备查询价格 -> POST /api/jst/price -> 返回价格
|
||||
3. 设备创建订单 -> POST /api/jst/daycard/create
|
||||
-> Redis 防重锁(按 deviceId)
|
||||
-> 解析设备组织链路(设备 -> 门店 -> 渠道 -> 代理商)
|
||||
-> 3层定价匹配(门店 > 渠道 > 平台默认)
|
||||
-> 插入订单(状态 PENDING)
|
||||
-> 调微信 Native 下单,获取 code_url
|
||||
-> SKIP LOCKED 抢卡(UNUSED -> RESERVED)
|
||||
-> 返回 { orderNo, codeUrl, amount, expireAt }
|
||||
4. App 用 codeUrl 生成二维码,用户扫码支付
|
||||
5. 用户取消支付 -> POST /api/jst/daycard/cancel/{orderNo}
|
||||
-> 校验订单归属 -> 微信关单 -> 释放卡(RESERVED -> UNUSED)-> 订单 CANCELLED -> 清理 Redis 锁
|
||||
6. 用户支付成功 -> 微信回调 -> 异步激活
|
||||
```
|
||||
|
||||
### 3.3 支付回调与异步激活
|
||||
|
||||
```
|
||||
微信支付回调 -> POST /openapi/payment/wechat/notify
|
||||
-> 验签(从 header 提取 Wechatpay-Serial/Nonce/Signature/Timestamp,HttpServletRequest.getHeader() 大小写不敏感)
|
||||
-> 解密获取交易信息
|
||||
-> 幂等检查(订单已 PAID 则直接返回)
|
||||
-> 乐观锁更新订单 PAID + 卡状态 RESERVED -> USED
|
||||
-> 异步激活(@Async):
|
||||
1. jushitingApiService.getToken(device, thirdCardNo) -> 获取 token + memberCode
|
||||
2. jushitingApiService.activateCard(deviceId, cardCode, memberCode, token) -> 激活卡密
|
||||
3. jushitingApiService.queryVipExpireTime(deviceId, thirdCardNo) -> 查询VIP到期时间
|
||||
4. 更新 day_card_pool: expire_at + activate_status=SUCCESS
|
||||
-> 清理 Redis 防重锁
|
||||
```
|
||||
|
||||
### 3.4 橘视厅权益映射
|
||||
|
||||
```
|
||||
关键标识 thirdCardNo:
|
||||
- 卡密导入时自动生成(D + 24位随机字符)
|
||||
- 存储在 day_card_pool.third_card_no
|
||||
- 作为橘视厅的 uid 和 mac 参数(mac = thirdCardNo + "_bul_" + businessLine)
|
||||
- 设备通过 checkBenefits 获取 thirdCardNo
|
||||
- 设备用 thirdCardNo 直连橘视厅获取 token,激活卡密,获得 VIP 权益
|
||||
|
||||
设备与卡的关联:
|
||||
day_card_pool.device_id -> device.id -> device.mac/device_sn/device_uid
|
||||
解绑时清除 device_id,卡状态保持 USED(终态不可复用)
|
||||
```
|
||||
|
||||
### 3.5 卡状态流转
|
||||
|
||||
```
|
||||
UNUSED -> RESERVED -> USED(终态)
|
||||
导入 下单抢卡 支付成功
|
||||
|
||||
取消/超时: RESERVED -> UNUSED(释放回池)
|
||||
退款: USED -> UNUSED(清除 device_id/order_no/expire_at/activate_status)
|
||||
解绑: USED 保持不变,仅清除 device_id
|
||||
```
|
||||
|
||||
### 3.6 代销模式(合作伙伴)
|
||||
|
||||
```
|
||||
采购: 平台天卡池 UNUSED -> RESERVED,写入 partner_card_pool(status=AVAILABLE)
|
||||
消耗: POST /openapi/partner/card/consume
|
||||
-> partner 池取1张 AVAILABLE 卡
|
||||
-> day_card_pool RESERVED -> USED + device_id
|
||||
-> partner_card_pool AVAILABLE -> CONSUMED
|
||||
-> 异步激活(同上)
|
||||
-> App 后续用同一设备标识调 init + checkBenefits
|
||||
回退: partner_card_pool AVAILABLE -> DISABLED,平台卡 RESERVED -> UNUSED
|
||||
```
|
||||
|
||||
### 3.7 分账(需微信商户号开通分账权限)
|
||||
|
||||
```
|
||||
支付成功后触发 executeSharing(orderNo):
|
||||
-> 匹配分润规则(全局/渠道/代理商/门店四级)
|
||||
-> 计算各方分账金额(比例或固定,非平台方总比例 ≤ 30%)
|
||||
-> 创建 profit_sharing_order + profit_sharing_detail
|
||||
-> 调微信 createProfitSharing API(设置 unfreezeUnsplit=true 自动解冻剩余)
|
||||
-> 调微信 finishProfitSharing 完结分账
|
||||
|
||||
注意: 当前分账标记(SettleInfo.profitSharing=true)已注释,需商户号开通分账权限后取消注释
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、接口体系
|
||||
|
||||
### 4.1 设备端接口(/api/jst/,sign 鉴权)
|
||||
|
||||
| 接口 | 说明 |
|
||||
|------|------|
|
||||
| POST /api/jst/init | 设备初始化,返回 sign |
|
||||
| POST /api/jst/checkBenefits | 查询天卡权益,返回 thirdCardNo |
|
||||
| POST /api/jst/price | 查询天卡价格 |
|
||||
| POST /api/jst/daycard/create | 创建天卡订单 |
|
||||
| GET /api/jst/daycard/status/{orderNo} | 查询支付状态 |
|
||||
| POST /api/jst/daycard/cancel/{orderNo} | 取消订单 |
|
||||
| POST /api/jst/heartBeat | 心跳续期 |
|
||||
| POST /api/jst/refreshSign | 刷新 sign |
|
||||
|
||||
### 4.2 管理后台接口(/manage/,JWT 鉴权)
|
||||
|
||||
覆盖:系统管理(用户/角色/菜单/组织/门店)、设备管理、天卡池/订单/退款/激活码、定价策略、支付配置、分账规则/记录、统计概览、合作伙伴管理、媒资管理等,共约 100+ 个接口。
|
||||
|
||||
### 4.3 合作伙伴接口(/openapi/partner/,appKey/appSecret 签名鉴权)
|
||||
|
||||
| 接口 | 说明 |
|
||||
|------|------|
|
||||
| POST /openapi/partner/card/consume | 消耗天卡(代销) |
|
||||
| GET /openapi/partner/card/stock | 查询库存 |
|
||||
| GET /openapi/partner/media/* | 媒资接口(内容包/栏目树/搜索等) |
|
||||
|
||||
### 4.4 微信回调接口(/openapi/payment/,无鉴权)
|
||||
|
||||
| 接口 | 说明 |
|
||||
|------|------|
|
||||
| POST /openapi/payment/wechat/notify | 支付回调 |
|
||||
| POST /openapi/payment/refund/notify | 退款回调 |
|
||||
| GET /openapi/payment/query/{orderNo} | 查询支付状态 |
|
||||
|
||||
### 4.5 统一响应结构
|
||||
|
||||
```json
|
||||
{ "code": 0, "msg": "", "data": {} }
|
||||
```
|
||||
- code=0 成功,code!=0 失败(500 业务错误,403 权限错误)
|
||||
- data 始终存在,列表分页用 `data.page`,详情用 `data.{业务字段}`
|
||||
|
||||
---
|
||||
|
||||
## 五、定价体系
|
||||
|
||||
三层维度优先级匹配:门店配置价 > 渠道配置价 > 平台默认价
|
||||
|
||||
| 配置层级 | channel_org_id | store_org_id |
|
||||
|---------|---------------|-------------|
|
||||
| 平台默认 | NULL | NULL |
|
||||
| 渠道级 | 具体值 | NULL |
|
||||
| 门店级 | NULL | 具体值 |
|
||||
|
||||
---
|
||||
|
||||
## 六、定时任务
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| DayCardOrderTimeoutTask | 取消超时未支付订单(15分钟),释放卡 |
|
||||
| DayCardActivateRetryTask | 重试激活失败的天卡 |
|
||||
| ProfitSharingQueryTask | 查询分账状态,更新分账单 |
|
||||
|
||||
---
|
||||
|
||||
## 七、橘视厅平台对接
|
||||
|
||||
### 平台级凭证(用于内容/UI接口,非设备级)
|
||||
|
||||
```
|
||||
SP_TOKEN = "9006874392099a89506142c6df9e3314"
|
||||
KEY = "alltvjs"
|
||||
PORTAL_CODE = "PCODE-JC"
|
||||
sign = MD5(sptoken + timeshift + portalcode + key).toUpperCase()
|
||||
```
|
||||
|
||||
App 用此凭证直连橘视厅 `/IPTVPortalInterface/` 下的接口(topButton、gettagslist、getcontentlist、search 等),获取内容和 UI。**不经过我们后端**。
|
||||
|
||||
### 设备级权益凭证(thirdCardNo)
|
||||
|
||||
- 由后端在卡密导入时自动生成
|
||||
- 设备通过 `checkBenefits` 获取
|
||||
- 用于橘视厅免密登录(getToken)和卡密激活(activateCard)
|
||||
- `getToken` 的 uid 和 mac 参数都基于 thirdCardNo 构造,设备实体不传给橘视厅
|
||||
|
||||
### 橘视厅 API 基地址
|
||||
|
||||
- 正式环境: `https://api.jetsenx.huashi.tv/`
|
||||
- 测试环境: `https://external.jetsenx.huashi.tv/`
|
||||
- 媒资接口: `https://servhub-ep03.future.jetsentv.com/`
|
||||
|
||||
---
|
||||
|
||||
## 八、微信支付配置
|
||||
|
||||
- 支持多商户号配置(DB `wechat_pay_config` 表,四级优先级:门店 > 代理商 > 渠道 > 全局)
|
||||
- DB 配置优先,回退到 yml 环境变量
|
||||
- SDK 配置实例按 商户号+证书序列号+密钥+私钥 缓存,配置变更自动重建
|
||||
- 回调验签支持多商户号(遍历所有 ACTIVE 配置逐个尝试解析)
|
||||
|
||||
---
|
||||
|
||||
## 九、待办与注意事项
|
||||
|
||||
1. **分账功能**: 代码已实现但分账标记(`SettleInfo.profitSharing=true`)已注释,需微信商户号开通分账权限后取消注释(WechatPayServiceImpl 和 DayCardOrderServiceImpl.payCallback 中)
|
||||
2. **退款SDK**: `WechatPayServiceImpl.refund()` 目前是骨架实现(TODO 接入 wechatpay-java RefundService)
|
||||
3. **设备标识改造**: 当前卡池用 `device_id`(Long, 自增) 关联设备,计划改为 `device_uid`(String) 关联,已搁置
|
||||
4. **天卡池不隔离**: 天卡池为平台级库存资源,不按组织隔离,所有管理员可见
|
||||
5. **媒资接口分页不统一**: 3个媒资相关端点用 `data.list` + `data.totalCount` 平铺式分页,与其他接口的 `data.page` 不同
|
||||
6. **支付接口路径**: 当前支付接口在 `/openapi/payment/` 下,计划迁移到 `/payment/` 与第三方接口区分(未实施)
|
||||
7. **密码哈希**: Shiro `Sha256Hash(password, salt)` 哈希顺序为 `salt + password`(不是 password + salt)
|
||||
|
||||
---
|
||||
|
||||
## 十、数据库与部署
|
||||
|
||||
### 数据库
|
||||
- MySQL 8.0,数据库名 `jstopenapi`
|
||||
- 完整建表脚本: `db/full-schema.sql`
|
||||
- 初始化数据: `db/init-sys-data.sql`(菜单 + 角色 + 超管用户 admin/admin)
|
||||
- 逻辑删除: `delete_flag` 字段(UNDEL/DEL)
|
||||
|
||||
### Redis
|
||||
- 用于:设备 sign 缓存、防重锁、橘视厅 token 缓存、激活锁
|
||||
- Key 前缀: `daycard:device:`(订单防重)、`api:thirdparty:activate:`(激活相关)
|
||||
|
||||
### 部署
|
||||
```bash
|
||||
# 构建
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# Docker 部署
|
||||
docker-compose up -d
|
||||
|
||||
# 关键环境变量(docker-compose.yml 中配置)
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
DB_URL / DB_USERNAME / DB_PASSWORD
|
||||
REDIS_HOST / REDIS_PORT / REDIS_PASSWORD
|
||||
JWT_SECRET / API_SIGN_SECRET
|
||||
WECHAT_PAY_* # 微信支付配置(或通过 DB wechat_pay_config 表配置)
|
||||
```
|
||||
|
||||
### 超管账号
|
||||
- 用户名: admin / 密码: admin
|
||||
- 新增超管: wangrui / 密码: Admin@2026.
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.entity.DayCardActivationCodeEntity;
|
||||
import io.as.modules.daycard.service.DayCardActivationCodeService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
@@ -27,7 +28,7 @@ import java.util.Map;
|
||||
@Api(tags = "天卡设备激活码")
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard/activation-code")
|
||||
public class DayCardActivationCodeController {
|
||||
public class DayCardActivationCodeController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DayCardActivationCodeService dayCardActivationCodeService;
|
||||
@@ -41,6 +42,9 @@ public class DayCardActivationCodeController {
|
||||
@ApiOperation("按门店查询激活码列表")
|
||||
@GetMapping("/list/{storeOrgId}")
|
||||
public R listByStore(@PathVariable("storeOrgId") Long storeOrgId) {
|
||||
if (!sysOrgService.isOrgInScope(storeOrgId, getUserId())) {
|
||||
throw new ASException("无权限操作该门店的数据");
|
||||
}
|
||||
List<DayCardActivationCodeEntity> list = dayCardActivationCodeService.listByStoreOrgId(storeOrgId);
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (DayCardActivationCodeEntity entity : list) {
|
||||
@@ -61,6 +65,9 @@ public class DayCardActivationCodeController {
|
||||
if (request.getStoreOrgId() == null) {
|
||||
throw new ASException("门店组织ID不能为空");
|
||||
}
|
||||
if (!sysOrgService.isOrgInScope(request.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该门店的数据");
|
||||
}
|
||||
// 校验门店存在
|
||||
SysOrgEntity storeOrg = sysOrgService.getById(request.getStoreOrgId());
|
||||
if (storeOrg == null) {
|
||||
@@ -70,9 +77,7 @@ public class DayCardActivationCodeController {
|
||||
Long channelOrgId = resolveChannelOrgId(storeOrg);
|
||||
DayCardActivationCodeEntity entity = dayCardActivationCodeService.generateForStore(
|
||||
request.getStoreOrgId(), channelOrgId, request.getRemark());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", toMap(entity));
|
||||
return R.dataSuccess(data);
|
||||
return R.dataSuccess(toMap(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,12 +87,17 @@ public class DayCardActivationCodeController {
|
||||
@AuditAction(value = "ACTCODE_REGENERATE", description = "重新生成激活码")
|
||||
@PostMapping("/{id}/regenerate")
|
||||
public R regenerate(@PathVariable("id") Long id) {
|
||||
DayCardActivationCodeEntity entity = dayCardActivationCodeService.getById(id);
|
||||
if (entity == null) {
|
||||
throw new ASException("激活码不存在");
|
||||
}
|
||||
if (!sysOrgService.isOrgInScope(entity.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该门店的数据");
|
||||
}
|
||||
String newCode = dayCardActivationCodeService.regenerateCode(id);
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("code", newCode);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", item);
|
||||
return R.dataSuccess(data);
|
||||
return R.dataSuccess(item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,8 +107,15 @@ public class DayCardActivationCodeController {
|
||||
@AuditAction(value = "ACTCODE_UPDATE_STATUS", description = "修改激活码状态")
|
||||
@PutMapping("/{id}/status")
|
||||
public R updateStatus(@PathVariable("id") Long id, @RequestBody UpdateStatusRequest request) {
|
||||
DayCardActivationCodeEntity entity = dayCardActivationCodeService.getById(id);
|
||||
if (entity == null) {
|
||||
throw new ASException("激活码不存在");
|
||||
}
|
||||
if (!sysOrgService.isOrgInScope(entity.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该门店的数据");
|
||||
}
|
||||
dayCardActivationCodeService.updateStatus(id, request.getStatus());
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,8 +125,15 @@ public class DayCardActivationCodeController {
|
||||
@AuditAction(value = "ACTCODE_DELETE", description = "删除激活码")
|
||||
@DeleteMapping("/{id}")
|
||||
public R delete(@PathVariable("id") Long id) {
|
||||
DayCardActivationCodeEntity entity = dayCardActivationCodeService.getById(id);
|
||||
if (entity == null) {
|
||||
throw new ASException("激活码不存在");
|
||||
}
|
||||
if (!sysOrgService.isOrgInScope(entity.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该门店的数据");
|
||||
}
|
||||
dayCardActivationCodeService.delete(id);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
// ==================== 内部方法 ====================
|
||||
|
||||
@@ -9,6 +9,10 @@ import io.as.modules.daycard.entity.DayCardPoolEntity;
|
||||
import io.as.modules.daycard.service.DayCardOrderService;
|
||||
import io.as.modules.daycard.service.DayCardPoolService;
|
||||
import io.as.modules.daycard.vo.DayCardPoolStatsVo;
|
||||
import io.as.modules.device.entity.DeviceEntity;
|
||||
import io.as.modules.device.service.DeviceService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -24,7 +28,7 @@ import java.util.Map;
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard")
|
||||
@RequiresAuthentication
|
||||
public class DayCardManageController {
|
||||
public class DayCardManageController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DayCardPoolService dayCardPoolService;
|
||||
@@ -32,10 +36,17 @@ public class DayCardManageController {
|
||||
@Autowired
|
||||
private DayCardOrderService dayCardOrderService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Autowired
|
||||
private DeviceService deviceService;
|
||||
|
||||
// ===================== 天卡池 =====================
|
||||
|
||||
/**
|
||||
* 天卡池列表
|
||||
* <p>天卡池为平台级资源,不按组织隔离</p>
|
||||
*/
|
||||
@GetMapping("/pool/list")
|
||||
public R poolList(@RequestParam Map<String, Object> params) {
|
||||
@@ -94,8 +105,7 @@ public class DayCardManageController {
|
||||
*/
|
||||
@GetMapping("/order/list")
|
||||
public R orderList(@RequestParam Map<String, Object> params) {
|
||||
Long partnerId = parsePartnerId(params);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, partnerId);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -108,6 +118,10 @@ public class DayCardManageController {
|
||||
*/
|
||||
@GetMapping("/benefits/{deviceId}")
|
||||
public R deviceBenefits(@PathVariable Long deviceId) {
|
||||
DeviceEntity device = deviceService.getById(deviceId);
|
||||
if (device != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限查看该设备");
|
||||
}
|
||||
DayCardPoolEntity pool = dayCardOrderService.getDeviceBenefits(deviceId);
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
if (pool != null) {
|
||||
@@ -118,9 +132,7 @@ public class DayCardManageController {
|
||||
} else {
|
||||
item.put("hasBenefits", false);
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", item);
|
||||
return R.dataSuccess(data);
|
||||
return R.dataSuccess(item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,8 +142,14 @@ public class DayCardManageController {
|
||||
@AuditAction(value = "DAYCARD_DEVICE_UNBIND", description = "解绑设备天卡权益")
|
||||
@PostMapping("/device/unbind")
|
||||
public R unbindDevice(@RequestParam Long deviceId) {
|
||||
DeviceEntity device = deviceService.getById(deviceId);
|
||||
if (device != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限操作该设备");
|
||||
}
|
||||
dayCardOrderService.unbindDevice(deviceId);
|
||||
return R.ok("设备解绑成功");
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("message", "设备解绑成功");
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,8 +159,14 @@ public class DayCardManageController {
|
||||
@AuditAction(value = "DAYCARD_DEVICE_UNBIND_ORG", description = "设备组织解绑")
|
||||
@PostMapping("/device/unbind-org")
|
||||
public R unbindDeviceOrg(@RequestParam Long deviceId) {
|
||||
DeviceEntity device = deviceService.getById(deviceId);
|
||||
if (device != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限操作该设备");
|
||||
}
|
||||
dayCardOrderService.unbindDeviceOrg(deviceId);
|
||||
return R.ok("设备组织解绑成功");
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("message", "设备组织解绑成功");
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
// ===================== 内部方法 =====================
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.entity.DayCardOrderEntity;
|
||||
import io.as.modules.daycard.service.DayCardOrderService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -20,7 +21,7 @@ import java.util.Map;
|
||||
@Api(tags = "天卡订单")
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard")
|
||||
public class DayCardOrderController {
|
||||
public class DayCardOrderController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DayCardOrderService dayCardOrderService;
|
||||
@@ -31,8 +32,7 @@ public class DayCardOrderController {
|
||||
@ApiOperation("分页查询天卡订单")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
Long partnerId = parsePartnerId(params);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, partnerId);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -52,22 +52,4 @@ public class DayCardOrderController {
|
||||
data.put("order", order);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求参数中解析 partnerId(兼容存量数据查询,新订单 partnerId 为 null)
|
||||
*/
|
||||
private Long parsePartnerId(Map<String, Object> params) {
|
||||
Object val = params.get("partnerId");
|
||||
if (val == null) {
|
||||
val = params.get("partner_id");
|
||||
}
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(String.valueOf(val));
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.constants.DayCardConstants;
|
||||
import io.as.modules.daycard.entity.DayCardPriceConfigEntity;
|
||||
import io.as.modules.daycard.service.DayCardPriceConfigService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,18 +26,21 @@ import java.util.Map;
|
||||
@Api(tags = "天卡定价策略管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/price")
|
||||
public class DayCardPriceConfigController {
|
||||
public class DayCardPriceConfigController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DayCardPriceConfigService dayCardPriceConfigService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 分页查询定价策略(支持 partnerId/status 筛选)
|
||||
*/
|
||||
@ApiOperation("分页查询定价策略")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardPriceConfigService.queryPage(params);
|
||||
PageUtils page = dayCardPriceConfigService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -51,6 +56,7 @@ public class DayCardPriceConfigController {
|
||||
if (config == null) {
|
||||
throw new ASException("定价策略不存在");
|
||||
}
|
||||
checkPriceConfigOrgScope(config);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("config", config);
|
||||
return R.dataSuccess(data);
|
||||
@@ -80,6 +86,7 @@ public class DayCardPriceConfigController {
|
||||
@AuditAction(value = "PRICE_SAVE", description = "新增定价策略")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody DayCardPriceConfigEntity config) {
|
||||
checkPriceConfigOrgScope(config);
|
||||
Date now = new Date();
|
||||
config.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
config.setCreatedAt(now);
|
||||
@@ -88,7 +95,9 @@ public class DayCardPriceConfigController {
|
||||
config.setStatus("ACTIVE");
|
||||
}
|
||||
dayCardPriceConfigService.save(config);
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", config.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,9 +110,14 @@ public class DayCardPriceConfigController {
|
||||
if (config.getId() == null) {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
DayCardPriceConfigEntity existing = dayCardPriceConfigService.getById(config.getId());
|
||||
if (existing == null) {
|
||||
throw new ASException("定价策略不存在");
|
||||
}
|
||||
checkPriceConfigOrgScope(existing);
|
||||
config.setUpdatedAt(new Date());
|
||||
dayCardPriceConfigService.updateById(config);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,12 +132,28 @@ public class DayCardPriceConfigController {
|
||||
}
|
||||
List<DayCardPriceConfigEntity> list = dayCardPriceConfigService.listByIds(
|
||||
Arrays.asList(ids));
|
||||
for (DayCardPriceConfigEntity config : list) {
|
||||
checkPriceConfigOrgScope(config);
|
||||
}
|
||||
Date now = new Date();
|
||||
for (DayCardPriceConfigEntity config : list) {
|
||||
config.setDeleteFlag(DayCardConstants.DELETE_FLAG_DEL);
|
||||
config.setUpdatedAt(now);
|
||||
}
|
||||
dayCardPriceConfigService.updateBatchById(list);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
private void checkPriceConfigOrgScope(DayCardPriceConfigEntity config) {
|
||||
Long orgId = config.getStoreOrgId() != null ? config.getStoreOrgId() : config.getChannelOrgId();
|
||||
if (orgId != null) {
|
||||
if (!sysOrgService.isOrgInScope(orgId, getUserId())) {
|
||||
throw new ASException("无权限操作该组织的数据");
|
||||
}
|
||||
} else {
|
||||
if (sysOrgService.getUserOrgMpath(getUserId()) != null) {
|
||||
throw new ASException("无权限操作平台默认配置");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ import io.as.common.annotation.AuditAction;
|
||||
import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.entity.DayCardOrderEntity;
|
||||
import io.as.modules.daycard.entity.DayCardRefundEntity;
|
||||
import io.as.modules.daycard.service.DayCardOrderService;
|
||||
import io.as.modules.daycard.service.DayCardRefundService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -21,18 +25,24 @@ import java.util.Map;
|
||||
@Api(tags = "天卡退款管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/refund")
|
||||
public class DayCardRefundController {
|
||||
public class DayCardRefundController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DayCardRefundService dayCardRefundService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Autowired
|
||||
private DayCardOrderService dayCardOrderService;
|
||||
|
||||
/**
|
||||
* 分页查询退款记录
|
||||
*/
|
||||
@ApiOperation("分页查询退款记录")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardRefundService.queryPage(params);
|
||||
PageUtils page = dayCardRefundService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -48,6 +58,12 @@ public class DayCardRefundController {
|
||||
if (refund == null) {
|
||||
throw new ASException("退款记录不存在");
|
||||
}
|
||||
// 组织数据隔离:校验关联订单是否在操作人数据范围内
|
||||
DayCardOrderEntity order = dayCardOrderService.getByOrderNo(refund.getOrderNo());
|
||||
if (order != null && order.getStoreOrgId() != null
|
||||
&& !sysOrgService.isOrgInScope(order.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限查看该退款记录");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("refund", refund);
|
||||
return R.dataSuccess(data);
|
||||
@@ -66,6 +82,15 @@ public class DayCardRefundController {
|
||||
if (StringUtils.isBlank(orderNo)) {
|
||||
throw new ASException("orderNo不能为空");
|
||||
}
|
||||
// 组织数据隔离:校验订单是否在操作人数据范围内
|
||||
DayCardOrderEntity order = dayCardOrderService.getByOrderNo(orderNo);
|
||||
if (order == null) {
|
||||
throw new ASException("订单不存在: " + orderNo);
|
||||
}
|
||||
if (order.getStoreOrgId() != null
|
||||
&& !sysOrgService.isOrgInScope(order.getStoreOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该订单的退款");
|
||||
}
|
||||
String reason = params.get("reason");
|
||||
DayCardRefundEntity refund = dayCardRefundService.refund(orderNo, reason);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
@@ -96,7 +96,7 @@ public class DayCardSettleController {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
dayCardSettleRuleService.update(rule);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ public class DayCardSettleController {
|
||||
@PostMapping("/rule/delete")
|
||||
public R ruleDelete(@RequestBody Long[] ids) {
|
||||
dayCardSettleRuleService.delete(ids);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
// ==================== 分账记录 ====================
|
||||
@@ -185,8 +185,6 @@ public class DayCardSettleController {
|
||||
throw new ASException("totalAmount必须大于0");
|
||||
}
|
||||
Map<String, Object> result = profitCalcService.calculate(partnerId, channelOrgId, storeOrgId, totalAmount);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", result);
|
||||
return R.dataSuccess(data);
|
||||
return R.dataSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class OrgSettlementAccountController {
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
orgSettlementAccountService.delete(ids);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +93,6 @@ public class OrgSettlementAccountController {
|
||||
@PostMapping("/sync/{id}")
|
||||
public R sync(@PathVariable("id") Long id) {
|
||||
orgSettlementAccountService.syncToWechat(id);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ProfitSharingRuleController {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
profitSharingRuleService.updateRule(rule);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ public class ProfitSharingRuleController {
|
||||
public R delete(@RequestBody Map<String, Object> body) {
|
||||
Long id = Long.parseLong(body.get("id").toString());
|
||||
profitSharingRuleService.delete(id);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,8 +245,6 @@ public class ProfitSharingRuleController {
|
||||
result.put("details", details);
|
||||
result.put("nonPlatformTotalRatio", nonPlatformTotalRatio);
|
||||
result.put("warning", warning);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", result);
|
||||
return R.dataSuccess(data);
|
||||
return R.dataSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ public interface DayCardOrderService extends IService<DayCardOrderEntity> {
|
||||
/**
|
||||
* 分页查询天卡订单
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @param partnerId 合作商ID
|
||||
* @param params 查询参数
|
||||
* @param operatorId 当前操作人userId(用于组织数据隔离)
|
||||
*/
|
||||
PageUtils queryPage(Map<String, Object> params, Long partnerId);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
|
||||
/**
|
||||
* 创建天卡订单(从 sign 鉴权上下文获取 deviceId)
|
||||
|
||||
@@ -14,8 +14,11 @@ public interface DayCardPriceConfigService extends IService<DayCardPriceConfigEn
|
||||
|
||||
/**
|
||||
* 分页查询定价策略
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
*/
|
||||
PageUtils queryPage(Map<String, Object> params);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
|
||||
/**
|
||||
* 获取有效定价策略(3 层维度,优先级从高到低)
|
||||
|
||||
@@ -13,8 +13,11 @@ public interface DayCardRefundService extends IService<DayCardRefundEntity> {
|
||||
|
||||
/**
|
||||
* 分页查询退款记录
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
*/
|
||||
PageUtils queryPage(Map<String, Object> params);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
|
||||
/**
|
||||
* 根据退款单号查询退款记录
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.as.modules.jst.service.JushitingApiService;
|
||||
import io.as.modules.jst.service.JstActivateCardResult;
|
||||
import io.as.modules.payment.service.WechatPayService;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -85,6 +86,9 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
||||
@Autowired
|
||||
private ApiDeviceSignService apiDeviceSignService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Value("${renren.daycard.order-timeout-minutes:15}")
|
||||
private int orderTimeoutMinutes;
|
||||
|
||||
@@ -92,14 +96,11 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
||||
private int cancelBatchSize;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params, Long partnerId) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
IPage<DayCardOrderEntity> page = new Query<DayCardOrderEntity>().getPage(params, "created_at", false);
|
||||
|
||||
QueryWrapper<DayCardOrderEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
if (partnerId != null) {
|
||||
wrapper.eq("partner_id", partnerId);
|
||||
}
|
||||
String orderNo = (String) params.get("orderNo");
|
||||
if (StringUtils.isNotBlank(orderNo)) {
|
||||
wrapper.eq("order_no", orderNo);
|
||||
@@ -108,6 +109,7 @@ public class DayCardOrderServiceImpl extends ServiceImpl<DayCardOrderDao, DayCar
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
wrapper.eq("status", status);
|
||||
}
|
||||
sysOrgService.applyBusinessOrgScope(wrapper, "store_org_id", operatorId);
|
||||
|
||||
IPage<DayCardOrderEntity> result = baseMapper.selectPage(page, wrapper);
|
||||
return new PageUtils(result);
|
||||
|
||||
@@ -9,7 +9,9 @@ import io.as.modules.daycard.constants.DayCardConstants;
|
||||
import io.as.modules.daycard.dao.DayCardPriceConfigDao;
|
||||
import io.as.modules.daycard.entity.DayCardPriceConfigEntity;
|
||||
import io.as.modules.daycard.service.DayCardPriceConfigService;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -21,8 +23,11 @@ import java.util.Map;
|
||||
@Service("dayCardPriceConfigService")
|
||||
public class DayCardPriceConfigServiceImpl extends ServiceImpl<DayCardPriceConfigDao, DayCardPriceConfigEntity> implements DayCardPriceConfigService {
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
IPage<DayCardPriceConfigEntity> page = new Query<DayCardPriceConfigEntity>().getPage(params, "created_at", false);
|
||||
|
||||
QueryWrapper<DayCardPriceConfigEntity> wrapper = new QueryWrapper<>();
|
||||
@@ -45,6 +50,8 @@ public class DayCardPriceConfigServiceImpl extends ServiceImpl<DayCardPriceConfi
|
||||
wrapper.eq("store_org_id", storeOrgId);
|
||||
}
|
||||
|
||||
sysOrgService.applyBusinessOrgScope(wrapper, "store_org_id", operatorId);
|
||||
|
||||
IPage<DayCardPriceConfigEntity> result = baseMapper.selectPage(page, wrapper);
|
||||
return new PageUtils(result);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import io.as.modules.daycard.entity.ProfitSharingDetailEntity;
|
||||
import io.as.modules.daycard.entity.ProfitSharingOrderEntity;
|
||||
import io.as.modules.daycard.service.DayCardRefundService;
|
||||
import io.as.modules.payment.service.WechatPayResult;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,6 +47,9 @@ public class DayCardRefundServiceImpl extends ServiceImpl<DayCardRefundDao, DayC
|
||||
@Autowired
|
||||
private DayCardPoolDao dayCardPoolDao;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Autowired
|
||||
private io.as.modules.daycard.dao.ProfitSharingOrderDao profitSharingOrderDao;
|
||||
@Autowired
|
||||
@@ -55,12 +59,20 @@ public class DayCardRefundServiceImpl extends ServiceImpl<DayCardRefundDao, DayC
|
||||
private io.as.modules.payment.service.WechatPayService wechatPayService;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
IPage<DayCardRefundEntity> page = new Query<DayCardRefundEntity>().getPage(params, "created_at", false);
|
||||
|
||||
QueryWrapper<DayCardRefundEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
|
||||
// 组织数据隔离:退款表无 store_org_id,通过 order_no 关联 day_card_order 做子查询过滤
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
if (mpath != null) {
|
||||
wrapper.apply("order_no IN (SELECT order_no FROM day_card_order WHERE delete_flag = 'UNDEL' " +
|
||||
"AND store_org_id IN (SELECT id FROM sys_org WHERE delete_flag = 'UNDEL' AND mpath LIKE CONCAT({0}, '%')))",
|
||||
mpath);
|
||||
}
|
||||
|
||||
String refundNo = (String) params.get("refundNo");
|
||||
if (StringUtils.isNotBlank(refundNo)) {
|
||||
wrapper.eq("refund_no", refundNo);
|
||||
|
||||
@@ -5,6 +5,8 @@ import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.device.entity.DeviceEntity;
|
||||
import io.as.modules.device.service.DeviceService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -19,18 +21,21 @@ import java.util.Map;
|
||||
@Api(tags = "终端管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/device")
|
||||
public class DeviceController {
|
||||
public class DeviceController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private DeviceService deviceService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 分页查询设备列表
|
||||
*/
|
||||
@ApiOperation("分页查询设备列表")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = deviceService.queryPage(params);
|
||||
PageUtils page = deviceService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -43,6 +48,9 @@ public class DeviceController {
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
DeviceEntity device = deviceService.getById(id);
|
||||
if (device != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限查看该设备");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("device", device);
|
||||
return R.dataSuccess(data);
|
||||
@@ -55,8 +63,13 @@ public class DeviceController {
|
||||
@AuditAction(value = "DEVICE_SAVE", description = "新增设备")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody DeviceEntity device) {
|
||||
if (device.getStoreOrgId() != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限操作该门店");
|
||||
}
|
||||
deviceService.saveDevice(device);
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", device.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,8 +79,12 @@ public class DeviceController {
|
||||
@AuditAction(value = "DEVICE_UPDATE", description = "修改设备")
|
||||
@PostMapping("/update")
|
||||
public R update(@RequestBody DeviceEntity device) {
|
||||
DeviceEntity existing = deviceService.getById(device.getId());
|
||||
if (existing != null && !sysOrgService.isOrgInScope(existing.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限操作该设备");
|
||||
}
|
||||
deviceService.updateDevice(device);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,8 +94,14 @@ public class DeviceController {
|
||||
@AuditAction(value = "DEVICE_DELETE", description = "删除设备")
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
for (Long id : ids) {
|
||||
DeviceEntity device = deviceService.getById(id);
|
||||
if (device != null && !sysOrgService.isOrgInScope(device.getStoreOrgId(), getUserId())) {
|
||||
return R.error(403, "无权限删除该设备");
|
||||
}
|
||||
}
|
||||
deviceService.delete(ids);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +111,10 @@ public class DeviceController {
|
||||
@AuditAction(value = "DEVICE_BIND_STORE", description = "设备绑定门店")
|
||||
@PostMapping("/bindStore")
|
||||
public R bindStore(@RequestParam("deviceId") Long deviceId, @RequestParam("storeOrgId") Long storeOrgId) {
|
||||
if (!sysOrgService.isOrgInScope(storeOrgId, getUserId())) {
|
||||
return R.error(403, "无权限绑定到此门店");
|
||||
}
|
||||
deviceService.bindStore(deviceId, storeOrgId);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ public interface DeviceService extends IService<DeviceEntity> {
|
||||
/**
|
||||
* 分页查询设备列表
|
||||
*
|
||||
* @param params 查询参数(支持 storeOrgId/status/deviceName/mac 筛选)
|
||||
* @param params 查询参数(支持 storeOrgId/status/deviceName/mac 筛选)
|
||||
* @param operatorId 当前操作人userId(用于组织数据隔离)
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageUtils queryPage(Map<String, Object> params);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
|
||||
/**
|
||||
* 根据ID获取设备详情
|
||||
|
||||
@@ -9,7 +9,9 @@ import io.as.common.utils.Query;
|
||||
import io.as.modules.device.dao.DeviceDao;
|
||||
import io.as.modules.device.entity.DeviceEntity;
|
||||
import io.as.modules.device.service.DeviceService;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -21,23 +23,29 @@ import java.util.Map;
|
||||
@Service("deviceService")
|
||||
public class DeviceServiceImpl extends ServiceImpl<DeviceDao, DeviceEntity> implements DeviceService {
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
String storeOrgId = (String) params.get("storeOrgId");
|
||||
String status = (String) params.get("status");
|
||||
String deviceName = (String) params.get("deviceName");
|
||||
String mac = (String) params.get("mac");
|
||||
String source = (String) params.get("source");
|
||||
|
||||
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<DeviceEntity>()
|
||||
.eq(StringUtils.isNotBlank(storeOrgId), "store_org_id", storeOrgId)
|
||||
.eq(StringUtils.isNotBlank(status), "status", status)
|
||||
.like(StringUtils.isNotBlank(deviceName), "device_name", deviceName)
|
||||
.like(StringUtils.isNotBlank(mac), "mac", mac)
|
||||
.eq(StringUtils.isNotBlank(source), "source", source)
|
||||
.eq("delete_flag", "UNDEL");
|
||||
sysOrgService.applyBusinessOrgScope(wrapper, "store_org_id", operatorId);
|
||||
|
||||
IPage<DeviceEntity> page = this.page(
|
||||
new Query<DeviceEntity>().getPage(params, "created_at", false),
|
||||
new QueryWrapper<DeviceEntity>()
|
||||
.eq(StringUtils.isNotBlank(storeOrgId), "store_org_id", storeOrgId)
|
||||
.eq(StringUtils.isNotBlank(status), "status", status)
|
||||
.like(StringUtils.isNotBlank(deviceName), "device_name", deviceName)
|
||||
.like(StringUtils.isNotBlank(mac), "mac", mac)
|
||||
.eq(StringUtils.isNotBlank(source), "source", source)
|
||||
.eq("delete_flag", "UNDEL")
|
||||
wrapper
|
||||
);
|
||||
|
||||
return new PageUtils(page);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class ContentPackageController {
|
||||
}
|
||||
contentPackage.setUpdatedAt(new Date());
|
||||
contentPackageService.updateById(contentPackage);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +114,7 @@ public class ContentPackageController {
|
||||
contentPackage.setUpdatedAt(new Date());
|
||||
contentPackageService.updateById(contentPackage);
|
||||
}
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
// ==================== 内容选择辅助接口 ====================
|
||||
|
||||
@@ -90,7 +90,7 @@ public class OpenApiPartnerController {
|
||||
}
|
||||
partner.setUpdatedAt(new Date());
|
||||
openApiPartnerService.updateById(partner);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,6 +107,6 @@ public class OpenApiPartnerController {
|
||||
partner.setUpdatedAt(new Date());
|
||||
openApiPartnerService.updateById(partner);
|
||||
}
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package io.as.modules.payment.controller;
|
||||
|
||||
import io.as.common.annotation.AuditAction;
|
||||
import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.payment.entity.WechatPayConfigEntity;
|
||||
import io.as.modules.payment.service.WechatPayConfigService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -19,18 +22,21 @@ import java.util.Map;
|
||||
@Api(tags = "支付配置管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/payconfig")
|
||||
public class WechatPayConfigController {
|
||||
public class WechatPayConfigController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private WechatPayConfigService wechatPayConfigService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 分页查询支付配置列表
|
||||
*/
|
||||
@ApiOperation("分页查询支付配置列表")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = wechatPayConfigService.queryPage(params);
|
||||
PageUtils page = wechatPayConfigService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -43,6 +49,10 @@ public class WechatPayConfigController {
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
WechatPayConfigEntity config = wechatPayConfigService.getById(id);
|
||||
if (config == null) {
|
||||
throw new ASException("支付配置不存在");
|
||||
}
|
||||
checkPayConfigOrgScope(config);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("config", config);
|
||||
return R.dataSuccess(data);
|
||||
@@ -55,8 +65,11 @@ public class WechatPayConfigController {
|
||||
@AuditAction(value = "PAYCONFIG_SAVE", description = "新增支付配置")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody WechatPayConfigEntity config) {
|
||||
checkPayConfigOrgScope(config);
|
||||
wechatPayConfigService.saveConfig(config);
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", config.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,8 +79,16 @@ public class WechatPayConfigController {
|
||||
@AuditAction(value = "PAYCONFIG_UPDATE", description = "修改支付配置")
|
||||
@PostMapping("/update")
|
||||
public R update(@RequestBody WechatPayConfigEntity config) {
|
||||
if (config.getId() == null) {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
WechatPayConfigEntity existing = wechatPayConfigService.getById(config.getId());
|
||||
if (existing == null) {
|
||||
throw new ASException("支付配置不存在");
|
||||
}
|
||||
checkPayConfigOrgScope(existing);
|
||||
wechatPayConfigService.update(config);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +98,28 @@ public class WechatPayConfigController {
|
||||
@AuditAction(value = "PAYCONFIG_DELETE", description = "删除支付配置")
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
for (Long id : ids) {
|
||||
WechatPayConfigEntity config = wechatPayConfigService.getById(id);
|
||||
if (config != null) {
|
||||
checkPayConfigOrgScope(config);
|
||||
}
|
||||
}
|
||||
wechatPayConfigService.delete(ids);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
private void checkPayConfigOrgScope(WechatPayConfigEntity config) {
|
||||
Long orgId = config.getStoreOrgId() != null ? config.getStoreOrgId()
|
||||
: config.getAgentOrgId() != null ? config.getAgentOrgId()
|
||||
: config.getChannelOrgId();
|
||||
if (orgId != null) {
|
||||
if (!sysOrgService.isOrgInScope(orgId, getUserId())) {
|
||||
throw new ASException("无权限操作该组织的数据");
|
||||
}
|
||||
} else {
|
||||
if (sysOrgService.getUserOrgMpath(getUserId()) != null) {
|
||||
throw new ASException("无权限操作全局默认配置");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ public interface WechatPayConfigService extends IService<WechatPayConfigEntity>
|
||||
/**
|
||||
* 分页查询支付配置列表
|
||||
*
|
||||
* @param params 查询参数
|
||||
* @param params 查询参数
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageUtils queryPage(Map<String, Object> params);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
|
||||
/**
|
||||
* 根据ID获取支付配置详情
|
||||
|
||||
@@ -9,7 +9,9 @@ import io.as.common.utils.Query;
|
||||
import io.as.modules.payment.dao.WechatPayConfigDao;
|
||||
import io.as.modules.payment.entity.WechatPayConfigEntity;
|
||||
import io.as.modules.payment.service.WechatPayConfigService;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -21,23 +23,39 @@ import java.util.Map;
|
||||
@Service("wechatPayConfigService")
|
||||
public class WechatPayConfigServiceImpl extends ServiceImpl<WechatPayConfigDao, WechatPayConfigEntity> implements WechatPayConfigService {
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
String partnerId = (String) params.get("partnerId");
|
||||
String status = (String) params.get("status");
|
||||
String channelOrgId = (String) params.get("channelOrgId");
|
||||
String agentOrgId = (String) params.get("agentOrgId");
|
||||
String storeOrgId = (String) params.get("storeOrgId");
|
||||
|
||||
QueryWrapper<WechatPayConfigEntity> wrapper = new QueryWrapper<WechatPayConfigEntity>()
|
||||
.eq(StringUtils.isNotBlank(partnerId), "partner_id", partnerId)
|
||||
.eq(StringUtils.isNotBlank(channelOrgId), "channel_org_id", channelOrgId)
|
||||
.eq(StringUtils.isNotBlank(agentOrgId), "agent_org_id", agentOrgId)
|
||||
.eq(StringUtils.isNotBlank(storeOrgId), "store_org_id", storeOrgId)
|
||||
.eq(StringUtils.isNotBlank(status), "status", status)
|
||||
.eq("delete_flag", "UNDEL");
|
||||
|
||||
// 组织数据隔离:非超管仅可见自身范围内的配置(store_org_id 命中或 store_org_id 为空时 channel_org_id 命中)
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
if (mpath != null) {
|
||||
wrapper.and(w -> w
|
||||
.apply("store_org_id IN (SELECT id FROM sys_org WHERE delete_flag = 'UNDEL' AND mpath LIKE CONCAT({0}, '%'))", mpath)
|
||||
.or()
|
||||
.isNull("store_org_id")
|
||||
.apply("channel_org_id IN (SELECT id FROM sys_org WHERE delete_flag = 'UNDEL' AND mpath LIKE CONCAT({0}, '%'))", mpath)
|
||||
);
|
||||
}
|
||||
|
||||
IPage<WechatPayConfigEntity> page = this.page(
|
||||
new Query<WechatPayConfigEntity>().getPage(params, "created_at", false),
|
||||
new QueryWrapper<WechatPayConfigEntity>()
|
||||
.eq(StringUtils.isNotBlank(partnerId), "partner_id", partnerId)
|
||||
.eq(StringUtils.isNotBlank(channelOrgId), "channel_org_id", channelOrgId)
|
||||
.eq(StringUtils.isNotBlank(agentOrgId), "agent_org_id", agentOrgId)
|
||||
.eq(StringUtils.isNotBlank(storeOrgId), "store_org_id", storeOrgId)
|
||||
.eq(StringUtils.isNotBlank(status), "status", status)
|
||||
.eq("delete_flag", "UNDEL")
|
||||
wrapper
|
||||
);
|
||||
|
||||
return new PageUtils(page);
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.as.modules.stats.controller;
|
||||
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.stats.service.StatsService;
|
||||
import io.as.modules.sys.controller.AbstractController;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
@@ -20,18 +22,21 @@ import java.util.Map;
|
||||
@Api(tags = "统计信息")
|
||||
@RestController
|
||||
@RequestMapping("/manage/stats")
|
||||
public class StatsController {
|
||||
public class StatsController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private StatsService statsService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 总览看板
|
||||
*/
|
||||
@ApiOperation("总览看板")
|
||||
@GetMapping("/overview")
|
||||
public R overview() {
|
||||
Map<String, Object> data = statsService.getOverview();
|
||||
Map<String, Object> data = statsService.getOverview(getUserId());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("overview", data);
|
||||
return R.dataSuccess(result);
|
||||
@@ -47,7 +52,7 @@ public class StatsController {
|
||||
@RequestParam String startDate,
|
||||
@ApiParam(name = "endDate", value = "结束日期,格式 yyyy-MM-dd", required = true)
|
||||
@RequestParam String endDate) {
|
||||
Map<String, Object> data = statsService.getOrderStats(startDate, endDate);
|
||||
Map<String, Object> data = statsService.getOrderStats(startDate, endDate, getUserId());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("orderStats", data);
|
||||
return R.dataSuccess(result);
|
||||
@@ -59,7 +64,7 @@ public class StatsController {
|
||||
@ApiOperation("库存统计")
|
||||
@GetMapping("/pool")
|
||||
public R pool() {
|
||||
Map<String, Object> data = statsService.getPoolStats();
|
||||
Map<String, Object> data = statsService.getPoolStats(getUserId());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("poolStats", data);
|
||||
return R.dataSuccess(result);
|
||||
@@ -71,7 +76,7 @@ public class StatsController {
|
||||
@ApiOperation("渠道统计")
|
||||
@GetMapping("/channels")
|
||||
public R channels() {
|
||||
Map<String, Object> data = statsService.getChannelStats();
|
||||
Map<String, Object> data = statsService.getChannelStats(getUserId());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("channelStats", data);
|
||||
return R.dataSuccess(result);
|
||||
|
||||
@@ -11,31 +11,35 @@ public interface StatsService {
|
||||
* 总览看板
|
||||
* 返回:总订单数、今日订单数、总收入、今日收入、库存总量、可用库存、已使用卡数、激活失败卡数
|
||||
*
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getOverview();
|
||||
Map<String, Object> getOverview(Long operatorId);
|
||||
|
||||
/**
|
||||
* 订单趋势统计(按日期分组的订单数和收入)
|
||||
*
|
||||
* @param startDate 开始日期,格式 yyyy-MM-dd
|
||||
* @param endDate 结束日期,格式 yyyy-MM-dd
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
* @return 按日期分组的订单趋势数据
|
||||
*/
|
||||
Map<String, Object> getOrderStats(String startDate, String endDate);
|
||||
Map<String, Object> getOrderStats(String startDate, String endDate, Long operatorId);
|
||||
|
||||
/**
|
||||
* 库存统计
|
||||
* 返回:按状态分组(UNUSED/RESERVED/USED)数量,按激活状态分组(PENDING/SUCCESS/FAILED)数量
|
||||
*
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
* @return 库存统计数据
|
||||
*/
|
||||
Map<String, Object> getPoolStats();
|
||||
Map<String, Object> getPoolStats(Long operatorId);
|
||||
|
||||
/**
|
||||
* 渠道统计(按 channel_org_id 分组的订单数和收入)
|
||||
*
|
||||
* @param operatorId 当前操作人ID(用于组织数据隔离)
|
||||
* @return 渠道统计数据
|
||||
*/
|
||||
Map<String, Object> getChannelStats();
|
||||
Map<String, Object> getChannelStats(Long operatorId);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.as.modules.stats.service.impl;
|
||||
|
||||
import io.as.modules.stats.service.StatsService;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -19,85 +20,183 @@ public class StatsServiceImpl implements StatsService {
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/** 组织数据隔离 SQL 片段:store_org_id 在操作人组织子树内 */
|
||||
private static final String ORG_SCOPE_FILTER =
|
||||
" AND store_org_id IN (SELECT id FROM sys_org WHERE delete_flag='UNDEL' AND mpath LIKE CONCAT(?, '%'))";
|
||||
|
||||
/** 组织数据隔离 SQL 片段(pool 表无 store_org_id,通过 order_no 关联 order 表) */
|
||||
private static final String POOL_ORG_SCOPE_FILTER =
|
||||
" AND (order_no IS NULL OR order_no IN (SELECT order_no FROM day_card_order WHERE delete_flag='UNDEL'" +
|
||||
" AND store_org_id IN (SELECT id FROM sys_org WHERE delete_flag='UNDEL' AND mpath LIKE CONCAT(?, '%'))))";
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getOverview() {
|
||||
public Map<String, Object> getOverview(Long operatorId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
String orgFilter = mpath != null ? ORG_SCOPE_FILTER : "";
|
||||
String poolFilter = mpath != null ? POOL_ORG_SCOPE_FILTER : "";
|
||||
|
||||
// 总订单数
|
||||
Integer totalOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL'",
|
||||
Integer.class);
|
||||
Integer totalOrders;
|
||||
if (mpath != null) {
|
||||
totalOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL'" + orgFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
totalOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL'",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("totalOrders", totalOrders != null ? totalOrders : 0);
|
||||
|
||||
// 今日订单数
|
||||
Integer todayOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL' AND DATE(created_at)=CURDATE()",
|
||||
Integer.class);
|
||||
Integer todayOrders;
|
||||
if (mpath != null) {
|
||||
todayOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL' AND DATE(created_at)=CURDATE()" + orgFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
todayOrders = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_order WHERE delete_flag='UNDEL' AND DATE(created_at)=CURDATE()",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("todayOrders", todayOrders != null ? todayOrders : 0);
|
||||
|
||||
// 总收入
|
||||
BigDecimal totalRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID'",
|
||||
BigDecimal.class);
|
||||
BigDecimal totalRevenue;
|
||||
if (mpath != null) {
|
||||
totalRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID'" + orgFilter,
|
||||
BigDecimal.class, mpath);
|
||||
} else {
|
||||
totalRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID'",
|
||||
BigDecimal.class);
|
||||
}
|
||||
result.put("totalRevenue", totalRevenue != null ? totalRevenue : BigDecimal.ZERO);
|
||||
|
||||
// 今日收入
|
||||
BigDecimal todayRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID' AND DATE(pay_time)=CURDATE()",
|
||||
BigDecimal.class);
|
||||
BigDecimal todayRevenue;
|
||||
if (mpath != null) {
|
||||
todayRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID' AND DATE(pay_time)=CURDATE()" + orgFilter,
|
||||
BigDecimal.class, mpath);
|
||||
} else {
|
||||
todayRevenue = jdbcTemplate.queryForObject(
|
||||
"SELECT COALESCE(SUM(pay_amount),0) FROM day_card_order WHERE delete_flag='UNDEL' AND status='PAID' AND DATE(pay_time)=CURDATE()",
|
||||
BigDecimal.class);
|
||||
}
|
||||
result.put("todayRevenue", todayRevenue != null ? todayRevenue : BigDecimal.ZERO);
|
||||
|
||||
// 库存总量
|
||||
Integer totalPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL'",
|
||||
Integer.class);
|
||||
Integer totalPool;
|
||||
if (mpath != null) {
|
||||
totalPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL'" + poolFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
totalPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL'",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("totalPool", totalPool != null ? totalPool : 0);
|
||||
|
||||
// 可用库存
|
||||
Integer availablePool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='UNUSED'",
|
||||
Integer.class);
|
||||
Integer availablePool;
|
||||
if (mpath != null) {
|
||||
availablePool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='UNUSED'" + poolFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
availablePool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='UNUSED'",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("availablePool", availablePool != null ? availablePool : 0);
|
||||
|
||||
// 已使用卡数
|
||||
Integer usedPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='USED'",
|
||||
Integer.class);
|
||||
Integer usedPool;
|
||||
if (mpath != null) {
|
||||
usedPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='USED'" + poolFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
usedPool = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND status='USED'",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("usedPool", usedPool != null ? usedPool : 0);
|
||||
|
||||
// 激活失败卡数
|
||||
Integer activateFailed = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND activate_status='FAILED'",
|
||||
Integer.class);
|
||||
Integer activateFailed;
|
||||
if (mpath != null) {
|
||||
activateFailed = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND activate_status='FAILED'" + poolFilter,
|
||||
Integer.class, mpath);
|
||||
} else {
|
||||
activateFailed = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(1) FROM day_card_pool WHERE delete_flag='UNDEL' AND activate_status='FAILED'",
|
||||
Integer.class);
|
||||
}
|
||||
result.put("activateFailed", activateFailed != null ? activateFailed : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getOrderStats(String startDate, String endDate) {
|
||||
public Map<String, Object> getOrderStats(String startDate, String endDate, Long operatorId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
||||
"SELECT DATE(created_at) as date, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(CASE WHEN status='PAID' THEN pay_amount ELSE 0 END),0) as revenue " +
|
||||
"FROM day_card_order WHERE delete_flag='UNDEL' " +
|
||||
"AND created_at BETWEEN ? AND ? " +
|
||||
"GROUP BY DATE(created_at) ORDER BY date",
|
||||
startDate + " 00:00:00", endDate + " 23:59:59");
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
String orgFilter = mpath != null ? ORG_SCOPE_FILTER : "";
|
||||
|
||||
List<Map<String, Object>> list;
|
||||
if (mpath != null) {
|
||||
list = jdbcTemplate.queryForList(
|
||||
"SELECT DATE(created_at) as date, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(CASE WHEN status='PAID' THEN pay_amount ELSE 0 END),0) as revenue " +
|
||||
"FROM day_card_order WHERE delete_flag='UNDEL' " +
|
||||
"AND created_at BETWEEN ? AND ? " +
|
||||
orgFilter +
|
||||
" GROUP BY DATE(created_at) ORDER BY date",
|
||||
startDate + " 00:00:00", endDate + " 23:59:59", mpath);
|
||||
} else {
|
||||
list = jdbcTemplate.queryForList(
|
||||
"SELECT DATE(created_at) as date, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(CASE WHEN status='PAID' THEN pay_amount ELSE 0 END),0) as revenue " +
|
||||
"FROM day_card_order WHERE delete_flag='UNDEL' " +
|
||||
"AND created_at BETWEEN ? AND ? " +
|
||||
"GROUP BY DATE(created_at) ORDER BY date",
|
||||
startDate + " 00:00:00", endDate + " 23:59:59");
|
||||
}
|
||||
|
||||
result.put("list", list);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPoolStats() {
|
||||
public Map<String, Object> getPoolStats(Long operatorId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
String poolFilter = mpath != null ? POOL_ORG_SCOPE_FILTER : "";
|
||||
|
||||
// 按状态分组
|
||||
List<Map<String, Object>> statusStats = jdbcTemplate.queryForList(
|
||||
"SELECT status, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL' GROUP BY status");
|
||||
List<Map<String, Object>> statusStats;
|
||||
if (mpath != null) {
|
||||
statusStats = jdbcTemplate.queryForList(
|
||||
"SELECT status, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL'" + poolFilter + " GROUP BY status",
|
||||
mpath);
|
||||
} else {
|
||||
statusStats = jdbcTemplate.queryForList(
|
||||
"SELECT status, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL' GROUP BY status");
|
||||
}
|
||||
Map<String, Object> statusMap = new HashMap<>();
|
||||
for (Map<String, Object> row : statusStats) {
|
||||
statusMap.put(String.valueOf(row.get("status")), row.get("count"));
|
||||
@@ -105,9 +204,17 @@ public class StatsServiceImpl implements StatsService {
|
||||
result.put("statusStats", statusMap);
|
||||
|
||||
// 按激活状态分组
|
||||
List<Map<String, Object>> activateStats = jdbcTemplate.queryForList(
|
||||
"SELECT activate_status as activateStatus, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL' GROUP BY activate_status");
|
||||
List<Map<String, Object>> activateStats;
|
||||
if (mpath != null) {
|
||||
activateStats = jdbcTemplate.queryForList(
|
||||
"SELECT activate_status as activateStatus, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL'" + poolFilter + " GROUP BY activate_status",
|
||||
mpath);
|
||||
} else {
|
||||
activateStats = jdbcTemplate.queryForList(
|
||||
"SELECT activate_status as activateStatus, COUNT(1) as count FROM day_card_pool " +
|
||||
"WHERE delete_flag='UNDEL' GROUP BY activate_status");
|
||||
}
|
||||
Map<String, Object> activateMap = new HashMap<>();
|
||||
for (Map<String, Object> row : activateStats) {
|
||||
activateMap.put(String.valueOf(row.get("activateStatus")), row.get("count"));
|
||||
@@ -118,14 +225,27 @@ public class StatsServiceImpl implements StatsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getChannelStats() {
|
||||
public Map<String, Object> getChannelStats(Long operatorId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
||||
"SELECT COALESCE(o.channel_org_id, 0) as channelOrgId, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(o.pay_amount),0) as revenue " +
|
||||
"FROM day_card_order o WHERE o.delete_flag='UNDEL' " +
|
||||
"GROUP BY o.channel_org_id");
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
String orgFilter = mpath != null ? ORG_SCOPE_FILTER : "";
|
||||
|
||||
List<Map<String, Object>> list;
|
||||
if (mpath != null) {
|
||||
list = jdbcTemplate.queryForList(
|
||||
"SELECT COALESCE(o.channel_org_id, 0) as channelOrgId, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(o.pay_amount),0) as revenue " +
|
||||
"FROM day_card_order o WHERE o.delete_flag='UNDEL'" + orgFilter +
|
||||
" GROUP BY o.channel_org_id",
|
||||
mpath);
|
||||
} else {
|
||||
list = jdbcTemplate.queryForList(
|
||||
"SELECT COALESCE(o.channel_org_id, 0) as channelOrgId, COUNT(1) as orderCount, " +
|
||||
"COALESCE(SUM(o.pay_amount),0) as revenue " +
|
||||
"FROM day_card_order o WHERE o.delete_flag='UNDEL' " +
|
||||
"GROUP BY o.channel_org_id");
|
||||
}
|
||||
|
||||
result.put("list", list);
|
||||
return result;
|
||||
|
||||
@@ -77,7 +77,9 @@ public class SysMenuController extends AbstractController {
|
||||
public R save(@RequestBody SysMenuEntity menu) {
|
||||
verifyForm(menu);
|
||||
sysMenuService.save(menu);
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", menu.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
@SysLog("修改菜单")
|
||||
@@ -85,7 +87,7 @@ public class SysMenuController extends AbstractController {
|
||||
public R update(@RequestBody SysMenuEntity menu) {
|
||||
verifyForm(menu);
|
||||
sysMenuService.updateById(menu);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
@SysLog("删除菜单")
|
||||
@@ -99,7 +101,7 @@ public class SysMenuController extends AbstractController {
|
||||
return R.error("请先删除子菜单或按钮");
|
||||
}
|
||||
sysMenuService.delete(menuId);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
private void verifyForm(SysMenuEntity menu) {
|
||||
|
||||
@@ -257,7 +257,7 @@ public class SysOrgController extends AbstractController {
|
||||
public R update(@RequestBody SysOrgEntity org) {
|
||||
ValidatorUtils.validateEntity(org);
|
||||
sysOrgService.updateOrg(org, getUserId());
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +268,6 @@ public class SysOrgController extends AbstractController {
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
sysOrgService.deleteBatch(ids, getUserId());
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ public class SysRoleController extends AbstractController {
|
||||
public R save(@RequestBody SysRoleEntity role) {
|
||||
ValidatorUtils.validateEntity(role);
|
||||
sysRoleService.saveRole(role, getUserId());
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", role.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
@SysLog("修改角色")
|
||||
@@ -64,7 +66,7 @@ public class SysRoleController extends AbstractController {
|
||||
public R update(@RequestBody SysRoleEntity role) {
|
||||
ValidatorUtils.validateEntity(role);
|
||||
sysRoleService.update(role, getUserId());
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
@SysLog("删除角色")
|
||||
@@ -72,6 +74,6 @@ public class SysRoleController extends AbstractController {
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] roleIds) {
|
||||
sysRoleService.deleteBatch(roleIds);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.as.modules.sys.controller;
|
||||
|
||||
import io.as.common.annotation.AuditAction;
|
||||
import io.as.common.annotation.SysLog;
|
||||
import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.Constant;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
@@ -13,6 +14,7 @@ import io.as.modules.sys.constants.OrgTypeRoleConstants;
|
||||
import io.as.modules.sys.entity.SysRoleEntity;
|
||||
import io.as.modules.sys.entity.SysUserEntity;
|
||||
import io.as.modules.sys.form.PasswordForm;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.as.modules.sys.service.SysRoleService;
|
||||
import io.as.modules.sys.service.SysUserRoleService;
|
||||
import io.as.modules.sys.service.SysUserService;
|
||||
@@ -34,12 +36,14 @@ public class SysUserController extends AbstractController {
|
||||
private SysUserRoleService sysUserRoleService;
|
||||
@Autowired
|
||||
private SysRoleService sysRoleService;
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
private static final String ROLE_NAME_PLATFORM = "PLATFORM";
|
||||
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = sysUserService.queryPage(params);
|
||||
PageUtils page = sysUserService.queryPage(params, getUserId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
@@ -51,7 +55,7 @@ public class SysUserController extends AbstractController {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("user", u);
|
||||
data.put("roleName", queryRoleNameByUserId(getUserId()));
|
||||
return R.ok().put("data", data);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
private String queryRoleNameByUserId(long userId) {
|
||||
@@ -83,12 +87,16 @@ public class SysUserController extends AbstractController {
|
||||
if (!flag) {
|
||||
return R.error("原密码不正确");
|
||||
}
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
@GetMapping("/info/{userId}")
|
||||
public R info(@PathVariable("userId") Long userId) {
|
||||
SysUserEntity user = sysUserService.getById(userId);
|
||||
if (user == null) {
|
||||
throw new ASException("用户不存在");
|
||||
}
|
||||
checkUserOrgScope(user);
|
||||
List<Long> roleIdList = sysUserRoleService.queryRoleIdList(userId);
|
||||
user.setRoleIdList(roleIdList);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
@@ -102,7 +110,9 @@ public class SysUserController extends AbstractController {
|
||||
public R save(@RequestBody SysUserEntity user) {
|
||||
ValidatorUtils.validateEntity(user, AddGroup.class);
|
||||
sysUserService.saveUser(user, getUserId());
|
||||
return R.ok();
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", user.getId());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
@SysLog("修改用户")
|
||||
@@ -111,7 +121,7 @@ public class SysUserController extends AbstractController {
|
||||
public R update(@RequestBody SysUserEntity user) {
|
||||
ValidatorUtils.validateEntity(user, UpdateGroup.class);
|
||||
sysUserService.update(user, getUserId());
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
@SysLog("删除用户")
|
||||
@@ -124,7 +134,25 @@ public class SysUserController extends AbstractController {
|
||||
if (ArrayUtils.contains(userIds, getUserId())) {
|
||||
return R.error("当前用户不能删除");
|
||||
}
|
||||
for (Long userId : userIds) {
|
||||
SysUserEntity user = sysUserService.getById(userId);
|
||||
if (user != null) {
|
||||
checkUserOrgScope(user);
|
||||
}
|
||||
}
|
||||
sysUserService.deleteBatch(userIds);
|
||||
return R.ok();
|
||||
return R.dataSuccess(new HashMap<>());
|
||||
}
|
||||
|
||||
private void checkUserOrgScope(SysUserEntity user) {
|
||||
if (user.getOrgId() != null) {
|
||||
if (!sysOrgService.isOrgInScope(user.getOrgId(), getUserId())) {
|
||||
throw new ASException("无权限操作该组织的数据");
|
||||
}
|
||||
} else {
|
||||
if (sysOrgService.getUserOrgMpath(getUserId()) != null) {
|
||||
throw new ASException("无权限操作平台用户");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.as.modules.sys.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
@@ -37,6 +38,21 @@ public interface SysOrgService extends IService<SysOrgEntity> {
|
||||
*/
|
||||
boolean isOrgInScope(Long orgId, Long operatorId);
|
||||
|
||||
/**
|
||||
* 获取操作人的组织mpath(用于业务数据隔离)
|
||||
* @return 超管返回null(不过滤),否则返回mpath字符串
|
||||
*/
|
||||
String getUserOrgMpath(Long operatorId);
|
||||
|
||||
/**
|
||||
* 向业务表QueryWrapper注入组织范围过滤条件
|
||||
* <p>超管不过滤;非超管按 orgColumn 字段关联sys_org的mpath前缀匹配</p>
|
||||
* @param wrapper 业务表QueryWrapper
|
||||
* @param orgColumn 业务表中的组织字段名(如 store_org_id、channel_org_id)
|
||||
* @param operatorId 当前操作人userId
|
||||
*/
|
||||
void applyBusinessOrgScope(QueryWrapper<?> wrapper, String orgColumn, Long operatorId);
|
||||
|
||||
/**
|
||||
* 指定组织及其子树范围内、未删除的全部门店组织 ID(type=STORE)
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SysUserService extends IService<SysUserEntity> {
|
||||
PageUtils queryPage(Map<String, Object> params);
|
||||
PageUtils queryPage(Map<String, Object> params, Long operatorId);
|
||||
List<String> queryAllPerms(Long userId);
|
||||
List<Long> queryAllMenuId(Long userId);
|
||||
SysUserEntity queryByUserName(String username);
|
||||
|
||||
@@ -965,6 +965,33 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgDao, SysOrgEntity> impl
|
||||
return target.getMpath().startsWith(userOrg.getMpath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserOrgMpath(Long operatorId) {
|
||||
if (operatorId == null) {
|
||||
return null;
|
||||
}
|
||||
SysUserEntity user = sysUserService.getById(operatorId);
|
||||
if (user == null || Constant.isSuperAdmin(user.getOrgId())) {
|
||||
return null;
|
||||
}
|
||||
SysOrgEntity org = this.getById(user.getOrgId());
|
||||
if (org == null || StringUtils.isBlank(org.getMpath())) {
|
||||
return null;
|
||||
}
|
||||
return org.getMpath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBusinessOrgScope(QueryWrapper<?> wrapper, String orgColumn, Long operatorId) {
|
||||
String mpath = getUserOrgMpath(operatorId);
|
||||
if (mpath == null) {
|
||||
// 超管,不过滤
|
||||
return;
|
||||
}
|
||||
// 非超管:orgColumn 字段值对应的组织 mpath 以当前用户 mpath 为前缀
|
||||
wrapper.apply(orgColumn + " IN (SELECT id FROM sys_org WHERE delete_flag = 'UNDEL' AND mpath LIKE CONCAT({0}, '%'))", mpath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listDescendantStoreOrgIds(Long orgId) {
|
||||
if (orgId == null) {
|
||||
|
||||
@@ -8,11 +8,13 @@ import io.as.common.utils.Constant;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.Query;
|
||||
import io.as.modules.sys.constants.OrgTypeRoleConstants;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import io.as.modules.sys.dao.SysOrgDao;
|
||||
import io.as.modules.sys.dao.SysUserDao;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
import io.as.modules.sys.entity.SysRoleEntity;
|
||||
import io.as.modules.sys.entity.SysUserEntity;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.as.modules.sys.service.SysRoleService;
|
||||
import io.as.modules.sys.vo.SysUserListVo;
|
||||
import io.as.modules.sys.service.SysUserRoleService;
|
||||
@@ -35,14 +37,26 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> i
|
||||
private SysRoleService sysRoleService;
|
||||
@Autowired
|
||||
private SysOrgDao sysOrgDao;
|
||||
@Autowired
|
||||
@Lazy
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
public PageUtils queryPage(Map<String, Object> params, Long operatorId) {
|
||||
String username = (String) params.get("username");
|
||||
QueryWrapper<SysUserEntity> wrapper = new QueryWrapper<SysUserEntity>()
|
||||
.like(StringUtils.isNotBlank(username), "username", username);
|
||||
|
||||
// 组织数据隔离:非超管仅可见自身组织范围内的用户,且不可见超管用户(org_id IS NULL)
|
||||
String mpath = sysOrgService.getUserOrgMpath(operatorId);
|
||||
if (mpath != null) {
|
||||
wrapper.isNotNull("org_id");
|
||||
sysOrgService.applyBusinessOrgScope(wrapper, "org_id", operatorId);
|
||||
}
|
||||
|
||||
IPage<SysUserEntity> page = this.page(
|
||||
new Query<SysUserEntity>().getPage(params),
|
||||
new QueryWrapper<SysUserEntity>()
|
||||
.like(StringUtils.isNotBlank(username), "username", username)
|
||||
wrapper
|
||||
);
|
||||
List<SysUserEntity> list = page.getRecords();
|
||||
List<SysUserListVo> voList = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user