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:
zhaogd
2026-08-11 15:50:36 +08:00
parent 8385196b38
commit 03e5f6fbf2
34 changed files with 869 additions and 257 deletions

View File

@@ -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<>());
}
// ==================== 内部方法 ====================

View File

@@ -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);
}
// ===================== 内部方法 =====================

View File

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

View File

@@ -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("无权限操作平台默认配置");
}
}
}
}

View File

@@ -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<>();

View File

@@ -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);
}
}

View File

@@ -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<>());
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -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 层维度,优先级从高到低)

View File

@@ -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);
/**
* 根据退款单号查询退款记录

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -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<>());
}
}

View File

@@ -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获取设备详情

View File

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

View File

@@ -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<>());
}
// ==================== 内容选择辅助接口 ====================

View File

@@ -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<>());
}
}

View File

@@ -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("无权限操作全局默认配置");
}
}
}
}

View File

@@ -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获取支付配置详情

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

@@ -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<>());
}
}

View File

@@ -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<>());
}
}

View File

@@ -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("无权限操作平台用户");
}
}
}
}

View File

@@ -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)
*/

View File

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

View File

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

View File

@@ -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<>();