初始化天卡开放平台后端项目
包含完整功能模块: - 设备激活(直销激活码模式 + 代销免激活码模式) - 微信支付 + 天卡订单 - 天卡池管理 + 异步橘视厅SDK激活 - 第三方合作伙伴接口(代销模式) - 签名鉴权(appKey/appSecret + nonce防重放) - 天卡消耗(事务 + 幂等 + 并发控制) - 媒资接口(三级授权过滤) - 审计日志 - 定价策略(3层:门店 > 渠道 > 平台) - 分账(微信分账 + 分润规则) - Docker部署(含Redis容器)
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
package io.as.modules.daycard.controller;
|
||||
|
||||
import io.as.common.annotation.AuditAction;
|
||||
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.entity.SysOrgEntity;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡设备激活码管理(管理后台用)
|
||||
* <p>管理后台按门店生成激活码,App 端 init 时输入激活码完成设备注册绑定</p>
|
||||
*/
|
||||
@Api(tags = "天卡设备激活码")
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard/activation-code")
|
||||
public class DayCardActivationCodeController {
|
||||
|
||||
@Autowired
|
||||
private DayCardActivationCodeService dayCardActivationCodeService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 按门店查询激活码列表
|
||||
*/
|
||||
@ApiOperation("按门店查询激活码列表")
|
||||
@GetMapping("/list/{storeOrgId}")
|
||||
public R listByStore(@PathVariable("storeOrgId") Long storeOrgId) {
|
||||
List<DayCardActivationCodeEntity> list = dayCardActivationCodeService.listByStoreOrgId(storeOrgId);
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (DayCardActivationCodeEntity entity : list) {
|
||||
result.add(toMap(entity));
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("list", result);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为门店生成激活码(每门店一个,已存在则返回已有的)
|
||||
*/
|
||||
@ApiOperation("为门店生成激活码")
|
||||
@AuditAction(value = "ACTCODE_GENERATE", description = "生成门店激活码")
|
||||
@PostMapping("/generate")
|
||||
public R generate(@RequestBody GenerateRequest request) {
|
||||
if (request.getStoreOrgId() == null) {
|
||||
throw new ASException("门店组织ID不能为空");
|
||||
}
|
||||
// 校验门店存在
|
||||
SysOrgEntity storeOrg = sysOrgService.getById(request.getStoreOrgId());
|
||||
if (storeOrg == null) {
|
||||
throw new ASException("门店不存在");
|
||||
}
|
||||
// 从门店 mpath 解析渠道
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成激活码 code(旧 code 失效)
|
||||
*/
|
||||
@ApiOperation("重新生成激活码")
|
||||
@AuditAction(value = "ACTCODE_REGENERATE", description = "重新生成激活码")
|
||||
@PostMapping("/{id}/regenerate")
|
||||
public R regenerate(@PathVariable("id") Long id) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用激活码
|
||||
*/
|
||||
@ApiOperation("启用/禁用激活码")
|
||||
@AuditAction(value = "ACTCODE_UPDATE_STATUS", description = "修改激活码状态")
|
||||
@PutMapping("/{id}/status")
|
||||
public R updateStatus(@PathVariable("id") Long id, @RequestBody UpdateStatusRequest request) {
|
||||
dayCardActivationCodeService.updateStatus(id, request.getStatus());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除激活码(软删除)
|
||||
*/
|
||||
@ApiOperation("删除激活码")
|
||||
@AuditAction(value = "ACTCODE_DELETE", description = "删除激活码")
|
||||
@DeleteMapping("/{id}")
|
||||
public R delete(@PathVariable("id") Long id) {
|
||||
dayCardActivationCodeService.delete(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 内部方法 ====================
|
||||
|
||||
/**
|
||||
* 从门店 mpath 解析渠道组织ID(mpath 第2段)
|
||||
*/
|
||||
private Long resolveChannelOrgId(SysOrgEntity storeOrg) {
|
||||
if (storeOrg == null || StringUtils.isBlank(storeOrg.getMpath())) {
|
||||
return null;
|
||||
}
|
||||
String[] segments = storeOrg.getMpath().split("\\.");
|
||||
if (segments.length >= 2) {
|
||||
try {
|
||||
return Long.parseLong(segments[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Object> toMap(DayCardActivationCodeEntity entity) {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("id", entity.getId());
|
||||
m.put("code", entity.getCode());
|
||||
m.put("channelOrgId", entity.getChannelOrgId());
|
||||
m.put("storeOrgId", entity.getStoreOrgId());
|
||||
m.put("status", entity.getStatus());
|
||||
m.put("maxDeviceCount", entity.getMaxDeviceCount());
|
||||
m.put("activatedCount", entity.getActivatedCount());
|
||||
m.put("remark", entity.getRemark());
|
||||
m.put("createdAt", entity.getCreatedAt());
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成激活码请求
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class GenerateRequest {
|
||||
private Long storeOrgId;
|
||||
private String remark;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态请求
|
||||
*/
|
||||
@lombok.Data
|
||||
public static class UpdateStatusRequest {
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package io.as.modules.daycard.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.as.common.annotation.AuditAction;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.constants.DayCardConstants;
|
||||
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 org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡管理后台接口(JWT 鉴权)
|
||||
* <p>供 Web 管理后台调用,与 /openapi/** 合作伙伴接口隔离</p>
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard")
|
||||
@RequiresAuthentication
|
||||
public class DayCardManageController {
|
||||
|
||||
@Autowired
|
||||
private DayCardPoolService dayCardPoolService;
|
||||
|
||||
@Autowired
|
||||
private DayCardOrderService dayCardOrderService;
|
||||
|
||||
// ===================== 天卡池 =====================
|
||||
|
||||
/**
|
||||
* 天卡池列表
|
||||
*/
|
||||
@GetMapping("/pool/list")
|
||||
public R poolList(@RequestParam Map<String, Object> params) {
|
||||
Long partnerId = parsePartnerId(params);
|
||||
PageUtils page = dayCardPoolService.queryPage(params, partnerId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 天卡池统计
|
||||
*/
|
||||
@GetMapping("/pool/stats")
|
||||
public R poolStats(@RequestParam(required = false) Long partnerId) {
|
||||
DayCardPoolStatsVo vo = buildStats(partnerId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("stats", vo);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入天卡卡密
|
||||
*/
|
||||
@AuditAction(value = "DAYCARD_POOL_IMPORT", description = "导入天卡卡密")
|
||||
@PostMapping("/pool/import")
|
||||
public R poolImport(@RequestBody Map<String, Object> body) {
|
||||
Long partnerId = null;
|
||||
Object pid = body.get("partnerId");
|
||||
if (pid != null) {
|
||||
partnerId = Long.parseLong(String.valueOf(pid));
|
||||
}
|
||||
Object codesObj = body.get("cardCodes");
|
||||
if (codesObj == null) {
|
||||
return R.error("cardCodes 不能为空");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> cardCodes = (List<String>) codesObj;
|
||||
if (cardCodes.isEmpty()) {
|
||||
return R.error("cardCodes 不能为空");
|
||||
}
|
||||
if (partnerId == null) {
|
||||
return R.error("partnerId 不能为空");
|
||||
}
|
||||
int success = dayCardPoolService.importCards(cardCodes, partnerId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("successCount", success);
|
||||
data.put("totalCount", cardCodes.size());
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
// ===================== 天卡订单 =====================
|
||||
|
||||
/**
|
||||
* 天卡订单列表
|
||||
*/
|
||||
@GetMapping("/order/list")
|
||||
public R orderList(@RequestParam Map<String, Object> params) {
|
||||
Long partnerId = parsePartnerId(params);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, partnerId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
// ===================== 设备权益 =====================
|
||||
|
||||
/**
|
||||
* 查询设备天卡权益
|
||||
*/
|
||||
@GetMapping("/benefits/{deviceId}")
|
||||
public R deviceBenefits(@PathVariable Long deviceId) {
|
||||
DayCardPoolEntity pool = dayCardOrderService.getDeviceBenefits(deviceId);
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
if (pool != null) {
|
||||
item.put("hasBenefits", true);
|
||||
item.put("thirdCardNo", pool.getThirdCardNo());
|
||||
item.put("cardCode", pool.getCardCode());
|
||||
item.put("expireAt", pool.getExpireAt());
|
||||
} else {
|
||||
item.put("hasBenefits", false);
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("data", item);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑设备天卡权益(Web端手动操作)
|
||||
* <p>将设备绑定的 USED 卡转为 REUSABLE,释放设备。橘视厅权益保留,后续新设备下单可复用此卡。</p>
|
||||
*/
|
||||
@AuditAction(value = "DAYCARD_DEVICE_UNBIND", description = "解绑设备天卡权益")
|
||||
@PostMapping("/device/unbind")
|
||||
public R unbindDevice(@RequestParam Long deviceId) {
|
||||
dayCardOrderService.unbindDevice(deviceId);
|
||||
return R.ok("设备解绑成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备组织解绑(更换安装场所时调用)
|
||||
* <p>释放设备当前天卡权益并清除门店组织关联(storeOrgId),设备可在新场所用新激活码重新激活绑定。</p>
|
||||
*/
|
||||
@AuditAction(value = "DAYCARD_DEVICE_UNBIND_ORG", description = "设备组织解绑")
|
||||
@PostMapping("/device/unbind-org")
|
||||
public R unbindDeviceOrg(@RequestParam Long deviceId) {
|
||||
dayCardOrderService.unbindDeviceOrg(deviceId);
|
||||
return R.ok("设备组织解绑成功");
|
||||
}
|
||||
|
||||
// ===================== 内部方法 =====================
|
||||
|
||||
private Long parsePartnerId(Map<String, Object> params) {
|
||||
Object val = params.get("partnerId");
|
||||
if (val == null) {
|
||||
val = params.get("partner_id");
|
||||
}
|
||||
if (val == null || "".equals(val)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(String.valueOf(val));
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DayCardPoolStatsVo buildStats(Long partnerId) {
|
||||
DayCardPoolStatsVo vo = new DayCardPoolStatsVo();
|
||||
|
||||
int available = dayCardPoolService.getAvailableCount(partnerId);
|
||||
vo.setAvailable(available);
|
||||
|
||||
QueryWrapper<DayCardPoolEntity> reservedWrapper = new QueryWrapper<>();
|
||||
reservedWrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
||||
.eq("status", DayCardConstants.POOL_STATUS_RESERVED);
|
||||
if (partnerId != null) {
|
||||
reservedWrapper.eq("partner_id", partnerId);
|
||||
}
|
||||
int reserved = dayCardPoolService.count(reservedWrapper);
|
||||
vo.setReserved(reserved);
|
||||
|
||||
QueryWrapper<DayCardPoolEntity> usedWrapper = new QueryWrapper<>();
|
||||
usedWrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL)
|
||||
.eq("status", DayCardConstants.POOL_STATUS_USED);
|
||||
if (partnerId != null) {
|
||||
usedWrapper.eq("partner_id", partnerId);
|
||||
}
|
||||
int used = dayCardPoolService.count(usedWrapper);
|
||||
vo.setUsed(used);
|
||||
|
||||
vo.setTotal(available + reserved + used);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package io.as.modules.daycard.controller;
|
||||
|
||||
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.service.DayCardOrderService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡订单 Controller(管理后台用)
|
||||
* <p>App 端接口已迁移至 /api/jst/** (ApiJstController),本 Controller 仅保留管理后台用的分页查询和详情接口</p>
|
||||
*/
|
||||
@Api(tags = "天卡订单")
|
||||
@RestController
|
||||
@RequestMapping("/manage/daycard")
|
||||
public class DayCardOrderController {
|
||||
|
||||
@Autowired
|
||||
private DayCardOrderService dayCardOrderService;
|
||||
|
||||
/**
|
||||
* 分页查询订单
|
||||
*/
|
||||
@ApiOperation("分页查询天卡订单")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
Long partnerId = parsePartnerId(params);
|
||||
PageUtils page = dayCardOrderService.queryPage(params, partnerId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单详情
|
||||
*/
|
||||
@ApiOperation("查询订单详情")
|
||||
@GetMapping("/info/{orderNo}")
|
||||
public R info(@PathVariable("orderNo") String orderNo) {
|
||||
DayCardOrderEntity order = dayCardOrderService.getByOrderNo(orderNo);
|
||||
if (order == null) {
|
||||
throw new ASException("订单不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package io.as.modules.daycard.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.daycard.constants.DayCardConstants;
|
||||
import io.as.modules.daycard.entity.DayCardPriceConfigEntity;
|
||||
import io.as.modules.daycard.service.DayCardPriceConfigService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡定价策略管理 Controller
|
||||
*/
|
||||
@Api(tags = "天卡定价策略管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/price")
|
||||
public class DayCardPriceConfigController {
|
||||
|
||||
@Autowired
|
||||
private DayCardPriceConfigService dayCardPriceConfigService;
|
||||
|
||||
/**
|
||||
* 分页查询定价策略(支持 partnerId/status 筛选)
|
||||
*/
|
||||
@ApiOperation("分页查询定价策略")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardPriceConfigService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询定价策略详情
|
||||
*/
|
||||
@ApiOperation("定价策略详情")
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
DayCardPriceConfigEntity config = dayCardPriceConfigService.getById(id);
|
||||
if (config == null) {
|
||||
throw new ASException("定价策略不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("config", config);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询生效中的定价策略(3 层维度)
|
||||
* <p>优先级:门店配置价 > 渠道配置价 > 平台默认价兜底</p>
|
||||
*
|
||||
* @param channelOrgId 渠道组织ID(可空)
|
||||
* @param storeOrgId 门店组织ID(可空)
|
||||
*/
|
||||
@ApiOperation("查询生效中的定价策略")
|
||||
@GetMapping("/effective")
|
||||
public R effective(@RequestParam(value = "channelOrgId", required = false) Long channelOrgId,
|
||||
@RequestParam(value = "storeOrgId", required = false) Long storeOrgId) {
|
||||
DayCardPriceConfigEntity config = dayCardPriceConfigService.getEffectivePrice(channelOrgId, storeOrgId);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("config", config);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增定价策略
|
||||
*/
|
||||
@ApiOperation("新增定价策略")
|
||||
@AuditAction(value = "PRICE_SAVE", description = "新增定价策略")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody DayCardPriceConfigEntity config) {
|
||||
Date now = new Date();
|
||||
config.setDeleteFlag(DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
config.setCreatedAt(now);
|
||||
config.setUpdatedAt(now);
|
||||
if (config.getStatus() == null) {
|
||||
config.setStatus("ACTIVE");
|
||||
}
|
||||
dayCardPriceConfigService.save(config);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改定价策略
|
||||
*/
|
||||
@ApiOperation("修改定价策略")
|
||||
@AuditAction(value = "PRICE_UPDATE", description = "修改定价策略")
|
||||
@PostMapping("/update")
|
||||
public R update(@RequestBody DayCardPriceConfigEntity config) {
|
||||
if (config.getId() == null) {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
config.setUpdatedAt(new Date());
|
||||
dayCardPriceConfigService.updateById(config);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 软删除定价策略
|
||||
*/
|
||||
@ApiOperation("删除定价策略")
|
||||
@AuditAction(value = "PRICE_DELETE", description = "删除定价策略")
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
if (ids == null || ids.length == 0) {
|
||||
throw new ASException("ids不能为空");
|
||||
}
|
||||
List<DayCardPriceConfigEntity> list = dayCardPriceConfigService.listByIds(
|
||||
Arrays.asList(ids));
|
||||
Date now = new Date();
|
||||
for (DayCardPriceConfigEntity config : list) {
|
||||
config.setDeleteFlag(DayCardConstants.DELETE_FLAG_DEL);
|
||||
config.setUpdatedAt(now);
|
||||
}
|
||||
dayCardPriceConfigService.updateBatchById(list);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package io.as.modules.daycard.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.daycard.entity.DayCardRefundEntity;
|
||||
import io.as.modules.daycard.service.DayCardRefundService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡退款管理 Controller
|
||||
*/
|
||||
@Api(tags = "天卡退款管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/refund")
|
||||
public class DayCardRefundController {
|
||||
|
||||
@Autowired
|
||||
private DayCardRefundService dayCardRefundService;
|
||||
|
||||
/**
|
||||
* 分页查询退款记录
|
||||
*/
|
||||
@ApiOperation("分页查询退款记录")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardRefundService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款详情
|
||||
*/
|
||||
@ApiOperation("退款详情")
|
||||
@GetMapping("/info/{refundNo}")
|
||||
public R info(@PathVariable("refundNo") String refundNo) {
|
||||
DayCardRefundEntity refund = dayCardRefundService.getByRefundNo(refundNo);
|
||||
if (refund == null) {
|
||||
throw new ASException("退款记录不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("refund", refund);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起退款
|
||||
*
|
||||
* @param params 包含 orderNo(订单号)和 reason(退款原因)
|
||||
*/
|
||||
@ApiOperation("发起退款")
|
||||
@AuditAction(value = "REFUND_APPLY", description = "发起退款")
|
||||
@PostMapping("/apply")
|
||||
public R apply(@RequestBody Map<String, String> params) {
|
||||
String orderNo = params.get("orderNo");
|
||||
if (StringUtils.isBlank(orderNo)) {
|
||||
throw new ASException("orderNo不能为空");
|
||||
}
|
||||
String reason = params.get("reason");
|
||||
DayCardRefundEntity refund = dayCardRefundService.refund(orderNo, reason);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("refund", refund);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package io.as.modules.daycard.controller;
|
||||
|
||||
import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.entity.DayCardSettleOrderEntity;
|
||||
import io.as.modules.daycard.entity.DayCardSettleRecordEntity;
|
||||
import io.as.modules.daycard.entity.DayCardSettleRuleEntity;
|
||||
import io.as.modules.daycard.service.DayCardSettleOrderService;
|
||||
import io.as.modules.daycard.service.DayCardSettleRecordService;
|
||||
import io.as.modules.daycard.service.DayCardSettleRuleService;
|
||||
import io.as.modules.daycard.service.ProfitCalcService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 天卡分账管理 Controller
|
||||
*/
|
||||
@Api(tags = "天卡分账管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/settle")
|
||||
public class DayCardSettleController {
|
||||
|
||||
@Autowired
|
||||
private DayCardSettleRuleService dayCardSettleRuleService;
|
||||
|
||||
@Autowired
|
||||
private DayCardSettleRecordService dayCardSettleRecordService;
|
||||
|
||||
@Autowired
|
||||
private DayCardSettleOrderService dayCardSettleOrderService;
|
||||
|
||||
@Autowired
|
||||
private ProfitCalcService profitCalcService;
|
||||
|
||||
// ==================== 分账规则 ====================
|
||||
|
||||
/**
|
||||
* 分页查询分账规则
|
||||
*/
|
||||
@ApiOperation("分页查询分账规则")
|
||||
@GetMapping("/rule/list")
|
||||
public R ruleList(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardSettleRuleService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账规则详情
|
||||
*/
|
||||
@ApiOperation("分账规则详情")
|
||||
@GetMapping("/rule/info/{id}")
|
||||
public R ruleInfo(@PathVariable("id") Long id) {
|
||||
DayCardSettleRuleEntity rule = dayCardSettleRuleService.getById(id);
|
||||
if (rule == null) {
|
||||
throw new ASException("分账规则不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("rule", rule);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增分账规则
|
||||
*/
|
||||
@ApiOperation("新增分账规则")
|
||||
@PostMapping("/rule/save")
|
||||
public R ruleSave(@RequestBody DayCardSettleRuleEntity rule) {
|
||||
if (rule.getName() == null || rule.getName().trim().isEmpty()) {
|
||||
throw new ASException("规则名称不能为空");
|
||||
}
|
||||
if (rule.getShareRatio() == null) {
|
||||
throw new ASException("分账比例不能为空");
|
||||
}
|
||||
dayCardSettleRuleService.saveRule(rule);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("rule", rule);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改分账规则
|
||||
*/
|
||||
@ApiOperation("修改分账规则")
|
||||
@PostMapping("/rule/update")
|
||||
public R ruleUpdate(@RequestBody DayCardSettleRuleEntity rule) {
|
||||
if (rule.getId() == null) {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
dayCardSettleRuleService.update(rule);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分账规则(软删除)
|
||||
*/
|
||||
@ApiOperation("删除分账规则")
|
||||
@PostMapping("/rule/delete")
|
||||
public R ruleDelete(@RequestBody Long[] ids) {
|
||||
dayCardSettleRuleService.delete(ids);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 分账记录 ====================
|
||||
|
||||
/**
|
||||
* 分页查询分账记录
|
||||
*/
|
||||
@ApiOperation("分页查询分账记录")
|
||||
@GetMapping("/record/list")
|
||||
public R recordList(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardSettleRecordService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账记录详情
|
||||
*/
|
||||
@ApiOperation("分账记录详情")
|
||||
@GetMapping("/record/info/{id}")
|
||||
public R recordInfo(@PathVariable("id") Long id) {
|
||||
DayCardSettleRecordEntity record = dayCardSettleRecordService.getById(id);
|
||||
if (record == null) {
|
||||
throw new ASException("分账记录不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("record", record);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
// ==================== 分账单 ====================
|
||||
|
||||
/**
|
||||
* 分页查询分账单
|
||||
*/
|
||||
@ApiOperation("分页查询分账单")
|
||||
@GetMapping("/order/list")
|
||||
public R orderList(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = dayCardSettleOrderService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账单详情
|
||||
*/
|
||||
@ApiOperation("分账单详情")
|
||||
@GetMapping("/order/info/{id}")
|
||||
public R orderInfo(@PathVariable("id") Long id) {
|
||||
DayCardSettleOrderEntity settleOrder = dayCardSettleOrderService.getById(id);
|
||||
if (settleOrder == null) {
|
||||
throw new ASException("分账单不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("settleOrder", settleOrder);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
// ==================== 试算 ====================
|
||||
|
||||
/**
|
||||
* 分润试算
|
||||
*/
|
||||
@ApiOperation("分润试算")
|
||||
@GetMapping("/trial")
|
||||
public R trial(@RequestParam("partnerId") Long partnerId,
|
||||
@RequestParam(value = "channelOrgId", required = false) Long channelOrgId,
|
||||
@RequestParam(value = "storeOrgId", required = false) Long storeOrgId,
|
||||
@RequestParam("totalAmount") BigDecimal totalAmount) {
|
||||
if (partnerId == null) {
|
||||
throw new ASException("partnerId不能为空");
|
||||
}
|
||||
if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.as.modules.daycard.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.daycard.entity.OrgSettlementAccountEntity;
|
||||
import io.as.modules.daycard.service.OrgSettlementAccountService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 组织分账接收方 Controller
|
||||
*/
|
||||
@Api(tags = "组织分账接收方管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/profit-receiver")
|
||||
public class OrgSettlementAccountController {
|
||||
|
||||
@Autowired
|
||||
private OrgSettlementAccountService orgSettlementAccountService;
|
||||
|
||||
/**
|
||||
* 分页查询分账接收方
|
||||
*/
|
||||
@ApiOperation("分页查询分账接收方")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = orgSettlementAccountService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账接收方详情
|
||||
*/
|
||||
@ApiOperation("分账接收方详情")
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
OrgSettlementAccountEntity account = orgSettlementAccountService.getById(id);
|
||||
if (account == null) {
|
||||
throw new ASException("分账接收方不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("account", account);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增分账接收方
|
||||
*/
|
||||
@ApiOperation("新增分账接收方")
|
||||
@AuditAction(value = "PROFIT_RECEIVER_SAVE", description = "新增分账接收方")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody OrgSettlementAccountEntity account) {
|
||||
if (account.getOrgId() == null) {
|
||||
throw new ASException("组织ID不能为空");
|
||||
}
|
||||
if (account.getReceiverAccount() == null || account.getReceiverAccount().trim().isEmpty()) {
|
||||
throw new ASException("接收方账号不能为空");
|
||||
}
|
||||
if (account.getReceiverType() == null || account.getReceiverType().trim().isEmpty()) {
|
||||
throw new ASException("接收方类型不能为空");
|
||||
}
|
||||
orgSettlementAccountService.saveAccount(account);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("account", account);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分账接收方(软删除)
|
||||
*/
|
||||
@ApiOperation("删除分账接收方")
|
||||
@AuditAction(value = "PROFIT_RECEIVER_DELETE", description = "删除分账接收方")
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Long[] ids) {
|
||||
orgSettlementAccountService.delete(ids);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步接收方到微信
|
||||
*/
|
||||
@ApiOperation("同步接收方到微信")
|
||||
@AuditAction(value = "PROFIT_RECEIVER_SYNC", description = "同步分账接收方到微信")
|
||||
@PostMapping("/sync/{id}")
|
||||
public R sync(@PathVariable("id") Long id) {
|
||||
orgSettlementAccountService.syncToWechat(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package io.as.modules.daycard.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.as.common.exception.ASException;
|
||||
import io.as.common.utils.PageUtils;
|
||||
import io.as.common.utils.Query;
|
||||
import io.as.common.utils.R;
|
||||
import io.as.modules.daycard.constants.DayCardConstants;
|
||||
import io.as.modules.daycard.dao.ProfitSharingDetailDao;
|
||||
import io.as.modules.daycard.dao.ProfitSharingOrderDao;
|
||||
import io.as.modules.daycard.entity.ProfitSharingDetailEntity;
|
||||
import io.as.modules.daycard.entity.ProfitSharingOrderEntity;
|
||||
import io.as.modules.sys.dao.SysOrgDao;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 分账记录查询 Controller
|
||||
*/
|
||||
@Api(tags = "分账记录查询")
|
||||
@RestController
|
||||
@RequestMapping("/manage/profit-sharing")
|
||||
public class ProfitSharingQueryController {
|
||||
|
||||
@Autowired
|
||||
private ProfitSharingOrderDao profitSharingOrderDao;
|
||||
|
||||
@Autowired
|
||||
private ProfitSharingDetailDao profitSharingDetailDao;
|
||||
|
||||
@Autowired
|
||||
private SysOrgDao sysOrgDao;
|
||||
|
||||
/**
|
||||
* 分页查询分账单(支持 orderNo/status 筛选),返回详情列表
|
||||
*/
|
||||
@ApiOperation("分页查询分账单")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
IPage<ProfitSharingOrderEntity> page = new Query<ProfitSharingOrderEntity>().getPage(params, "created_at", false);
|
||||
|
||||
QueryWrapper<ProfitSharingOrderEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
|
||||
String orderNo = (String) params.get("orderNo");
|
||||
if (StringUtils.isNotBlank(orderNo)) {
|
||||
wrapper.eq("pay_order_no", orderNo);
|
||||
}
|
||||
String status = (String) params.get("status");
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
wrapper.eq("status", status);
|
||||
}
|
||||
String outOrderNo = (String) params.get("outOrderNo");
|
||||
if (StringUtils.isNotBlank(outOrderNo)) {
|
||||
wrapper.eq("out_order_no", outOrderNo);
|
||||
}
|
||||
|
||||
IPage<ProfitSharingOrderEntity> result = profitSharingOrderDao.selectPage(page, wrapper);
|
||||
|
||||
// 填充明细列表
|
||||
for (ProfitSharingOrderEntity order : result.getRecords()) {
|
||||
QueryWrapper<ProfitSharingDetailEntity> detailWrapper = new QueryWrapper<>();
|
||||
detailWrapper.eq("sharing_order_id", order.getId());
|
||||
List<ProfitSharingDetailEntity> details = profitSharingDetailDao.selectList(detailWrapper);
|
||||
order.setDetails(details);
|
||||
}
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", new PageUtils(result));
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账单详情 + details 子表
|
||||
*/
|
||||
@ApiOperation("分账单详情")
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
ProfitSharingOrderEntity order = profitSharingOrderDao.selectById(id);
|
||||
if (order == null) {
|
||||
throw new ASException("分账单不存在");
|
||||
}
|
||||
QueryWrapper<ProfitSharingDetailEntity> detailWrapper = new QueryWrapper<>();
|
||||
detailWrapper.eq("sharing_order_id", id);
|
||||
List<ProfitSharingDetailEntity> details = profitSharingDetailDao.selectList(detailWrapper);
|
||||
order.setDetails(details);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("order", order);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计(参数 startDate, endDate, groupBy)
|
||||
* 返回: { summary: {totalCount, totalAmount, successCount, successAmount}, groups: [{orgId, orgName, orgType, count, amount}] }
|
||||
*/
|
||||
@ApiOperation("分账统计")
|
||||
@GetMapping("/stats")
|
||||
public R stats(@RequestParam(value = "startDate", required = false) String startDate,
|
||||
@RequestParam(value = "endDate", required = false) String endDate,
|
||||
@RequestParam(value = "groupBy", required = false) String groupBy) {
|
||||
QueryWrapper<ProfitSharingOrderEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("delete_flag", DayCardConstants.DELETE_FLAG_UNDEL);
|
||||
|
||||
if (StringUtils.isNotBlank(startDate)) {
|
||||
wrapper.ge("created_at", startDate + " 00:00:00");
|
||||
}
|
||||
if (StringUtils.isNotBlank(endDate)) {
|
||||
wrapper.le("created_at", endDate + " 23:59:59");
|
||||
}
|
||||
|
||||
List<ProfitSharingOrderEntity> orders = profitSharingOrderDao.selectList(wrapper);
|
||||
|
||||
// 汇总
|
||||
int totalCount = orders.size();
|
||||
int totalAmount = 0;
|
||||
int successCount = 0;
|
||||
int successAmount = 0;
|
||||
for (ProfitSharingOrderEntity order : orders) {
|
||||
if (order.getTotalAmount() != null) {
|
||||
totalAmount += order.getTotalAmount();
|
||||
}
|
||||
if (DayCardConstants.SETTLE_STATUS_SUCCESS.equals(order.getStatus())) {
|
||||
successCount++;
|
||||
if (order.getTotalAmount() != null) {
|
||||
successAmount += order.getTotalAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> summary = new HashMap<>();
|
||||
summary.put("totalCount", totalCount);
|
||||
summary.put("totalAmount", totalAmount);
|
||||
summary.put("successCount", successCount);
|
||||
summary.put("successAmount", successAmount);
|
||||
|
||||
// 分组统计
|
||||
List<Map<String, Object>> groups = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(groupBy) && "org".equals(groupBy)) {
|
||||
// 按组织分组:查询所有明细,按 orgId 聚合
|
||||
Map<Long, Map<String, Object>> orgMap = new HashMap<>();
|
||||
for (ProfitSharingOrderEntity order : orders) {
|
||||
QueryWrapper<ProfitSharingDetailEntity> detailWrapper = new QueryWrapper<>();
|
||||
detailWrapper.eq("sharing_order_id", order.getId());
|
||||
List<ProfitSharingDetailEntity> details = profitSharingDetailDao.selectList(detailWrapper);
|
||||
for (ProfitSharingDetailEntity detail : details) {
|
||||
Long orgId = detail.getOrgId();
|
||||
if (orgId == null) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> group = orgMap.get(orgId);
|
||||
if (group == null) {
|
||||
group = new HashMap<>();
|
||||
group.put("orgId", orgId);
|
||||
group.put("count", 0);
|
||||
group.put("amount", 0);
|
||||
group.put("orgRole", detail.getOrgRole());
|
||||
// 查询组织名称
|
||||
SysOrgEntity org = sysOrgDao.selectById(orgId);
|
||||
if (org != null) {
|
||||
group.put("orgName", org.getName());
|
||||
group.put("orgType", org.getType());
|
||||
}
|
||||
orgMap.put(orgId, group);
|
||||
}
|
||||
group.put("count", (Integer) group.get("count") + 1);
|
||||
if (detail.getAmount() != null) {
|
||||
group.put("amount", (Integer) group.get("amount") + detail.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
groups.addAll(orgMap.values());
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("summary", summary);
|
||||
result.put("groups", groups);
|
||||
return R.dataSuccess(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package io.as.modules.daycard.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.daycard.constants.DayCardConstants;
|
||||
import io.as.modules.daycard.entity.ProfitSharingRuleEntity;
|
||||
import io.as.modules.daycard.entity.ProfitSharingRuleItemEntity;
|
||||
import io.as.modules.daycard.service.ProfitSharingRuleService;
|
||||
import io.as.modules.sys.entity.SysOrgEntity;
|
||||
import io.as.modules.sys.service.SysOrgService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 分润规则 Controller
|
||||
*/
|
||||
@Api(tags = "分润规则管理")
|
||||
@RestController
|
||||
@RequestMapping("/manage/profit-rule")
|
||||
public class ProfitSharingRuleController {
|
||||
|
||||
@Autowired
|
||||
private ProfitSharingRuleService profitSharingRuleService;
|
||||
|
||||
@Autowired
|
||||
private SysOrgService sysOrgService;
|
||||
|
||||
/**
|
||||
* 分页查询分润规则
|
||||
*/
|
||||
@ApiOperation("分页查询分润规则")
|
||||
@GetMapping("/list")
|
||||
public R list(@RequestParam Map<String, Object> params) {
|
||||
PageUtils page = profitSharingRuleService.queryPage(params);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("page", page);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分润规则详情(含 items 子表)
|
||||
*/
|
||||
@ApiOperation("分润规则详情")
|
||||
@GetMapping("/info/{id}")
|
||||
public R info(@PathVariable("id") Long id) {
|
||||
ProfitSharingRuleEntity rule = profitSharingRuleService.getInfo(id);
|
||||
if (rule == null) {
|
||||
throw new ASException("分润规则不存在");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("rule", rule);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增分润规则
|
||||
*/
|
||||
@ApiOperation("新增分润规则")
|
||||
@AuditAction(value = "PROFIT_RULE_SAVE", description = "新增分润规则")
|
||||
@PostMapping("/save")
|
||||
public R save(@RequestBody ProfitSharingRuleEntity rule) {
|
||||
if (StringUtils.isBlank(rule.getRuleName())) {
|
||||
throw new ASException("规则名称不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(rule.getScopeType())) {
|
||||
throw new ASException("范围类型不能为空");
|
||||
}
|
||||
profitSharingRuleService.saveRule(rule);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("rule", rule);
|
||||
return R.dataSuccess(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改分润规则
|
||||
*/
|
||||
@ApiOperation("修改分润规则")
|
||||
@AuditAction(value = "PROFIT_RULE_UPDATE", description = "修改分润规则")
|
||||
@PostMapping("/update")
|
||||
public R update(@RequestBody ProfitSharingRuleEntity rule) {
|
||||
if (rule.getId() == null) {
|
||||
throw new ASException("id不能为空");
|
||||
}
|
||||
profitSharingRuleService.updateRule(rule);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分润规则(逻辑删除)
|
||||
*/
|
||||
@ApiOperation("删除分润规则")
|
||||
@AuditAction(value = "PROFIT_RULE_DELETE", description = "删除分润规则")
|
||||
@PostMapping("/delete")
|
||||
public R delete(@RequestBody Map<String, Object> body) {
|
||||
Long id = Long.parseLong(body.get("id").toString());
|
||||
profitSharingRuleService.delete(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 试算:根据门店和金额预览分润结果
|
||||
*/
|
||||
@ApiOperation("分润试算")
|
||||
@AuditAction(value = "PROFIT_RULE_PREVIEW", description = "分润试算")
|
||||
@PostMapping("/preview")
|
||||
public R preview(@RequestBody Map<String, Object> body) {
|
||||
Long storeOrgId = Long.parseLong(body.get("storeOrgId").toString());
|
||||
Long amount = Long.parseLong(body.get("amount").toString());
|
||||
|
||||
// 1. 从 storeOrgId 查 sys_org 获取 mpath,解析出 channelOrgId 和 agentOrgId
|
||||
SysOrgEntity storeOrg = sysOrgService.getById(storeOrgId);
|
||||
if (storeOrg == null) {
|
||||
throw new ASException("门店组织不存在");
|
||||
}
|
||||
String mpath = storeOrg.getMpath();
|
||||
if (StringUtils.isBlank(mpath)) {
|
||||
throw new ASException("门店组织 mpath 为空");
|
||||
}
|
||||
String[] segments = mpath.split("\\.");
|
||||
// segments[0] = PLATFORM, segments[1] = CHANNEL, segments[n-1] = STORE
|
||||
Long platformOrgId = null;
|
||||
Long channelOrgId = null;
|
||||
Long agentOrgId = null;
|
||||
if (segments.length >= 1) {
|
||||
platformOrgId = Long.parseLong(segments[0]);
|
||||
}
|
||||
if (segments.length >= 2) {
|
||||
channelOrgId = Long.parseLong(segments[1]);
|
||||
}
|
||||
// segments.length >= 4 表示路径中有代理商(PLATFORM.CHANNEL.AGENT.STORE)
|
||||
if (segments.length >= 4) {
|
||||
agentOrgId = Long.parseLong(segments[segments.length - 2]);
|
||||
}
|
||||
|
||||
// 2. 调用 matchRule 匹配规则
|
||||
ProfitSharingRuleEntity rule = profitSharingRuleService.matchRule(storeOrgId, agentOrgId, channelOrgId);
|
||||
if (rule == null) {
|
||||
throw new ASException("未匹配到分润规则");
|
||||
}
|
||||
|
||||
// 3. 按 items 计算各方分账金额
|
||||
List<ProfitSharingRuleItemEntity> items = rule.getItems();
|
||||
if (items == null || items.isEmpty()) {
|
||||
throw new ASException("分润规则未配置明细");
|
||||
}
|
||||
|
||||
// 按 sortOrder 排序
|
||||
items.sort((a, b) -> {
|
||||
int sa = a.getSortOrder() == null ? 0 : a.getSortOrder();
|
||||
int sb = b.getSortOrder() == null ? 0 : b.getSortOrder();
|
||||
return Integer.compare(sa, sb);
|
||||
});
|
||||
|
||||
// orgRole -> orgId 映射
|
||||
Map<String, Long> orgIdMap = new HashMap<>();
|
||||
orgIdMap.put(DayCardConstants.ORG_ROLE_PLATFORM, platformOrgId);
|
||||
orgIdMap.put(DayCardConstants.ORG_ROLE_CHANNEL, channelOrgId);
|
||||
orgIdMap.put(DayCardConstants.ORG_ROLE_AGENT, agentOrgId);
|
||||
orgIdMap.put(DayCardConstants.ORG_ROLE_STORE, storeOrgId);
|
||||
|
||||
// 找到最后一个 PLATFORM 项(尾差归平台)
|
||||
int lastPlatformIndex = -1;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (DayCardConstants.ORG_ROLE_PLATFORM.equals(items.get(i).getOrgRole())) {
|
||||
lastPlatformIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算非平台方 RATIO 比例之和
|
||||
BigDecimal nonPlatformTotalRatio = BigDecimal.ZERO;
|
||||
|
||||
// 计算各方分账金额
|
||||
List<Map<String, Object>> details = new ArrayList<>();
|
||||
long allocatedTotal = 0;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ProfitSharingRuleItemEntity item = items.get(i);
|
||||
Map<String, Object> detail = new HashMap<>();
|
||||
detail.put("orgRole", item.getOrgRole());
|
||||
detail.put("shareType", item.getShareType());
|
||||
detail.put("shareValue", item.getShareValue());
|
||||
|
||||
Long orgId = orgIdMap.get(item.getOrgRole());
|
||||
detail.put("orgId", orgId);
|
||||
|
||||
// 查询组织名称
|
||||
String orgName = null;
|
||||
if (orgId != null) {
|
||||
SysOrgEntity org = sysOrgService.getById(orgId);
|
||||
if (org != null) {
|
||||
orgName = org.getName();
|
||||
}
|
||||
}
|
||||
detail.put("orgName", orgName);
|
||||
|
||||
long itemAmount;
|
||||
if (i == lastPlatformIndex) {
|
||||
// 最后一方 PLATFORM 用减法(尾差归平台)
|
||||
itemAmount = amount - allocatedTotal;
|
||||
} else if (DayCardConstants.SHARE_TYPE_FIXED.equals(item.getShareType())) {
|
||||
// FIXED: shareValue 直接作为分
|
||||
itemAmount = item.getShareValue().longValue();
|
||||
allocatedTotal += itemAmount;
|
||||
} else {
|
||||
// RATIO: amount * shareValue / 100
|
||||
BigDecimal ratioAmount = BigDecimal.valueOf(amount)
|
||||
.multiply(item.getShareValue())
|
||||
.divide(BigDecimal.valueOf(100), 0, RoundingMode.DOWN);
|
||||
itemAmount = ratioAmount.longValue();
|
||||
allocatedTotal += itemAmount;
|
||||
}
|
||||
|
||||
// 统计非平台方 RATIO 比例之和
|
||||
if (!DayCardConstants.ORG_ROLE_PLATFORM.equals(item.getOrgRole())
|
||||
&& DayCardConstants.SHARE_TYPE_RATIO.equals(item.getShareType())
|
||||
&& item.getShareValue() != null) {
|
||||
nonPlatformTotalRatio = nonPlatformTotalRatio.add(item.getShareValue());
|
||||
}
|
||||
|
||||
detail.put("amount", itemAmount);
|
||||
details.add(detail);
|
||||
}
|
||||
|
||||
// 5. warning 非 null 时表示超30%
|
||||
String warning = null;
|
||||
if (nonPlatformTotalRatio.compareTo(BigDecimal.valueOf(DayCardConstants.PROFIT_SHARE_LIMIT)) > 0) {
|
||||
warning = "非平台方分润比例之和" + nonPlatformTotalRatio + "%超过上限" + DayCardConstants.PROFIT_SHARE_LIMIT + "%";
|
||||
}
|
||||
|
||||
// 4. 返回结果
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("ruleId", rule.getId());
|
||||
result.put("ruleName", rule.getRuleName());
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user