fix: 客户品牌
This commit is contained in:
parent
2e8210ca90
commit
656e6feadd
|
|
@ -2,12 +2,9 @@ package com.ruoyi.api;
|
||||||
|
|
||||||
import com.ruoyi.ai.domain.AiSampleAmount;
|
import com.ruoyi.ai.domain.AiSampleAmount;
|
||||||
import com.ruoyi.ai.domain.AiSampleAmountRecord;
|
import com.ruoyi.ai.domain.AiSampleAmountRecord;
|
||||||
import com.ruoyi.ai.domain.AiStatistics;
|
|
||||||
import com.ruoyi.ai.domain.enums.AiConfigEnum;
|
|
||||||
import com.ruoyi.ai.mapper.AiSampleAmountMapper;
|
import com.ruoyi.ai.mapper.AiSampleAmountMapper;
|
||||||
import com.ruoyi.ai.service.EmailVerifyService;
|
import com.ruoyi.ai.service.EmailVerifyService;
|
||||||
import com.ruoyi.ai.service.IAiSampleAmountRecordService;
|
import com.ruoyi.ai.service.IAiSampleAmountRecordService;
|
||||||
import com.ruoyi.ai.service.IAiStatisticsService;
|
|
||||||
import com.ruoyi.ai.service.IAiUserService;
|
import com.ruoyi.ai.service.IAiUserService;
|
||||||
import com.ruoyi.common.annotation.Anonymous;
|
import com.ruoyi.common.annotation.Anonymous;
|
||||||
import com.ruoyi.common.constant.BalanceChangerConstants;
|
import com.ruoyi.common.constant.BalanceChangerConstants;
|
||||||
|
|
@ -22,10 +19,8 @@ import com.ruoyi.common.core.domain.model.RegisterAiUserBody;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.exception.job.TaskException;
|
import com.ruoyi.common.exception.job.TaskException;
|
||||||
import com.ruoyi.common.utils.DateUtils;
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
import com.ruoyi.common.utils.IpCountryQueryByApi;
|
|
||||||
import com.ruoyi.common.utils.MessageUtils;
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
import com.ruoyi.common.utils.ip.IpUtils;
|
|
||||||
import com.ruoyi.framework.web.service.SysLoginService;
|
import com.ruoyi.framework.web.service.SysLoginService;
|
||||||
import com.ruoyi.quartz.domain.SysJob;
|
import com.ruoyi.quartz.domain.SysJob;
|
||||||
import com.ruoyi.quartz.service.ISysJobService;
|
import com.ruoyi.quartz.service.ISysJobService;
|
||||||
|
|
@ -40,7 +35,10 @@ import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -104,8 +102,11 @@ public class AiUserApiController extends BaseController {
|
||||||
// 查询启用状态体验金活动
|
// 查询启用状态体验金活动
|
||||||
AiSampleAmount aiSampleAmount = aiSampleAmountMapper.getSampleAmount();
|
AiSampleAmount aiSampleAmount = aiSampleAmountMapper.getSampleAmount();
|
||||||
if (aiSampleAmount != null) {
|
if (aiSampleAmount != null) {
|
||||||
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
|
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
String orderNo = dateTime + uuid;
|
||||||
// 余额变更记录
|
// 余额变更记录
|
||||||
aiUserService.addUserBalance(aiUser.getId(), aiSampleAmount.getAmount(), BalanceChangerConstants.EXPERIENCE_GOLD_GIFT);
|
aiUserService.addUserBalance(orderNo, aiUser.getId(), aiSampleAmount.getAmount(), BalanceChangerConstants.EXPERIENCE_GOLD_GIFT);
|
||||||
// 新增体验金记录
|
// 新增体验金记录
|
||||||
AiSampleAmountRecord aiSampleAmountRecord = new AiSampleAmountRecord();
|
AiSampleAmountRecord aiSampleAmountRecord = new AiSampleAmountRecord();
|
||||||
aiSampleAmountRecord.setUserId(aiUser.getId());
|
aiSampleAmountRecord.setUserId(aiUser.getId());
|
||||||
|
|
@ -218,6 +219,7 @@ public class AiUserApiController extends BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询返佣比例配置
|
* 查询返佣比例配置
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@GetMapping("/getRebateConfig")
|
@GetMapping("/getRebateConfig")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.ruoyi.api;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COS 上传兼容接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/cos")
|
||||||
|
@Api(tags = "COS文件上传")
|
||||||
|
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||||
|
public class CosController {
|
||||||
|
|
||||||
|
private final TencentCosUtil tencentCosUtil;
|
||||||
|
|
||||||
|
@ApiOperation("COS上传接口")
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public AjaxResult upload(
|
||||||
|
@ApiParam(name = "file", value = "文件", required = true)
|
||||||
|
@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
|
String uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||||
|
AjaxResult ajax = AjaxResult.success(uploadUrl);
|
||||||
|
ajax.put("url", uploadUrl);
|
||||||
|
ajax.put("oldName", file.getOriginalFilename());
|
||||||
|
return ajax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.ruoyi.api;
|
package com.ruoyi.api;
|
||||||
|
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.utils.AwsS3Util;
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
|
|
@ -18,7 +18,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
@Api(tags = "文件上传")
|
@Api(tags = "文件上传")
|
||||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||||
public class FileController {
|
public class FileController {
|
||||||
private final AwsS3Util awsS3Util;
|
private final TencentCosUtil tencentCosUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件上传
|
* 文件上传
|
||||||
|
|
@ -30,7 +30,7 @@ public class FileController {
|
||||||
@ApiParam(name = "file", value = "文件", required = true)
|
@ApiParam(name = "file", value = "文件", required = true)
|
||||||
@RequestParam("file") MultipartFile file) throws Exception {
|
@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
String uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
String uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||||
ajax.put("url", uploadUrl);
|
ajax.put("url", uploadUrl);
|
||||||
return ajax;
|
return ajax;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ public class KadaPaymentJava {
|
||||||
map.put("pm", "QRPH");
|
map.put("pm", "QRPH");
|
||||||
map.put("ref", orderNo);
|
map.put("ref", orderNo);
|
||||||
map.put("payer", payer);
|
map.put("payer", payer);
|
||||||
map.put("redirect", "www.google.com/recharge");
|
map.put("redirect", "https://undressing.top/recharge");
|
||||||
map.put("callbackUrl", "www.google.com/api/pay/kada-callBack");
|
map.put("callbackUrl", "https://undressing.top/api/pay/kada-callBack");
|
||||||
|
|
||||||
return JSON.toJSONString(map);
|
return JSON.toJSONString(map);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import com.ruoyi.ai.service.IAiRechargeService;
|
||||||
import com.ruoyi.ai.service.IJinShaService;
|
import com.ruoyi.ai.service.IJinShaService;
|
||||||
import com.ruoyi.ai.service.IKaDaService;
|
import com.ruoyi.ai.service.IKaDaService;
|
||||||
import com.ruoyi.ai.service.IYuZhouService;
|
import com.ruoyi.ai.service.IYuZhouService;
|
||||||
|
import com.ruoyi.ai.service.IVmService;
|
||||||
import com.ruoyi.common.annotation.Anonymous;
|
import com.ruoyi.common.annotation.Anonymous;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.utils.ip.IpUtils;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
@ -25,6 +27,7 @@ public class PayController {
|
||||||
private final IKaDaService kaDaService;
|
private final IKaDaService kaDaService;
|
||||||
private final IAiRechargeService aiRechargeService;
|
private final IAiRechargeService aiRechargeService;
|
||||||
private final IYuZhouService yuZhouService;
|
private final IYuZhouService yuZhouService;
|
||||||
|
private final IVmService vmService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jinsha请求支付
|
* jinsha请求支付
|
||||||
|
|
@ -85,4 +88,109 @@ public class PayController {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM请求支付
|
||||||
|
*/
|
||||||
|
@PostMapping("/vm-pay")
|
||||||
|
@ApiOperation("VM请求支付")
|
||||||
|
public AjaxResult vmPay(@RequestBody VmPayReq req, HttpServletRequest request) throws Exception {
|
||||||
|
// 获取用户真实IP地址
|
||||||
|
String clientIp = IpUtils.getIpAddr(request);
|
||||||
|
PayResVO payResVO = vmService.vmPay(req.getGearId(), req.getVmCardInfo(), clientIp);
|
||||||
|
return AjaxResult.success(payResVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/vm-callBack")
|
||||||
|
@ApiOperation("VM支付回调")
|
||||||
|
@Anonymous
|
||||||
|
public String vmCallBack(HttpServletRequest request) {
|
||||||
|
// 记录请求的Content-Type
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
|
||||||
|
// 从请求中解析表单参数
|
||||||
|
VmCallBackReq req = new VmCallBackReq();
|
||||||
|
|
||||||
|
// 记录所有请求参数用于调试
|
||||||
|
java.util.Enumeration<String> paramNames = request.getParameterNames();
|
||||||
|
java.util.Map<String, String> allParams = new java.util.HashMap<>();
|
||||||
|
while (paramNames.hasMoreElements()) {
|
||||||
|
String paramName = paramNames.nextElement();
|
||||||
|
String paramValue = request.getParameter(paramName);
|
||||||
|
allParams.put(paramName, paramValue);
|
||||||
|
}
|
||||||
|
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PayController.class);
|
||||||
|
log.info("VM支付回调请求参数: {}, Content-Type: {}", allParams, contentType);
|
||||||
|
|
||||||
|
req.setMchNo(request.getParameter("mchNo"));
|
||||||
|
req.setAppId(request.getParameter("appId"));
|
||||||
|
req.setMchOrderNo(request.getParameter("mchOrderNo"));
|
||||||
|
req.setPayOrderId(request.getParameter("payOrderId"));
|
||||||
|
req.setWayCode(request.getParameter("wayCode"));
|
||||||
|
|
||||||
|
// 解析整数类型字段
|
||||||
|
String amountStr = request.getParameter("amount");
|
||||||
|
if (amountStr != null && !amountStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
req.setAmount(Integer.parseInt(amountStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("VM支付回调amount解析失败: {}", amountStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.setCurrency(request.getParameter("currency"));
|
||||||
|
req.setIfCode(request.getParameter("ifCode"));
|
||||||
|
req.setSubject(request.getParameter("subject"));
|
||||||
|
req.setBody(request.getParameter("body"));
|
||||||
|
req.setClientIp(request.getParameter("clientIp"));
|
||||||
|
|
||||||
|
// 解析订单状态(文档字段名:state)
|
||||||
|
String stateStr = request.getParameter("state");
|
||||||
|
if (stateStr != null && !stateStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
req.setState(Integer.parseInt(stateStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("VM支付回调state解析失败: {}", stateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.setChannelOrderNo(request.getParameter("channelOrderNo"));
|
||||||
|
req.setErrCode(request.getParameter("errCode"));
|
||||||
|
req.setErrMsg(request.getParameter("errMsg"));
|
||||||
|
req.setExtParam(request.getParameter("extParam"));
|
||||||
|
|
||||||
|
// 解析时间戳:createdAt、successTime、reqTime(文档字段名)
|
||||||
|
String createdAtStr = request.getParameter("createdAt");
|
||||||
|
if (createdAtStr != null && !createdAtStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
req.setCreatedAt(Long.parseLong(createdAtStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("VM支付回调createdAt解析失败: {}", createdAtStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String successTimeStr = request.getParameter("successTime");
|
||||||
|
if (successTimeStr != null && !successTimeStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
req.setSuccessTime(Long.parseLong(successTimeStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("VM支付回调successTime解析失败: {}", successTimeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String reqTimeStr = request.getParameter("reqTime");
|
||||||
|
if (reqTimeStr != null && !reqTimeStr.isEmpty()) {
|
||||||
|
try {
|
||||||
|
req.setReqTime(Long.parseLong(reqTimeStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.warn("VM支付回调reqTime解析失败: {}", reqTimeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.setSign(request.getParameter("sign"));
|
||||||
|
req.setSignType(request.getParameter("signType"));
|
||||||
|
|
||||||
|
log.info("VM支付回调解析后的对象: mchOrderNo={}, state={}, sign={}",
|
||||||
|
req.getMchOrderNo(), req.getState(), req.getSign());
|
||||||
|
|
||||||
|
return vmService.vmCallBack(req);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@ public class ByteApiRequest {
|
||||||
@ApiModelProperty(name = "标签字符串")
|
@ApiModelProperty(name = "标签字符串")
|
||||||
private String tags;
|
private String tags;
|
||||||
|
|
||||||
|
@ApiModelProperty(name = "使用的模型")
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
@ApiModelProperty(name = "生成模式:text-to-video 或 image-to-video")
|
||||||
|
private String mode = "text-to-video";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.ruoyi.api.request;
|
||||||
|
|
||||||
|
import com.ruoyi.ai.domain.ContentItem;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门户视频生成(火山 Seedance)请求体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PortalVideoGenRequest {
|
||||||
|
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文生视频时可选:多段文本 + 参考图(与火山 content 一致);不传则仅使用 text 单行
|
||||||
|
*/
|
||||||
|
private List<ContentItem> content;
|
||||||
|
|
||||||
|
/** 默认与后台配置的视频计费类型一致 */
|
||||||
|
private String functionType = "21";
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
private Integer duration;
|
||||||
|
|
||||||
|
private String resolution;
|
||||||
|
|
||||||
|
private String ratio;
|
||||||
|
|
||||||
|
private String firstUrl;
|
||||||
|
|
||||||
|
private String lastUrl;
|
||||||
|
|
||||||
|
/** 图生视频-参考图模式 */
|
||||||
|
private String referenceUrl;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
package com.ruoyi.web.controller.ai;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import com.ruoyi.ai.service.IAiUserService;
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.system.service.ISysDeptService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 业务侧部门管理(数据源与系统部门 sys_dept 一致,权限独立)
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai/dept")
|
||||||
|
public class AiDeptController extends BaseController
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private ISysDeptService deptService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiUserService aiUserService;
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:list')")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public AjaxResult list(SysDept dept)
|
||||||
|
{
|
||||||
|
List<SysDept> depts = deptService.selectDeptList(dept);
|
||||||
|
return success(depts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:list')")
|
||||||
|
@GetMapping("/list/exclude/{deptId}")
|
||||||
|
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
|
||||||
|
{
|
||||||
|
List<SysDept> depts = deptService.selectDeptList(new SysDept());
|
||||||
|
depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
|
||||||
|
return success(depts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:query')")
|
||||||
|
@GetMapping(value = "/{deptId}")
|
||||||
|
public AjaxResult getInfo(@PathVariable Long deptId)
|
||||||
|
{
|
||||||
|
deptService.checkDeptDataScope(deptId);
|
||||||
|
return success(deptService.selectDeptById(deptId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:add')")
|
||||||
|
@Log(title = "AI部门管理", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping
|
||||||
|
public AjaxResult add(@Validated @RequestBody SysDept dept)
|
||||||
|
{
|
||||||
|
if (!deptService.checkDeptNameUnique(dept))
|
||||||
|
{
|
||||||
|
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
||||||
|
}
|
||||||
|
dept.setCreateBy(getUsername());
|
||||||
|
return toAjax(deptService.insertDept(dept));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:edit')")
|
||||||
|
@Log(title = "AI部门管理", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping
|
||||||
|
public AjaxResult edit(@Validated @RequestBody SysDept dept)
|
||||||
|
{
|
||||||
|
Long deptId = dept.getDeptId();
|
||||||
|
deptService.checkDeptDataScope(deptId);
|
||||||
|
if (!deptService.checkDeptNameUnique(dept))
|
||||||
|
{
|
||||||
|
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
||||||
|
}
|
||||||
|
else if (dept.getParentId().equals(deptId))
|
||||||
|
{
|
||||||
|
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
|
||||||
|
}
|
||||||
|
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
|
||||||
|
{
|
||||||
|
return error("该部门包含未停用的子部门!");
|
||||||
|
}
|
||||||
|
dept.setUpdateBy(getUsername());
|
||||||
|
return toAjax(deptService.updateDept(dept));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:dept:remove')")
|
||||||
|
@Log(title = "AI部门管理", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{deptId}")
|
||||||
|
public AjaxResult remove(@PathVariable Long deptId)
|
||||||
|
{
|
||||||
|
if (deptService.hasChildByDeptId(deptId))
|
||||||
|
{
|
||||||
|
return warn("存在下级部门,不允许删除");
|
||||||
|
}
|
||||||
|
if (deptService.checkDeptExistUser(deptId))
|
||||||
|
{
|
||||||
|
return warn("部门存在系统用户,不允许删除");
|
||||||
|
}
|
||||||
|
if (aiUserService.countAiUserByDeptId(deptId) > 0)
|
||||||
|
{
|
||||||
|
return warn("部门存在 AI 用户,不允许删除");
|
||||||
|
}
|
||||||
|
deptService.checkDeptDataScope(deptId);
|
||||||
|
return toAjax(deptService.deleteDeptById(deptId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.ruoyi.ai.controller;
|
package com.ruoyi.web.controller.ai;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,27 @@
|
||||||
package com.ruoyi.web.controller.ai;
|
package com.ruoyi.web.controller.ai;
|
||||||
|
|
||||||
import cn.hutool.core.util.NumberUtil;
|
import cn.hutool.core.util.NumberUtil;
|
||||||
import com.ruoyi.ai.service.EmailVerifyService;
|
|
||||||
import com.ruoyi.ai.service.IAiUserService;
|
import com.ruoyi.ai.service.IAiUserService;
|
||||||
import com.ruoyi.common.annotation.Anonymous;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.constant.BalanceChangerConstants;
|
import com.ruoyi.common.constant.BalanceChangerConstants;
|
||||||
import com.ruoyi.common.constant.HttpStatus;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.core.domain.entity.AiUser;
|
import com.ruoyi.common.core.domain.entity.AiUser;
|
||||||
import com.ruoyi.common.core.domain.model.RegisterAiUserBody;
|
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
|
||||||
import com.ruoyi.common.utils.MessageUtils;
|
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.mail.MessagingException;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ai-用户信息Controller
|
* ai-用户信息Controller
|
||||||
|
|
@ -77,7 +71,6 @@ public class AiUserController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增ai-用户信息
|
* 新增ai-用户信息
|
||||||
*/
|
*/
|
||||||
|
|
@ -100,6 +93,20 @@ public class AiUserController extends BaseController {
|
||||||
return toAjax(aiUserService.updateAiUser(aiUser));
|
return toAjax(aiUserService.updateAiUser(aiUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配归属部门(deptId 为空表示不归属任何部门)
|
||||||
|
*/
|
||||||
|
@ApiOperation("分配AI用户归属部门")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ai:user:edit')")
|
||||||
|
@Log(title = "ai-用户信息", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/dept")
|
||||||
|
public AjaxResult assignDept(@RequestBody AiUser aiUser) {
|
||||||
|
if (aiUser.getId() == null) {
|
||||||
|
return error("用户主键不能为空");
|
||||||
|
}
|
||||||
|
return toAjax(aiUserService.updateAiUserDept(aiUser.getId(), aiUser.getDeptId()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态修改
|
* 状态修改
|
||||||
*/
|
*/
|
||||||
|
|
@ -123,9 +130,12 @@ public class AiUserController extends BaseController {
|
||||||
aiUser.setUpdateBy(getUsername());
|
aiUser.setUpdateBy(getUsername());
|
||||||
// 获取原余额
|
// 获取原余额
|
||||||
AiUser u = aiUserService.selectAiUserById(aiUser.getId());
|
AiUser u = aiUserService.selectAiUserById(aiUser.getId());
|
||||||
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
|
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
String orderNo = dateTime + uuid;
|
||||||
// 计算变更余额
|
// 计算变更余额
|
||||||
BigDecimal amount = NumberUtil.sub(aiUser.getBalance(), u.getBalance());
|
BigDecimal amount = NumberUtil.sub(aiUser.getBalance(), u.getBalance());
|
||||||
aiUserService.addUserBalance(aiUser.getId(), amount, BalanceChangerConstants.SYSTEM_OPERATION);
|
aiUserService.addUserBalance(orderNo, aiUser.getId(), amount, BalanceChangerConstants.SYSTEM_OPERATION);
|
||||||
return toAjax(1);
|
return toAjax(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,8 +161,6 @@ public class AiUserController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @GetMapping("/addUserBalance")
|
// @GetMapping("/addUserBalance")
|
||||||
// public AjaxResult addUserBalance(BigDecimal amount) {
|
// public AjaxResult addUserBalance(BigDecimal amount) {
|
||||||
// aiUserService.addUserBalance(SecurityUtils.getAiUserId(), amount, BalanceChangerConstants.RECHARGE);
|
// aiUserService.addUserBalance(SecurityUtils.getAiUserId(), amount, BalanceChangerConstants.RECHARGE);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import com.ruoyi.common.annotation.Anonymous;
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
import com.ruoyi.common.config.RuoYiConfig;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.exception.base.BaseException;
|
import com.ruoyi.common.exception.base.BaseException;
|
||||||
import com.ruoyi.common.utils.AwsS3Util;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.TencentCosUtil;
|
import com.ruoyi.common.utils.TencentCosUtil;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
|
|
@ -36,7 +35,6 @@ public class CommonController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
||||||
private final ServerConfig serverConfig;
|
private final ServerConfig serverConfig;
|
||||||
private final TencentCosUtil tencentCosUtil;
|
private final TencentCosUtil tencentCosUtil;
|
||||||
private final AwsS3Util awsS3Util;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用下载请求
|
* 通用下载请求
|
||||||
|
|
@ -158,7 +156,7 @@ public class CommonController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AWS上传请求(单个)
|
* 腾讯云COS上传请求(单个)
|
||||||
*/
|
*/
|
||||||
@ApiOperation("图片上传接口")
|
@ApiOperation("图片上传接口")
|
||||||
@PostMapping("/aws/upload")
|
@PostMapping("/aws/upload")
|
||||||
|
|
@ -166,7 +164,7 @@ public class CommonController {
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
String uploadUrl;
|
String uploadUrl;
|
||||||
try {
|
try {
|
||||||
uploadUrl = awsS3Util.uploadMultipartFile(file, true);
|
uploadUrl = tencentCosUtil.uploadMultipartFile(file, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AjaxResult.error(e.getMessage());
|
return AjaxResult.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ spring:
|
||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:mysql://xxxxx:xxxxx/byteai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://database-1.cvs822qoc391.ap-southeast-1.rds.amazonaws.com:3306/byteai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
username: root
|
username: root
|
||||||
password: xxxxxx
|
password: mkMReisAKl6I7rVqEY90
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
# 初始连接数
|
# 初始连接数
|
||||||
|
|
@ -41,8 +41,8 @@ spring:
|
||||||
allow:
|
allow:
|
||||||
url-pattern: /druid/*
|
url-pattern: /druid/*
|
||||||
# 控制台管理用户名和密码
|
# 控制台管理用户名和密码
|
||||||
login-username:
|
login-username: ruoyi
|
||||||
login-password:
|
login-password: cvs822qoc391
|
||||||
filter:
|
filter:
|
||||||
stat:
|
stat:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
@ -54,3 +54,4 @@ spring:
|
||||||
config:
|
config:
|
||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,12 @@
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Codec for Base64 encoding/decoding -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- excel工具 -->
|
<!-- excel工具 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.poi</groupId>
|
<groupId>org.apache.poi</groupId>
|
||||||
|
|
|
||||||
|
|
@ -117,12 +117,19 @@ public class AiUser extends BaseEntity {
|
||||||
*/
|
*/
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
|
/** 归属部门ID(sys_dept.dept_id) */
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级用户昵称
|
* 上级用户昵称
|
||||||
*/
|
*/
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String superiorName;
|
private String superiorName;
|
||||||
|
|
||||||
|
/** 归属部门名称 */
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级用户ID
|
* 上级用户ID
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ public class SysDept extends BaseEntity
|
||||||
|
|
||||||
/** 父部门名称 */
|
/** 父部门名称 */
|
||||||
private String parentName;
|
private String parentName;
|
||||||
|
|
||||||
|
/** Byte API Key */
|
||||||
|
private String byteApiKey;
|
||||||
|
|
||||||
/** 子部门 */
|
/** 子部门 */
|
||||||
private List<SysDept> children = new ArrayList<SysDept>();
|
private List<SysDept> children = new ArrayList<SysDept>();
|
||||||
|
|
@ -171,6 +174,16 @@ public class SysDept extends BaseEntity
|
||||||
this.parentName = parentName;
|
this.parentName = parentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getByteApiKey()
|
||||||
|
{
|
||||||
|
return byteApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setByteApiKey(String byteApiKey)
|
||||||
|
{
|
||||||
|
this.byteApiKey = byteApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
public List<SysDept> getChildren()
|
public List<SysDept> getChildren()
|
||||||
{
|
{
|
||||||
return children;
|
return children;
|
||||||
|
|
@ -192,6 +205,7 @@ public class SysDept extends BaseEntity
|
||||||
.append("leader", getLeader())
|
.append("leader", getLeader())
|
||||||
.append("phone", getPhone())
|
.append("phone", getPhone())
|
||||||
.append("email", getEmail())
|
.append("email", getEmail())
|
||||||
|
.append("byteApiKey", getByteApiKey())
|
||||||
.append("status", getStatus())
|
.append("status", getStatus())
|
||||||
.append("delFlag", getDelFlag())
|
.append("delFlag", getDelFlag())
|
||||||
.append("createBy", getCreateBy())
|
.append("createBy", getCreateBy())
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,13 @@ import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
|
@ -33,22 +39,32 @@ public class TencentCosUtil {
|
||||||
private String domain;
|
private String domain;
|
||||||
|
|
||||||
|
|
||||||
//文件上传
|
/**
|
||||||
public String upload(MultipartFile file) {
|
* 上传MultipartFile到腾讯云COS,返回文件访问地址
|
||||||
|
* 与AwsS3Util.uploadMultipartFile方法接口兼容
|
||||||
|
*/
|
||||||
|
public String upload(MultipartFile file) throws Exception {
|
||||||
|
return uploadMultipartFile(file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传MultipartFile到腾讯云COS,返回文件访问地址
|
||||||
|
*
|
||||||
|
* @param file 前端上传的MultipartFile
|
||||||
|
* @param isPublic 是否公开访问(当前实现中忽略,使用domain配置)
|
||||||
|
* @return 文件访问地址(URL字符串)
|
||||||
|
*/
|
||||||
|
public String uploadMultipartFile(MultipartFile file, boolean isPublic) throws Exception {
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("上传文件不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
// 3 生成 cos 客户端。
|
|
||||||
COSClient cosClient = createCosClient();
|
COSClient cosClient = createCosClient();
|
||||||
|
|
||||||
// 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
|
// 生成唯一文件键,格式与AWS一致:yyyy/MM/dd/uuid_filename
|
||||||
// 对象键(Key)是对象在存储桶中的唯一标识。 998u-09iu-09i-333
|
String key = generateCosKey(file.getOriginalFilename());
|
||||||
//在文件名称前面添加uuid值
|
|
||||||
String key = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8) + "_"
|
|
||||||
+ file.getOriginalFilename();
|
|
||||||
//对上传文件分组,根据当前日期 /2022/11/11
|
|
||||||
String dateTime = new DateTime().toString("yyyy/MM/dd");
|
|
||||||
key = dateTime + "/" + key;
|
|
||||||
try {
|
try {
|
||||||
//获取上传文件输入流
|
|
||||||
InputStream inputStream = file.getInputStream();
|
InputStream inputStream = file.getInputStream();
|
||||||
ObjectMetadata objectMetadata = new ObjectMetadata();
|
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||||
PutObjectRequest putObjectRequest = new PutObjectRequest(
|
PutObjectRequest putObjectRequest = new PutObjectRequest(
|
||||||
|
|
@ -56,17 +72,41 @@ public class TencentCosUtil {
|
||||||
key,
|
key,
|
||||||
inputStream,
|
inputStream,
|
||||||
objectMetadata);
|
objectMetadata);
|
||||||
// 高级接口会返回一个异步结果Upload
|
|
||||||
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
||||||
|
|
||||||
//返回上传文件路径
|
// 返回COS文件访问地址
|
||||||
//https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
|
String url = domain + (domain.endsWith("/") ? "" : "/") + key;
|
||||||
String url = domain + "/" + key;
|
|
||||||
return url;
|
return url;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("上传文件到COS失败: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
cosClient.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过URL下载文件并上传到COS
|
||||||
|
* 与AwsS3Util.uploadFileByUrl方法接口兼容
|
||||||
|
*/
|
||||||
|
public String uploadFileByUrl(String fileUrl) throws Exception {
|
||||||
|
return uploadFileByUrl(fileUrl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String uploadFileByUrl(String fileUrl, boolean isPublic) throws Exception {
|
||||||
|
if (fileUrl == null || fileUrl.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("文件下载链接不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
Path tempPath = downloadFileToTemp(fileUrl);
|
||||||
|
try {
|
||||||
|
// 使用临时文件上传
|
||||||
|
MultipartFile multipartFile = createMultipartFileFromPath(tempPath, extractFileNameFromUrl(fileUrl));
|
||||||
|
return uploadMultipartFile(multipartFile, isPublic);
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(tempPath);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,7 +118,107 @@ public class TencentCosUtil {
|
||||||
ClientConfig clientConfig = new ClientConfig(region);
|
ClientConfig clientConfig = new ClientConfig(region);
|
||||||
// 这里建议设置使用 https 协议
|
// 这里建议设置使用 https 协议
|
||||||
clientConfig.setHttpProtocol(HttpProtocol.https);
|
clientConfig.setHttpProtocol(HttpProtocol.https);
|
||||||
|
clientConfig.setConnectionTimeout(30 * 1000); // 连接超时30秒
|
||||||
|
clientConfig.setSocketTimeout(60 * 1000); // 读取超时60秒
|
||||||
//1.3 生成cos客户端
|
//1.3 生成cos客户端
|
||||||
return new COSClient(credentials, clientConfig);
|
return new COSClient(credentials, clientConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成COS文件键,与AWS保持一致的命名格式
|
||||||
|
*/
|
||||||
|
private String generateCosKey(String originalFileName) {
|
||||||
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
|
String dateTime = new DateTime().toString("yyyy/MM/dd");
|
||||||
|
return dateTime + "/" + uuid + "_" + originalFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件到临时路径
|
||||||
|
*/
|
||||||
|
private Path downloadFileToTemp(String fileUrl) throws Exception {
|
||||||
|
String suffix = getFileSuffixFromUrl(fileUrl);
|
||||||
|
Path tempPath = Files.createTempFile("url-upload-", suffix);
|
||||||
|
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(fileUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setConnectTimeout(5000);
|
||||||
|
connection.setReadTimeout(10000);
|
||||||
|
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode >= 300) {
|
||||||
|
throw new RuntimeException("文件下载失败,状态码:" + responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
in = connection.getInputStream();
|
||||||
|
out = Files.newOutputStream(tempPath);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try { out.close(); } catch (IOException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
if (in != null) {
|
||||||
|
try { in.close(); } catch (IOException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractFileNameFromUrl(String fileUrl) {
|
||||||
|
String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
|
||||||
|
if (fileName.contains("?")) {
|
||||||
|
fileName = fileName.split("\\?")[0];
|
||||||
|
}
|
||||||
|
return fileName.isEmpty() ? "default_file" : fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFileSuffixFromUrl(String fileUrl) {
|
||||||
|
String fileName = extractFileNameFromUrl(fileUrl);
|
||||||
|
if (fileName.contains(".")) {
|
||||||
|
return fileName.substring(fileName.lastIndexOf("."));
|
||||||
|
}
|
||||||
|
return ".tmp";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Path转换为MultipartFile(简单实现,用于uploadFileByUrl)
|
||||||
|
*/
|
||||||
|
private MultipartFile createMultipartFileFromPath(Path path, String originalFilename) throws IOException {
|
||||||
|
byte[] bytes = Files.readAllBytes(path);
|
||||||
|
return new MultipartFile() {
|
||||||
|
@Override
|
||||||
|
public String getName() { return "file"; }
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() { return originalFilename; }
|
||||||
|
@Override
|
||||||
|
public String getContentType() { return null; }
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() { return bytes.length == 0; }
|
||||||
|
@Override
|
||||||
|
public long getSize() { return bytes.length; }
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() throws IOException { return bytes; }
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException { return Files.newInputStream(path); }
|
||||||
|
@Override
|
||||||
|
public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
|
||||||
|
Files.copy(path, dest.toPath());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
package com.ruoyi.common.utils.sign;
|
package com.ruoyi.common.utils.sign;
|
||||||
|
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.security.interfaces.RSAPublicKey;
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -16,25 +21,29 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class RSAUtils {
|
public class RSAUtils {
|
||||||
|
|
||||||
// RSA最⼤加密明⽂⼤⼩
|
// RSA最大加密明文大小
|
||||||
private static final int MAX_ENCRYPT_BLOCK = 117;
|
private static final int MAX_ENCRYPT_BLOCK = 117;
|
||||||
// 不仅可以使⽤DSA算法,同样也可以使⽤RSA算法做数字签名
|
// 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名
|
||||||
private static final String KEY_ALGORITHM = "RSA";
|
private static final String KEY_ALGORITHM = "RSA";
|
||||||
|
|
||||||
public static String encryptByPrivateKey(String str, String privateKey) throws Exception {
|
public static String encryptByPrivateKey(String str, String privateKey)
|
||||||
|
throws InvalidKeySpecException, NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, java.security.InvalidKeyException,
|
||||||
|
javax.crypto.IllegalBlockSizeException, javax.crypto.BadPaddingException, UnsupportedEncodingException {
|
||||||
// base64编码的公钥
|
// base64编码的公钥
|
||||||
byte[] keyBytes = decryptBASE64(privateKey);
|
byte[] keyBytes = decryptBASE64(privateKey);
|
||||||
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
|
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM)
|
||||||
|
.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
|
||||||
// RSA加密
|
// RSA加密
|
||||||
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
|
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, priKey);
|
cipher.init(Cipher.ENCRYPT_MODE, priKey);
|
||||||
byte[] data = str.getBytes("UTF-8");
|
byte[] data = str.getBytes("UTF-8");
|
||||||
// 加密时超过117字节就报错。为此采⽤分段加密的办法来加密
|
// 加密时超过117字节就报错。为此采用分段加密的办法来加密
|
||||||
byte[] enBytes = null;
|
byte[] enBytes = null;
|
||||||
for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
|
for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
|
||||||
// 注意要使⽤2的倍数,否则会出现加密后的内容再解密时为乱码
|
// 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
|
||||||
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
|
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
|
||||||
enBytes = ArrayUtils.addAll(enBytes, doFinal);
|
enBytes = ArrayUtils.addAll(enBytes, doFinal);
|
||||||
}
|
}
|
||||||
|
|
@ -43,15 +52,22 @@ public class RSAUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String encryptBASE64(byte[] data) {
|
private static String encryptBASE64(byte[] data) {
|
||||||
return new String(Base64.encode(data));
|
return new String(Base64.encodeBase64(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] decryptBASE64(String data) {
|
private static byte[] decryptBASE64(String data) {
|
||||||
return Base64.decode(data);
|
return Base64.decodeBase64(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify signature
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static boolean verifySign(JSONObject params, String publickey) {
|
public static boolean verifySign(JSONObject params, String publickey) {
|
||||||
String platSign = params.getStr("signature"); // sign
|
String platSign = params.getStr("signature"); // sign
|
||||||
|
log.info("signature:" + platSign);
|
||||||
List<String> paramNameList = new ArrayList<>();
|
List<String> paramNameList = new ArrayList<>();
|
||||||
for (String key : params.keySet()) {
|
for (String key : params.keySet()) {
|
||||||
if (!"signature".equals(key)) {
|
if (!"signature".equals(key)) {
|
||||||
|
|
@ -64,13 +80,14 @@ public class RSAUtils {
|
||||||
String name = paramNameList.get(i);
|
String name = paramNameList.get(i);
|
||||||
stringBuilder.append(params.getStr(name));
|
stringBuilder.append(params.getStr(name));
|
||||||
}
|
}
|
||||||
|
log.info("keys:" + stringBuilder);
|
||||||
String decryptSign = "";
|
String decryptSign = "";
|
||||||
try {
|
try {
|
||||||
decryptSign = publicDecrypt(platSign, getPublicKey(publickey)
|
decryptSign = publicDecrypt(platSign, getPublicKey(publickey));
|
||||||
);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(e.toString());
|
log.error("Error decrypting signature", e);
|
||||||
}
|
}
|
||||||
|
log.info("decryptSign:" + decryptSign);
|
||||||
if (!stringBuilder.toString().equalsIgnoreCase(decryptSign)) {
|
if (!stringBuilder.toString().equalsIgnoreCase(decryptSign)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -89,11 +106,12 @@ public class RSAUtils {
|
||||||
stringBuilder.append(createMap.get(key)); // 拼接参数
|
stringBuilder.append(createMap.get(key)); // 拼接参数
|
||||||
}
|
}
|
||||||
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
|
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
|
||||||
|
log.info("keyStr:" + keyStr);
|
||||||
String signedStr = "";
|
String signedStr = "";
|
||||||
try {
|
try {
|
||||||
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
|
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(e.toString());
|
log.error("Error encrypting signature", e);
|
||||||
}
|
}
|
||||||
return signedStr;
|
return signedStr;
|
||||||
}
|
}
|
||||||
|
|
@ -110,47 +128,70 @@ public class RSAUtils {
|
||||||
stringBuilder.append(createMap.get(key)); // 拼接参数
|
stringBuilder.append(createMap.get(key)); // 拼接参数
|
||||||
}
|
}
|
||||||
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
|
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
|
||||||
|
log.info("keyStr:" + keyStr);
|
||||||
String signedStr = "";
|
String signedStr = "";
|
||||||
try {
|
try {
|
||||||
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
|
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println(e.toString());
|
log.error("Error encrypting signature", e);
|
||||||
}
|
}
|
||||||
return signedStr;
|
return signedStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* private key encryption
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param privateKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
|
public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||||
return Base64.encode(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
|
return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Exception encountered while encry pting string [" + data + "]", e);
|
throw new RuntimeException("Exception encountered while encrypting string [" + data + "]", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* public key decryption
|
||||||
|
*/
|
||||||
public static String publicDecrypt(String data, RSAPublicKey publicKey) {
|
public static String publicDecrypt(String data, RSAPublicKey publicKey) {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||||
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decode(data), publicKey.getModulus().bitLength()), "UTF-8");
|
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), "UTF-8");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("An exception was encountered whil e decrypting the string[" + data + "]", e);
|
throw new RuntimeException("An exception was encountered while decrypting the string[" + data + "]", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
|
/**
|
||||||
|
* get private key
|
||||||
|
*
|
||||||
|
* @param privateKey key string (base64 encoded)
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
//Get the private key object through the PKCS#8 encoded Key instruction
|
//Get the private key object through the PKCS#8 encoded Key instruction
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
|
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
|
||||||
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
|
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
|
/**
|
||||||
|
* get the public key
|
||||||
|
*
|
||||||
|
* @param publicKey key string (base64 encoded)
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
//Get the public key object through the X509 encoded Key instruction
|
//Get the public key object through the X509 encoded Key instruction
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(publicKey));
|
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
|
||||||
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
|
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +220,7 @@ public class RSAUtils {
|
||||||
offSet = i * maxBlock;
|
offSet = i * maxBlock;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("An exception occurred when encryp ting and decrypting data whose threshold is [" + maxBlock + "]", e);
|
throw new RuntimeException("An exception occurred when encrypting and decrypting data whose threshold is [" + maxBlock + "]", e);
|
||||||
}
|
}
|
||||||
byte[] resultDatas = out.toByteArray();
|
byte[] resultDatas = out.toByteArray();
|
||||||
IOUtils.closeQuietly(out);
|
IOUtils.closeQuietly(out);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
package com.ruoyi.ai.domain;
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
import com.ruoyi.common.annotation.Excel;
|
||||||
import com.ruoyi.common.core.domain.BaseEntity;
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 余额使用记录对象 ai_balance_change_record
|
* 余额使用记录对象 ai_balance_change_record
|
||||||
|
|
@ -24,13 +22,23 @@ public class AiBalanceChangeRecord extends BaseEntity {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 主键ID */
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/** 删除标志(0代表存在 2代表删除) */
|
/**
|
||||||
|
* 删除标志(0代表存在 2代表删除)
|
||||||
|
*/
|
||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联订单号
|
||||||
|
*/
|
||||||
|
@Excel(name = "关联订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户ID
|
* 用户ID
|
||||||
*/
|
*/
|
||||||
|
|
@ -42,19 +50,27 @@ public class AiBalanceChangeRecord extends BaseEntity {
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String uuid;
|
private String uuid;
|
||||||
|
|
||||||
/** 操作类型:0-充值 1-返佣 2-充值赠送 3-体验金赠送 4-体验金回收 5-图生图 6-一键换脸 7-快捷生图 8-快捷生视频 9-退款 10-系统操作 */
|
/**
|
||||||
|
* 操作类型:0-充值 1-返佣 2-充值赠送 3-体验金赠送 4-体验金回收 5-图生图 6-一键换脸 7-快捷生图 8-快捷生视频 9-退款 10-系统操作
|
||||||
|
*/
|
||||||
@Excel(name = "操作类型")
|
@Excel(name = "操作类型")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
/** 变更金额 */
|
/**
|
||||||
|
* 变更金额
|
||||||
|
*/
|
||||||
@Excel(name = "变更金额")
|
@Excel(name = "变更金额")
|
||||||
private BigDecimal changeAmount;
|
private BigDecimal changeAmount;
|
||||||
|
|
||||||
/** 变更后金额 */
|
/**
|
||||||
|
* 变更后金额
|
||||||
|
*/
|
||||||
@Excel(name = "变更后金额")
|
@Excel(name = "变更后金额")
|
||||||
private BigDecimal resultAmount;
|
private BigDecimal resultAmount;
|
||||||
|
|
||||||
/** 用户昵称 */
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
@Excel(name = "用户昵称")
|
@Excel(name = "用户昵称")
|
||||||
private String nickname;
|
private String nickname;
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,28 @@ public class AiOrder extends BaseEntity {
|
||||||
@Excel(name = "是否置顶:N-否 Y-是")
|
@Excel(name = "是否置顶:N-否 Y-是")
|
||||||
private String isTop;
|
private String isTop;
|
||||||
|
|
||||||
|
/** 生成模式:text-to-video 或 image-to-video */
|
||||||
|
@Excel(name = "生成模式")
|
||||||
|
private String mode;
|
||||||
|
|
||||||
|
/** 视频时长(秒) */
|
||||||
|
@Excel(name = "视频时长")
|
||||||
|
private Integer duration;
|
||||||
|
|
||||||
|
/** 分辨率(如 720p, 1080p) */
|
||||||
|
@Excel(name = "分辨率")
|
||||||
|
private String resolution;
|
||||||
|
|
||||||
|
/** 宽高比(如 16:9, 9:16) */
|
||||||
|
@Excel(name = "宽高比")
|
||||||
|
private String ratio;
|
||||||
|
|
||||||
|
/** 火山/方舟模型 endpoint */
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
/** 视频类订单:本次提交的完整参数 JSON(提示词、模型、时长等) */
|
||||||
|
private String videoParams;
|
||||||
|
|
||||||
/** 首帧图片 */
|
/** 首帧图片 */
|
||||||
private String img1;
|
private String img1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
package com.ruoyi.ai.domain;
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
|
||||||
import com.ruoyi.common.core.domain.BaseEntity;
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
/**
|
/**
|
||||||
* 返佣记录对象 ai_rebate_record
|
* 返佣记录对象 ai_rebate_record
|
||||||
*
|
*
|
||||||
|
|
@ -23,22 +22,38 @@ public class AiRebateRecord extends BaseEntity {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 主键ID */
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/** 删除标志(0代表存在 2代表删除) */
|
/**
|
||||||
|
* 关联订单号
|
||||||
|
*/
|
||||||
|
@Excel(name = "关联订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志(0代表存在 2代表删除)
|
||||||
|
*/
|
||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
/** 上级ID */
|
/**
|
||||||
|
* 上级ID
|
||||||
|
*/
|
||||||
@Excel(name = "上级ID")
|
@Excel(name = "上级ID")
|
||||||
private Long superiorId;
|
private Long superiorId;
|
||||||
|
|
||||||
/** 下级ID */
|
/**
|
||||||
|
* 下级ID
|
||||||
|
*/
|
||||||
@Excel(name = "下级ID")
|
@Excel(name = "下级ID")
|
||||||
private Long subordinateId;
|
private Long subordinateId;
|
||||||
|
|
||||||
/** 返佣金额 */
|
/**
|
||||||
|
* 返佣金额
|
||||||
|
*/
|
||||||
@Excel(name = "返佣金额")
|
@Excel(name = "返佣金额")
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
package com.ruoyi.ai.domain;
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
import com.ruoyi.common.annotation.Excel;
|
|
||||||
import com.ruoyi.common.core.domain.BaseEntity;
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
/**
|
/**
|
||||||
* 体验金领取记录对象 ai_sample_amount_record
|
* 体验金领取记录对象 ai_sample_amount_record
|
||||||
*
|
*
|
||||||
|
|
@ -25,22 +24,38 @@ public class AiSampleAmountRecord extends BaseEntity {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 主键ID */
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/** 删除标志(0代表存在 2代表删除) */
|
/**
|
||||||
|
* 关联订单号
|
||||||
|
*/
|
||||||
|
@Excel(name = "关联订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志(0代表存在 2代表删除)
|
||||||
|
*/
|
||||||
private String delFlag;
|
private String delFlag;
|
||||||
|
|
||||||
/** 用户ID */
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
@Excel(name = "用户ID")
|
@Excel(name = "用户ID")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
/** 体验金额 */
|
/**
|
||||||
|
* 体验金额
|
||||||
|
*/
|
||||||
@Excel(name = "体验金额")
|
@Excel(name = "体验金额")
|
||||||
private BigDecimal sampleAmount;
|
private BigDecimal sampleAmount;
|
||||||
|
|
||||||
/** 回收时间 */
|
/**
|
||||||
|
* 回收时间
|
||||||
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@Excel(name = "回收时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
@Excel(name = "回收时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date recycleTime;
|
private Date recycleTime;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ public class AiTemplate extends BaseEntity {
|
||||||
@Excel(name = "模版图片URL")
|
@Excel(name = "模版图片URL")
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
|
|
||||||
|
/** AI类型 */
|
||||||
|
@Excel(name = "AI类型")
|
||||||
|
private Long aiId;
|
||||||
|
|
||||||
/** 状态:0-禁用,1-启用 */
|
/** 状态:0-禁用,1-启用 */
|
||||||
@Excel(name = "状态:0-禁用,1-启用")
|
@Excel(name = "状态:0-禁用,1-启用")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,10 @@ public class ByteBodyReq {
|
||||||
@JsonProperty("content")
|
@JsonProperty("content")
|
||||||
private List<ContentItem> content;
|
private List<ContentItem> content;
|
||||||
|
|
||||||
|
private Integer duration;
|
||||||
|
private String resolution;
|
||||||
|
private String ratio;
|
||||||
|
private Integer seed;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ public class ContentItem {
|
||||||
@JsonProperty("image_url")
|
@JsonProperty("image_url")
|
||||||
private ImageUrl imageUrl;
|
private ImageUrl imageUrl;
|
||||||
|
|
||||||
|
@JsonProperty("video_url")
|
||||||
|
private ImageUrl videoUrl;
|
||||||
|
|
||||||
|
@JsonProperty("audio_url")
|
||||||
|
private ImageUrl audioUrl;
|
||||||
|
|
||||||
@JsonProperty("role")
|
@JsonProperty("role")
|
||||||
private String role;
|
private String role;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ public class JinShaBodyReq {
|
||||||
|
|
||||||
private String notify_url;
|
private String notify_url;
|
||||||
|
|
||||||
private String return_url;
|
|
||||||
|
|
||||||
private String sign;
|
private String sign;
|
||||||
|
private String cardname;
|
||||||
|
private String cardno;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 档位ID
|
* 档位ID
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM支付回调请求
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class VmCallBackReq {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户号
|
||||||
|
*/
|
||||||
|
private String mchNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用ID
|
||||||
|
*/
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户订单号
|
||||||
|
*/
|
||||||
|
private String mchOrderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付订单号
|
||||||
|
*/
|
||||||
|
private String payOrderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付方式
|
||||||
|
*/
|
||||||
|
private String wayCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付金额,单位分
|
||||||
|
*/
|
||||||
|
private Integer amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 货币代码
|
||||||
|
*/
|
||||||
|
private String currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付接口编码
|
||||||
|
*/
|
||||||
|
private String ifCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品标题
|
||||||
|
*/
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品描述
|
||||||
|
*/
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态:0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已撤销, 5-已退款, 6-订单关闭
|
||||||
|
* 文档字段名:state
|
||||||
|
*/
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端IP(可选)
|
||||||
|
*/
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道订单号(可选)
|
||||||
|
*/
|
||||||
|
private String channelOrderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道错误码(可选)
|
||||||
|
*/
|
||||||
|
private String errCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道错误描述(可选)
|
||||||
|
*/
|
||||||
|
private String errMsg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展参数,回调时会原样返回
|
||||||
|
*/
|
||||||
|
private String extParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单创建时间,13位时间戳
|
||||||
|
*/
|
||||||
|
private Long createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单支付成功时间,13位时间戳(可选)
|
||||||
|
*/
|
||||||
|
private Long successTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知请求时间,13位时间戳。文档字段名:reqTime
|
||||||
|
*/
|
||||||
|
private Long reqTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名值
|
||||||
|
*/
|
||||||
|
private String sign;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名类型(文档回调示例无此字段,不参与签名)
|
||||||
|
*/
|
||||||
|
private String signType;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM卡信息
|
||||||
|
* 当wayCode为BUZHI_VM时,需要在extParam中传入此信息的JSON字符串
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class VmCardInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信用卡卡号
|
||||||
|
*/
|
||||||
|
private String number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CVC
|
||||||
|
*/
|
||||||
|
private String cvc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期年(四位数字,如:2027)
|
||||||
|
*/
|
||||||
|
private String expYear;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期月(两位数,如:11)
|
||||||
|
*/
|
||||||
|
private String expMonth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信用卡持有者电子邮件
|
||||||
|
*/
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持卡人名字
|
||||||
|
*/
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持卡人姓氏
|
||||||
|
*/
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国家: 双字母 ISO 国家代码(两位字母)
|
||||||
|
*/
|
||||||
|
private String country;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM支付请求
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class VmPayReq {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 档位ID
|
||||||
|
*/
|
||||||
|
private Long gearId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM卡信息(可选,如果为null则需要在extParam中传入)
|
||||||
|
*/
|
||||||
|
private VmCardInfo vmCardInfo;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM统一下单请求
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class VmUnifiedOrderReq {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户号
|
||||||
|
*/
|
||||||
|
private String mchNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用ID
|
||||||
|
*/
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户订单号
|
||||||
|
*/
|
||||||
|
private String mchOrderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付方式,如:BUZHI_VM(国际VM卡支付)
|
||||||
|
*/
|
||||||
|
private String wayCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付金额,单位分
|
||||||
|
*/
|
||||||
|
private Integer amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 货币代码,三位货币代码,人民币:cny 美元:USD
|
||||||
|
*/
|
||||||
|
private String currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端IPV4地址
|
||||||
|
*/
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品标题
|
||||||
|
*/
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品描述
|
||||||
|
*/
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步通知地址
|
||||||
|
*/
|
||||||
|
private String notifyUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单失效时间,单位秒,默认2小时
|
||||||
|
*/
|
||||||
|
private Integer expiredTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特定渠道发起的额外参数,json格式字符串
|
||||||
|
*/
|
||||||
|
private String channelExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户扩展参数,回调时会原样返回
|
||||||
|
* 当wayCode为BUZHI_VM时,需要传入VM卡信息的JSON字符串
|
||||||
|
*/
|
||||||
|
private String extParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求接口时间,13位时间戳
|
||||||
|
*/
|
||||||
|
private Long reqTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口版本号,固定:1.0
|
||||||
|
*/
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名值
|
||||||
|
*/
|
||||||
|
private String sign;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名类型,目前只支持MD5方式
|
||||||
|
*/
|
||||||
|
private String signType;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package com.ruoyi.ai.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM统一下单响应
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class VmUnifiedOrderRes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应码,0表示成功
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应消息
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应数据
|
||||||
|
*/
|
||||||
|
private VmUnifiedOrderData data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应签名(API 返回)
|
||||||
|
*/
|
||||||
|
private String sign;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class VmUnifiedOrderData {
|
||||||
|
/**
|
||||||
|
* 支付订单号(必填)
|
||||||
|
*/
|
||||||
|
private String payOrderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户订单号(必填)
|
||||||
|
*/
|
||||||
|
private String mchOrderNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态(必填)
|
||||||
|
* 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已撤销, 5-已退款, 6-订单关闭
|
||||||
|
*/
|
||||||
|
private Integer orderState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付数据类型(必填)
|
||||||
|
* payUrl-跳转链接的方式
|
||||||
|
* form-表单方式
|
||||||
|
* wxapp-微信支付参数(微信公众号,小程序,app支付时)
|
||||||
|
* aliapp-支付宝app支付参数
|
||||||
|
* ysfapp-云闪付app支付参数
|
||||||
|
* codeUrl-二维码地址
|
||||||
|
* codeImgUrl-二维码图片地址
|
||||||
|
* none-空支付参数
|
||||||
|
*/
|
||||||
|
private String payDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付数据(可选)
|
||||||
|
* 发起支付用到的支付参数,根据payDataType不同而不同
|
||||||
|
* 如果payDataType为payUrl,则此字段为跳转链接
|
||||||
|
*/
|
||||||
|
private String payData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道错误码(可选)
|
||||||
|
*/
|
||||||
|
private String errCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道错误描述(可选)
|
||||||
|
*/
|
||||||
|
private String errMsg;
|
||||||
|
|
||||||
|
// 以下字段为兼容旧版本保留
|
||||||
|
/**
|
||||||
|
* 支付链接(兼容字段,优先使用payData)
|
||||||
|
*/
|
||||||
|
private String payUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单号(兼容字段)
|
||||||
|
*/
|
||||||
|
private String orderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付参数(兼容字段)
|
||||||
|
*/
|
||||||
|
private String payParams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,5 +22,8 @@ public interface AiOrderMapper extends BaseMapper<AiOrder> {
|
||||||
|
|
||||||
AiOrder getAiOrderByResult(@Param("result") String result);
|
AiOrder getAiOrderByResult(@Param("result") String result);
|
||||||
|
|
||||||
|
/** 门户视频:按火山任务 id 查单(result 可能已被替换为成品 URL,故兼查 video_params.volcTaskId) */
|
||||||
|
AiOrder getAiOrderByPortalVideoTask(@Param("taskId") String taskId);
|
||||||
|
|
||||||
BigDecimal getSumAmountByUserId(@Param("userId") String userId);
|
BigDecimal getSumAmountByUserId(@Param("userId") String userId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.ruoyi.common.core.domain.entity.AiUser;
|
import com.ruoyi.common.core.domain.entity.AiUser;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ai-用户信息Mapper接口
|
* ai-用户信息Mapper接口
|
||||||
|
|
@ -22,4 +23,9 @@ public interface AiUserMapper extends BaseMapper<AiUser> {
|
||||||
|
|
||||||
AiUser selectAiUserById(Long id);
|
AiUser selectAiUserById(Long id);
|
||||||
AiUser getUserInfo(Long id);
|
AiUser getUserInfo(Long id);
|
||||||
|
|
||||||
|
int countAiUserByDeptId(@Param("deptId") Long deptId);
|
||||||
|
|
||||||
|
@Update("update ai_user set dept_id = #{deptId}, update_time = sysdate() where id = #{userId}")
|
||||||
|
int updateAiUserDeptId(@Param("userId") Long userId, @Param("deptId") Long deptId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,5 +79,7 @@ public interface IAiOrderService {
|
||||||
|
|
||||||
AiOrder getAiOrderByResult(String result);
|
AiOrder getAiOrderByResult(String result);
|
||||||
|
|
||||||
|
AiOrder getAiOrderByPortalVideoTask(String taskId);
|
||||||
|
|
||||||
BigDecimal getSumAmountByUserId(String userId);
|
BigDecimal getSumAmountByUserId(String userId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,9 @@ public interface IAiUserService {
|
||||||
|
|
||||||
void updateEmail(String email, String code);
|
void updateEmail(String email, String code);
|
||||||
|
|
||||||
void addUserBalance(Long userId, BigDecimal amount, int recharge);
|
void addUserBalance(String orderNo, Long userId, BigDecimal amount, int recharge);
|
||||||
|
|
||||||
void addUserBalance(Long userId, BigDecimal amount, int recharge, String remark);
|
void addUserBalance(String orderNo, Long userId, BigDecimal amount, int recharge, String remark);
|
||||||
|
|
||||||
AiUser getUserByEmail(String email);
|
AiUser getUserByEmail(String email);
|
||||||
|
|
||||||
|
|
@ -99,9 +99,19 @@ public interface IAiUserService {
|
||||||
|
|
||||||
int updatePassword(AiUser aiUser);
|
int updatePassword(AiUser aiUser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门下 AI 用户数量(用于删除部门前校验)
|
||||||
|
*/
|
||||||
|
int countAiUserByDeptId(Long deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分配 AI 用户归属部门,deptId 为空则清空
|
||||||
|
*/
|
||||||
|
int updateAiUserDept(Long userId, Long deptId);
|
||||||
|
|
||||||
public void deductSampleAmount(String userId);
|
public void deductSampleAmount(String userId);
|
||||||
|
|
||||||
void handleRebate(Long userId, BigDecimal rechargeAmount);
|
void handleRebate(String orderNo, Long userId, BigDecimal rechargeAmount);
|
||||||
|
|
||||||
String getRebateConfig();
|
String getRebateConfig();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.ruoyi.ai.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门户用户火山方舟 API Key:取自所属二级部门 {@code sys_dept.byte_api_key},带 Redis 缓存。
|
||||||
|
*/
|
||||||
|
public interface IByteDeptApiKeyService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis 键:{userId}_byte_api_key,优先读缓存,再读库;均无有效 key 则抛出业务异常。
|
||||||
|
*/
|
||||||
|
String resolveVolcApiKey(Long aiUserId);
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.ruoyi.ai.service;
|
||||||
|
|
||||||
import com.ruoyi.ai.domain.ByteBodyReq;
|
import com.ruoyi.ai.domain.ByteBodyReq;
|
||||||
import com.ruoyi.ai.domain.ByteBodyRes;
|
import com.ruoyi.ai.domain.ByteBodyRes;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
|
||||||
public interface IByteService {
|
public interface IByteService {
|
||||||
|
|
||||||
|
|
@ -16,12 +17,31 @@ public interface IByteService {
|
||||||
ByteBodyRes imgToImg(ByteBodyReq req) throws Exception;
|
ByteBodyRes imgToImg(ByteBodyReq req) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 首尾帧图生视频
|
* 首尾帧图生视频(使用全局配置的 Ark API Key)
|
||||||
*/
|
*/
|
||||||
ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception;
|
ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频生成任务创建(指定 Ark API Key,门户按部门密钥调用)
|
||||||
|
*/
|
||||||
|
ByteBodyRes imgToVideo(ByteBodyReq req, String arkApiKey) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载视频
|
* 下载视频
|
||||||
*/
|
*/
|
||||||
ByteBodyRes uploadVideo(String id) throws Exception;
|
ByteBodyRes uploadVideo(String id) throws Exception;
|
||||||
|
|
||||||
|
ByteBodyRes uploadVideo(String id, String arkApiKey) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消视频生成任务
|
||||||
|
*/
|
||||||
|
AjaxResult cancelVideoTask(String id) throws Exception;
|
||||||
|
|
||||||
|
AjaxResult cancelVideoTask(String id, String arkApiKey) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 查询视频生成任务列表(火山 list 文档),返回原始 JSON 字符串
|
||||||
|
*/
|
||||||
|
String listVideoGenerationTasks(int pageNum, int pageSize, String arkApiKey) throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.ruoyi.ai.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 汇率服务接口
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
public interface IExchangeRateService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取汇率
|
||||||
|
*
|
||||||
|
* @param fromCurrency 源货币代码(如:CNY)
|
||||||
|
* @param toCurrency 目标货币代码(如:PHP)
|
||||||
|
* @return 汇率值
|
||||||
|
* @throws Exception 获取汇率失败时抛出异常
|
||||||
|
*/
|
||||||
|
BigDecimal getExchangeRate(String fromCurrency, String toCurrency) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换金额
|
||||||
|
*
|
||||||
|
* @param amount 原始金额
|
||||||
|
* @param fromCurrency 源货币代码(如:CNY)
|
||||||
|
* @param toCurrency 目标货币代码(如:PHP)
|
||||||
|
* @return 转换后的金额
|
||||||
|
* @throws Exception 获取汇率失败时抛出异常
|
||||||
|
*/
|
||||||
|
BigDecimal convertAmount(BigDecimal amount, String fromCurrency, String toCurrency) throws Exception;
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ public interface IJinShaService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付
|
* 支付
|
||||||
|
* @param gearId 档位ID
|
||||||
*/
|
*/
|
||||||
PayResVO jinShaPay(Long gearId) throws Exception;
|
PayResVO jinShaPay(Long gearId) throws Exception;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.ruoyi.ai.service;
|
||||||
|
|
||||||
|
import com.ruoyi.ai.domain.PayResVO;
|
||||||
|
import com.ruoyi.ai.domain.VmCallBackReq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM支付服务接口
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
public interface IVmService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM支付
|
||||||
|
*
|
||||||
|
* @param gearId 档位ID
|
||||||
|
* @param vmCardInfo VM卡信息(可选,如果为null则需要在extParam中传入)
|
||||||
|
* @param clientIp 客户端IP地址
|
||||||
|
* @return 支付结果
|
||||||
|
* @throws Exception 支付异常
|
||||||
|
*/
|
||||||
|
PayResVO vmPay(Long gearId, com.ruoyi.ai.domain.VmCardInfo vmCardInfo, String clientIp) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VM支付回调。文档要求返回字符串 "success" 表示成功,非 success 表示失败,支付中心将再次通知。
|
||||||
|
*
|
||||||
|
* @param req 回调请求
|
||||||
|
* @return "success" 表示处理成功,其他字符串表示失败
|
||||||
|
*/
|
||||||
|
String vmCallBack(VmCallBackReq req);
|
||||||
|
}
|
||||||
|
|
@ -1,29 +1,24 @@
|
||||||
package com.ruoyi.ai.service.impl;
|
package com.ruoyi.ai.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.ruoyi.ai.domain.*;
|
||||||
|
import com.ruoyi.ai.mapper.AiRechargeMapper;
|
||||||
|
import com.ruoyi.ai.service.*;
|
||||||
|
import com.ruoyi.common.constant.BalanceChangerConstants;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import cn.hutool.core.util.NumberUtil;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.ruoyi.ai.domain.*;
|
|
||||||
import com.ruoyi.ai.domain.enums.AiConfigEnum;
|
|
||||||
import com.ruoyi.ai.service.*;
|
|
||||||
import com.ruoyi.common.constant.BalanceChangerConstants;
|
|
||||||
import com.ruoyi.common.core.domain.entity.AiUser;
|
|
||||||
import com.ruoyi.common.utils.DateUtils;
|
|
||||||
import com.ruoyi.common.utils.SecurityUtils;
|
|
||||||
import com.ruoyi.system.domain.SysConfig;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import com.ruoyi.ai.mapper.AiRechargeMapper;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 充值管理Service业务层处理
|
* 充值管理Service业务层处理
|
||||||
*
|
*
|
||||||
|
|
@ -84,6 +79,10 @@ public class AiRechargeServiceImpl implements IAiRechargeService {
|
||||||
public void addRecharge(String orderNo) {
|
public void addRecharge(String orderNo) {
|
||||||
// 设置到账时间
|
// 设置到账时间
|
||||||
AiRecharge aiRecharge = this.getAiRechargeByOrderNum(orderNo);
|
AiRecharge aiRecharge = this.getAiRechargeByOrderNum(orderNo);
|
||||||
|
if (aiRecharge == null) {
|
||||||
|
log.error("支付回调订单不存在,orderNo: {}", orderNo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Date creditedTime = aiRecharge.getCreditedTime();
|
Date creditedTime = aiRecharge.getCreditedTime();
|
||||||
if (creditedTime != null) {
|
if (creditedTime != null) {
|
||||||
log.error("支付回调重复 {}", orderNo);
|
log.error("支付回调重复 {}", orderNo);
|
||||||
|
|
@ -107,12 +106,10 @@ public class AiRechargeServiceImpl implements IAiRechargeService {
|
||||||
aiRecharge.setGiftAmount(amount);
|
aiRecharge.setGiftAmount(amount);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 新增用户余额
|
// 新增用户余额
|
||||||
aiUserService.addUserBalance(aiRecharge.getUserId(), amount, BalanceChangerConstants.RECHARGE);
|
aiUserService.addUserBalance(orderNo, aiRecharge.getUserId(), amount, BalanceChangerConstants.RECHARGE);
|
||||||
// 处理返佣 (根据充值金额计算返佣)
|
// 处理返佣 (根据充值金额计算返佣)
|
||||||
aiUserService.handleRebate(aiRecharge.getUserId(), aiRechargeGiftGear.getRechargeAmount());
|
aiUserService.handleRebate(orderNo, aiRecharge.getUserId(), aiRechargeGiftGear.getRechargeAmount());
|
||||||
// 新增充值统计
|
// 新增充值统计
|
||||||
AiStatistics aiStatistics = new AiStatistics();
|
AiStatistics aiStatistics = new AiStatistics();
|
||||||
aiStatistics.setSource(aiRecharge.getSource());
|
aiStatistics.setSource(aiRecharge.getSource());
|
||||||
|
|
@ -120,14 +117,14 @@ public class AiRechargeServiceImpl implements IAiRechargeService {
|
||||||
statisticsService.saveOrUpdateData(aiStatistics);
|
statisticsService.saveOrUpdateData(aiStatistics);
|
||||||
|
|
||||||
|
|
||||||
// 处理赠送金额
|
// 处理赠送金额
|
||||||
if (isFristRecharge && aiRecharge.getGearId() != null) {
|
if (isFristRecharge && aiRecharge.getGearId() != null) {
|
||||||
// 查询活动是否过期
|
// 查询活动是否过期
|
||||||
boolean isAvailable = aiRechargeGiftService.isActivityAvailable(aiRechargeGiftGear.getRechargeId());
|
boolean isAvailable = aiRechargeGiftService.isActivityAvailable(aiRechargeGiftGear.getRechargeId());
|
||||||
if (isAvailable) {
|
if (isAvailable) {
|
||||||
aiRecharge.setGiveAmount(aiRechargeGiftGear.getGiveAmount());
|
aiRecharge.setGiveAmount(aiRechargeGiftGear.getGiveAmount());
|
||||||
// 新增用户赠送余额
|
// 新增用户赠送余额
|
||||||
aiUserService.addUserBalance(aiRecharge.getUserId(), aiRechargeGiftGear.getGiveAmount(), BalanceChangerConstants.RECHARGE_BONUS);
|
aiUserService.addUserBalance(orderNo, aiRecharge.getUserId(), aiRechargeGiftGear.getGiveAmount(), BalanceChangerConstants.RECHARGE_BONUS);
|
||||||
// 新增充值赠送记录
|
// 新增充值赠送记录
|
||||||
AiRechargeGiftRecord aiRechargeGiftRecord = new AiRechargeGiftRecord();
|
AiRechargeGiftRecord aiRechargeGiftRecord = new AiRechargeGiftRecord();
|
||||||
aiRechargeGiftRecord.setOrderNum(orderNo);
|
aiRechargeGiftRecord.setOrderNum(orderNo);
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,17 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.ai.domain.*;
|
import com.ruoyi.ai.domain.*;
|
||||||
import com.ruoyi.ai.domain.enums.AiConfigEnum;
|
import com.ruoyi.ai.domain.enums.AiConfigEnum;
|
||||||
import com.ruoyi.ai.mapper.AiRechargeMapper;
|
import com.ruoyi.ai.mapper.AiRechargeMapper;
|
||||||
import com.ruoyi.ai.mapper.AiSampleAmountMapper;
|
import com.ruoyi.ai.mapper.AiUserMapper;
|
||||||
import com.ruoyi.ai.mapper.AiSampleAmountRecordMapper;
|
|
||||||
import com.ruoyi.ai.service.*;
|
import com.ruoyi.ai.service.*;
|
||||||
import com.ruoyi.common.constant.BalanceChangerConstants;
|
import com.ruoyi.common.constant.BalanceChangerConstants;
|
||||||
import com.ruoyi.common.constant.HttpStatus;
|
import com.ruoyi.common.constant.HttpStatus;
|
||||||
import com.ruoyi.common.core.domain.entity.AiUser;
|
import com.ruoyi.common.core.domain.entity.AiUser;
|
||||||
import com.ruoyi.ai.mapper.AiUserMapper;
|
|
||||||
import com.ruoyi.common.core.domain.model.LoginAiUser;
|
import com.ruoyi.common.core.domain.model.LoginAiUser;
|
||||||
import com.ruoyi.common.core.domain.model.LoginAiUserBody;
|
import com.ruoyi.common.core.domain.model.LoginAiUserBody;
|
||||||
import com.ruoyi.common.core.domain.model.RegisterAiUserBody;
|
import com.ruoyi.common.core.domain.model.RegisterAiUserBody;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.*;
|
import com.ruoyi.common.utils.*;
|
||||||
import com.ruoyi.common.utils.ip.IpUtils;
|
import com.ruoyi.common.utils.ip.IpUtils;
|
||||||
import com.ruoyi.common.utils.uuid.UUID;
|
|
||||||
import com.ruoyi.system.domain.SysConfig;
|
import com.ruoyi.system.domain.SysConfig;
|
||||||
import com.ruoyi.system.mapper.SysConfigMapper;
|
import com.ruoyi.system.mapper.SysConfigMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -30,7 +27,6 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -70,7 +66,6 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
private AiRechargeMapper aiRechargeMapper;
|
private AiRechargeMapper aiRechargeMapper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询ai-用户信息
|
* 查询ai-用户信息
|
||||||
*
|
*
|
||||||
|
|
@ -137,6 +132,19 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
return aiUserMapper.updateById(aiUser);
|
return aiUserMapper.updateById(aiUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int countAiUserByDeptId(Long deptId) {
|
||||||
|
if (deptId == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return aiUserMapper.countAiUserByDeptId(deptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int updateAiUserDept(Long userId, Long deptId) {
|
||||||
|
return aiUserMapper.updateAiUserDeptId(userId, deptId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除ai-用户信息
|
* 批量删除ai-用户信息
|
||||||
*
|
*
|
||||||
|
|
@ -208,7 +216,7 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
// 新增访客统计
|
// 新增访客统计
|
||||||
AiStatistics aiStatistics = new AiStatistics();
|
AiStatistics aiStatistics = new AiStatistics();
|
||||||
aiStatistics.setSource(registerAiUserBody.getSource());
|
aiStatistics.setSource(registerAiUserBody.getSource());
|
||||||
aiStatistics.setAddUserCount(1l);
|
aiStatistics.setAddUserCount(1L);
|
||||||
aiStatisticsService.saveOrUpdateData(aiStatistics);
|
aiStatisticsService.saveOrUpdateData(aiStatistics);
|
||||||
return aiUser;
|
return aiUser;
|
||||||
}
|
}
|
||||||
|
|
@ -352,13 +360,13 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addUserBalance(Long userId, BigDecimal amount, int recharge) {
|
public void addUserBalance(String orderNo, Long userId, BigDecimal amount, int recharge) {
|
||||||
addUserBalance(userId, amount, recharge, null);
|
addUserBalance(orderNo, userId, amount, recharge, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void addUserBalance(Long userId, BigDecimal amount, int recharge, String remark) {
|
public void addUserBalance(String orderNo, Long userId, BigDecimal amount, int recharge, String remark) {
|
||||||
// 新增余额使用记录
|
// 新增余额使用记录
|
||||||
AiUser aiUser = aiUserMapper.selectById(userId);
|
AiUser aiUser = aiUserMapper.selectById(userId);
|
||||||
AiBalanceChangeRecord balanceChangeRecord = new AiBalanceChangeRecord();
|
AiBalanceChangeRecord balanceChangeRecord = new AiBalanceChangeRecord();
|
||||||
|
|
@ -367,6 +375,7 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
balanceChangeRecord.setChangeAmount(amount);
|
balanceChangeRecord.setChangeAmount(amount);
|
||||||
balanceChangeRecord.setResultAmount(NumberUtil.add(amount, aiUser.getBalance()));
|
balanceChangeRecord.setResultAmount(NumberUtil.add(amount, aiUser.getBalance()));
|
||||||
balanceChangeRecord.setRemark(remark);
|
balanceChangeRecord.setRemark(remark);
|
||||||
|
balanceChangeRecord.setOrderNo(orderNo);
|
||||||
balanceChangeRecordService.insertAiBalanceChangeRecord(balanceChangeRecord);
|
balanceChangeRecordService.insertAiBalanceChangeRecord(balanceChangeRecord);
|
||||||
// 修改用户余额
|
// 修改用户余额
|
||||||
aiUser.setBalance(balanceChangeRecord.getResultAmount());
|
aiUser.setBalance(balanceChangeRecord.getResultAmount());
|
||||||
|
|
@ -396,7 +405,7 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
public int updatePassword(AiUser aiUser) {
|
public int updatePassword(AiUser aiUser) {
|
||||||
aiUser.setPassword(SecurityUtils.encryptPassword(aiUser.getNewPassword()));
|
aiUser.setPassword(SecurityUtils.encryptPassword(aiUser.getNewPassword()));
|
||||||
return aiUserMapper.updateById(aiUser);
|
return aiUserMapper.updateById(aiUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|
@ -410,14 +419,14 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
// if (deductAmount.compareTo(BigDecimal.ZERO) < 0) {
|
// if (deductAmount.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
// deductAmount = new BigDecimal(0);
|
// deductAmount = new BigDecimal(0);
|
||||||
// }
|
// }
|
||||||
addUserBalance(Long.valueOf(userId), NumberUtil.mul(aiSampleAmountRecord.getSampleAmount(), -1), BalanceChangerConstants.EXPERIENCE_GOLD_RECYCLE);
|
addUserBalance(aiSampleAmountRecord.getOrderNo(), Long.valueOf(userId), NumberUtil.mul(aiSampleAmountRecord.getSampleAmount(), -1), BalanceChangerConstants.EXPERIENCE_GOLD_RECYCLE);
|
||||||
// 修改状态已回收
|
// 修改状态已回收
|
||||||
aiSampleAmountRecord.setStatus(1);
|
aiSampleAmountRecord.setStatus(1);
|
||||||
aiSampleAmountRecordService.updateAiSampleAmountRecord(aiSampleAmountRecord);
|
aiSampleAmountRecordService.updateAiSampleAmountRecord(aiSampleAmountRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRebate(Long userId, BigDecimal amount) {
|
public void handleRebate(String orderNo, Long userId, BigDecimal amount) {
|
||||||
AiUser aiUser = aiUserMapper.selectById(userId);
|
AiUser aiUser = aiUserMapper.selectById(userId);
|
||||||
if (aiUser.getSuperiorId() == null) return;
|
if (aiUser.getSuperiorId() == null) return;
|
||||||
// 查询上级用户
|
// 查询上级用户
|
||||||
|
|
@ -428,7 +437,7 @@ public class AiUserServiceImpl implements IAiUserService {
|
||||||
SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique(AiConfigEnum.SYS_INVITE_COMMISSION.getValue());
|
SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique(AiConfigEnum.SYS_INVITE_COMMISSION.getValue());
|
||||||
double decimal = NumberUtil.div(Integer.parseInt(sysConfig.getConfigValue()), 100, 2);
|
double decimal = NumberUtil.div(Integer.parseInt(sysConfig.getConfigValue()), 100, 2);
|
||||||
BigDecimal rebateAmount = NumberUtil.mul(amount, decimal);
|
BigDecimal rebateAmount = NumberUtil.mul(amount, decimal);
|
||||||
addUserBalance(superiorUser.getId(), rebateAmount, BalanceChangerConstants.REBATE);
|
addUserBalance(orderNo, superiorUser.getId(), rebateAmount, BalanceChangerConstants.REBATE);
|
||||||
// 新增返佣记录
|
// 新增返佣记录
|
||||||
AiRebateRecord rebateRecord = new AiRebateRecord();
|
AiRebateRecord rebateRecord = new AiRebateRecord();
|
||||||
rebateRecord.setSuperiorId(superiorUser.getId());
|
rebateRecord.setSuperiorId(superiorUser.getId());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.ruoyi.ai.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.ai.service.IByteDeptApiKeyService;
|
||||||
|
import com.ruoyi.ai.service.IAiUserService;
|
||||||
|
import com.ruoyi.common.core.domain.entity.AiUser;
|
||||||
|
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.system.service.ISysDeptService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
|
||||||
|
|
||||||
|
private static final String NO_DEPT_MSG = "用户未分配部门:请在后台为门户用户设置 ai_user.dept_id(关联 sys_dept.dept_id)";
|
||||||
|
private static final String NO_DEPT_ROW_MSG = "用户所属部门不存在或已删除,请核对 ai_user.dept_id";
|
||||||
|
private static final String NO_API_KEY_MSG = "部门未配置火山 API Key:请在 sys_dept 为用户所在部门(或其上级二级部门)配置 byte_api_key";
|
||||||
|
private static final int CACHE_HOURS = 1;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiUserService aiUserService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISysDeptService sysDeptService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolveVolcApiKey(Long aiUserId) {
|
||||||
|
if (aiUserId == null) {
|
||||||
|
throw new ServiceException(NO_DEPT_MSG);
|
||||||
|
}
|
||||||
|
String cacheKey = aiUserId + "_byte_api_key";
|
||||||
|
String cached = redisCache.getCacheObject(cacheKey);
|
||||||
|
if (StringUtils.isNotEmpty(cached)) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
AiUser aiUser = aiUserService.selectAiUserById(aiUserId);
|
||||||
|
if (aiUser == null || aiUser.getDeptId() == null) {
|
||||||
|
throw new ServiceException(NO_DEPT_MSG);
|
||||||
|
}
|
||||||
|
SysDept userDept = sysDeptService.selectDeptById(aiUser.getDeptId());
|
||||||
|
if (userDept == null) {
|
||||||
|
throw new ServiceException(NO_DEPT_ROW_MSG);
|
||||||
|
}
|
||||||
|
// 优先使用用户直接归属部门的 Key;多数业务把用户挂在分公司(如 101)并在该节点配 byte_api_key
|
||||||
|
String apiKey = trimKey(userDept.getByteApiKey());
|
||||||
|
if (StringUtils.isEmpty(apiKey)) {
|
||||||
|
Long fallbackDeptId = resolveSecondLevelDeptId(userDept);
|
||||||
|
if (!fallbackDeptId.equals(userDept.getDeptId())) {
|
||||||
|
SysDept keyDept = sysDeptService.selectDeptById(fallbackDeptId);
|
||||||
|
if (keyDept != null) {
|
||||||
|
apiKey = trimKey(keyDept.getByteApiKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(apiKey)) {
|
||||||
|
throw new ServiceException(NO_API_KEY_MSG);
|
||||||
|
}
|
||||||
|
redisCache.setCacheObject(cacheKey, apiKey, CACHE_HOURS, TimeUnit.HOURS);
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trimKey(String raw) {
|
||||||
|
return raw == null ? null : raw.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二级部门:祖级路径中,紧接在「一级」(ancestors 第二段)之下的部门节点;
|
||||||
|
* 深度不足时退回用户当前部门。
|
||||||
|
*/
|
||||||
|
private Long resolveSecondLevelDeptId(SysDept userDept) {
|
||||||
|
String ancestors = userDept.getAncestors();
|
||||||
|
if (StringUtils.isEmpty(ancestors)) {
|
||||||
|
return userDept.getDeptId();
|
||||||
|
}
|
||||||
|
String[] parts = ancestors.split(",");
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(parts[2].trim());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return userDept.getDeptId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parts.length == 2) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(parts[1].trim());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return userDept.getDeptId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userDept.getDeptId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,292 @@
|
||||||
|
package com.ruoyi.ai.service.impl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.ruoyi.ai.service.IExchangeRateService;
|
||||||
|
import com.ruoyi.common.utils.http.OkHttpUtils;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 汇率服务实现类
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-01-XX
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class ExchangeRateServiceImpl implements IExchangeRateService {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Value("${exchange-rate.api-url:https://api.currencyfreaks.com/v2.0/rates/latest}")
|
||||||
|
private String apiUrl;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.apikey:}")
|
||||||
|
private String apikey;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.base-currency:USD}")
|
||||||
|
private String baseCurrency;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.enabled:true}")
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.fallback-rate-jinsha:90}")
|
||||||
|
private BigDecimal fallbackRateJinSha;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.fallback-rate-kada:60}")
|
||||||
|
private BigDecimal fallbackRateKaDa;
|
||||||
|
|
||||||
|
@Value("${exchange-rate.fallback-rate-php:90}")
|
||||||
|
private BigDecimal fallbackRatePHP;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal getExchangeRate(String fromCurrency, String toCurrency) throws Exception {
|
||||||
|
if (!enabled) {
|
||||||
|
log.warn("汇率服务未启用,使用默认汇率");
|
||||||
|
return getFallbackRate(toCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建请求URL - 使用 CurrencyFreaks Rates Latest Endpoint
|
||||||
|
String url = buildApiUrl();
|
||||||
|
|
||||||
|
// 构建请求
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
Response response = OkHttpUtils.newCall(request).execute();
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
|
||||||
|
if (response.isSuccessful() && body != null) {
|
||||||
|
String responseBody = body.string();
|
||||||
|
log.debug("汇率API响应: {}", responseBody);
|
||||||
|
|
||||||
|
// 解析响应 - CurrencyFreaks Rates Latest Endpoint 响应格式
|
||||||
|
RatesResponse ratesResponse = objectMapper.readValue(responseBody, RatesResponse.class);
|
||||||
|
|
||||||
|
if (ratesResponse != null && ratesResponse.getRates() != null) {
|
||||||
|
BigDecimal rate = calculateRate(ratesResponse, fromCurrency, toCurrency);
|
||||||
|
if (rate != null && rate.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
log.info("成功获取汇率 {} -> {}: {}", fromCurrency, toCurrency, rate);
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("汇率API请求失败,状态码: {}", response.code());
|
||||||
|
if (body != null) {
|
||||||
|
log.warn("响应内容: {}", body.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取汇率失败,使用备用汇率: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果API调用失败,使用备用汇率
|
||||||
|
return getFallbackRate(toCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal convertAmount(BigDecimal amount, String fromCurrency, String toCurrency) throws Exception {
|
||||||
|
if (amount == null) {
|
||||||
|
throw new IllegalArgumentException("金额不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
log.warn("汇率服务未启用,使用默认汇率计算");
|
||||||
|
BigDecimal rate = getFallbackRate(toCurrency);
|
||||||
|
// 返回完整精度,不限制小数位
|
||||||
|
return amount.multiply(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建请求URL - 使用 CurrencyFreaks Rates Latest Endpoint
|
||||||
|
String url = buildApiUrl();
|
||||||
|
|
||||||
|
// 构建请求
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
Response response = OkHttpUtils.newCall(request).execute();
|
||||||
|
ResponseBody body = response.body();
|
||||||
|
|
||||||
|
if (response.isSuccessful() && body != null) {
|
||||||
|
String responseBody = body.string();
|
||||||
|
// log.debug("汇率API响应: {}", responseBody);
|
||||||
|
|
||||||
|
// 解析响应 - CurrencyFreaks Rates Latest Endpoint 响应格式
|
||||||
|
RatesResponse ratesResponse = objectMapper.readValue(responseBody, RatesResponse.class);
|
||||||
|
|
||||||
|
if (ratesResponse != null && ratesResponse.getRates() != null) {
|
||||||
|
BigDecimal rate = calculateRate(ratesResponse, fromCurrency, toCurrency);
|
||||||
|
if (rate != null && rate.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
BigDecimal convertedAmount = amount.multiply(rate);
|
||||||
|
log.info("成功转换金额 {} {} -> {} {}: {}",
|
||||||
|
amount, fromCurrency, convertedAmount, toCurrency, rate);
|
||||||
|
// 返回完整精度,不限制小数位
|
||||||
|
return convertedAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("汇率API请求失败,状态码: {}", response.code());
|
||||||
|
if (body != null) {
|
||||||
|
log.warn("响应内容: {}", body.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("转换金额失败,使用备用汇率计算: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果API调用失败,使用备用汇率计算(不限制小数位)
|
||||||
|
BigDecimal rate = getFallbackRate(toCurrency);
|
||||||
|
return amount.multiply(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建API URL - 使用 CurrencyFreaks Rates Latest Endpoint
|
||||||
|
*/
|
||||||
|
private String buildApiUrl() {
|
||||||
|
if (apiUrl.contains("currencyfreaks.com") && apiUrl.contains("/rates/latest")) {
|
||||||
|
// 使用 CurrencyFreaks Rates Latest Endpoint 格式
|
||||||
|
// https://api.currencyfreaks.com/v2.0/rates/latest?apikey=YOUR_APIKEY&base=USD
|
||||||
|
StringBuilder urlBuilder = new StringBuilder(apiUrl);
|
||||||
|
urlBuilder.append("?apikey=").append(apikey);
|
||||||
|
urlBuilder.append("&base=").append(baseCurrency.toUpperCase());
|
||||||
|
return urlBuilder.toString();
|
||||||
|
} else {
|
||||||
|
// 兼容其他API格式(向后兼容)
|
||||||
|
return apiUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据汇率响应计算 fromCurrency 到 toCurrency 的汇率
|
||||||
|
*/
|
||||||
|
private BigDecimal calculateRate(RatesResponse ratesResponse, String fromCurrency, String toCurrency) {
|
||||||
|
String base = ratesResponse.getBase();
|
||||||
|
java.util.Map<String, String> rates = ratesResponse.getRates();
|
||||||
|
|
||||||
|
if (rates == null || rates.isEmpty()) {
|
||||||
|
log.warn("汇率响应中 rates 为空");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fromUpper = fromCurrency.toUpperCase();
|
||||||
|
String toUpper = toCurrency.toUpperCase();
|
||||||
|
String baseUpper = base.toUpperCase();
|
||||||
|
|
||||||
|
// 如果源货币就是基准货币
|
||||||
|
if (baseUpper.equals(fromUpper)) {
|
||||||
|
String toRateStr = rates.get(toUpper);
|
||||||
|
if (toRateStr != null) {
|
||||||
|
try {
|
||||||
|
return new BigDecimal(toRateStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("无法解析目标货币 {} 的汇率: {}", toUpper, toRateStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("未找到目标货币 {} 的汇率", toUpper);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果目标货币就是基准货币
|
||||||
|
if (baseUpper.equals(toUpper)) {
|
||||||
|
String fromRateStr = rates.get(fromUpper);
|
||||||
|
if (fromRateStr != null) {
|
||||||
|
try {
|
||||||
|
// 如果目标货币是基准货币,汇率是 1 / fromRate
|
||||||
|
BigDecimal fromRate = new BigDecimal(fromRateStr);
|
||||||
|
if (fromRate.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
return BigDecimal.ONE.divide(fromRate, 10, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("无法解析源货币 {} 的汇率: {}", fromUpper, fromRateStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("未找到源货币 {} 的汇率", fromUpper);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 两个货币都不是基准货币,需要计算
|
||||||
|
String fromRateStr = rates.get(fromUpper);
|
||||||
|
String toRateStr = rates.get(toUpper);
|
||||||
|
|
||||||
|
if (fromRateStr == null) {
|
||||||
|
log.warn("未找到源货币 {} 的汇率", fromUpper);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (toRateStr == null) {
|
||||||
|
log.warn("未找到目标货币 {} 的汇率", toUpper);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
BigDecimal fromRate = new BigDecimal(fromRateStr);
|
||||||
|
BigDecimal toRate = new BigDecimal(toRateStr);
|
||||||
|
|
||||||
|
if (fromRate.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
// 汇率 = toRate / fromRate
|
||||||
|
// 例如:base=USD, from=CNY, to=PHP
|
||||||
|
// USD->CNY = fromRate, USD->PHP = toRate
|
||||||
|
// CNY->PHP = toRate / fromRate
|
||||||
|
return toRate.divide(fromRate, 10, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("无法解析汇率: from={}, to={}", fromRateStr, toRateStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取备用汇率(当API调用失败时使用)
|
||||||
|
*/
|
||||||
|
private BigDecimal getFallbackRate(String toCurrency) {
|
||||||
|
// 根据目标货币返回不同的备用汇率
|
||||||
|
if ("PHP".equalsIgnoreCase(toCurrency)) {
|
||||||
|
// PHP比索,使用配置的PHP备用汇率
|
||||||
|
log.warn("使用PHP备用汇率: {}", fallbackRatePHP);
|
||||||
|
return fallbackRatePHP;
|
||||||
|
}
|
||||||
|
// 其他货币可以扩展,默认使用PHP的备用汇率
|
||||||
|
log.warn("未配置货币 {} 的备用汇率,使用PHP默认汇率 {}", toCurrency, fallbackRatePHP);
|
||||||
|
return fallbackRatePHP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CurrencyFreaks Rates Latest Endpoint 响应对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
static class RatesResponse {
|
||||||
|
@JsonProperty("date")
|
||||||
|
private String date;
|
||||||
|
|
||||||
|
@JsonProperty("base")
|
||||||
|
private String base;
|
||||||
|
|
||||||
|
@JsonProperty("rates")
|
||||||
|
private java.util.Map<String, String> rates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -47,6 +47,10 @@ public class JinShaService implements IJinShaService {
|
||||||
private IAiUserService aiUserService;
|
private IAiUserService aiUserService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IAiRechargeGiftGearService aiRechargeGiftGearService;
|
private IAiRechargeGiftGearService aiRechargeGiftGearService;
|
||||||
|
@Autowired
|
||||||
|
private IAiRechargeGiftService aiRechargeGiftService;
|
||||||
|
@Autowired
|
||||||
|
private IExchangeRateService exchangeRateService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayResVO jinShaPay(Long gearId) throws Exception {
|
public PayResVO jinShaPay(Long gearId) throws Exception {
|
||||||
|
|
@ -57,64 +61,104 @@ public class JinShaService implements IJinShaService {
|
||||||
throw new ServiceException("The gear position does not exist.", -1);
|
throw new ServiceException("The gear position does not exist.", -1);
|
||||||
}
|
}
|
||||||
BigDecimal amount = aiRechargeGiftGear.getRechargeAmount();
|
BigDecimal amount = aiRechargeGiftGear.getRechargeAmount();
|
||||||
amount = amount.multiply(new BigDecimal(90));
|
// 使用汇率服务转换金额(从CNY转换为目标货币,默认INR)
|
||||||
|
amount = exchangeRateService.convertAmount(amount, "USD", "INR");
|
||||||
|
|
||||||
JinShaBodyReq req = new JinShaBodyReq();
|
// 生成订单号
|
||||||
req.setAppid(appId);
|
|
||||||
req.setReturn_url(returnUrl + "/recharge");
|
|
||||||
req.setNotify_url(notifyUrl + "/api/pay/jinsha-callBack");
|
|
||||||
req.setAmount(amount);
|
|
||||||
// 1. 配置请求参数
|
|
||||||
TreeMap<String, Object> params = new TreeMap<>();
|
|
||||||
params.put("appid", req.getAppid());
|
|
||||||
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
String orderNo = dateTime + "js" + uuid;
|
String orderNo = dateTime + "js" + uuid;
|
||||||
params.put("orderno", orderNo);
|
|
||||||
params.put("amount", amount);
|
// 金额转换为number类型(保留两位小数)
|
||||||
params.put("notify_url", req.getNotify_url());
|
String amountStr = String.format("%.2f", amount.doubleValue());
|
||||||
params.put("return_url", req.getReturn_url());
|
|
||||||
StringBuilder append = this.append(params);
|
// 构建回调地址和返回地址
|
||||||
String sign = this.sign(append);
|
String notifyUrlFull = notifyUrl + "/api/pay/jinsha-callBack";
|
||||||
String formData = append + "&sign=" + sign;
|
String returnUrlFull = returnUrl + "/recharge";
|
||||||
// 2. 构建请求体(form-urlencoded 格式)
|
|
||||||
RequestBody requestBody = RequestBody.create(
|
// 1. 配置签名参数(按照key排序:appid, amount, orderno, notify_url, return_url,不包含sign)
|
||||||
MediaType.get("application/x-www-form-urlencoded;charset=utf-8"),
|
TreeMap<String, Object> signParams = new TreeMap<>();
|
||||||
formData
|
signParams.put("appid", appId);
|
||||||
);
|
signParams.put("amount", amountStr);
|
||||||
// 3. 构建 POST 请求
|
signParams.put("orderno", orderNo);
|
||||||
|
signParams.put("notify_url", notifyUrlFull);
|
||||||
|
signParams.put("return_url", returnUrlFull);
|
||||||
|
|
||||||
|
// 2. 生成签名
|
||||||
|
String sign = this.generateSign(signParams);
|
||||||
|
|
||||||
|
// 3. 构建请求参数(使用FormBody.Builder自动处理URL编码)
|
||||||
|
FormBody.Builder formBuilder = new FormBody.Builder();
|
||||||
|
formBuilder.add("appid", appId);
|
||||||
|
formBuilder.add("amount", amountStr);
|
||||||
|
formBuilder.add("orderno", orderNo);
|
||||||
|
formBuilder.add("notify_url", notifyUrlFull);
|
||||||
|
formBuilder.add("return_url", returnUrlFull);
|
||||||
|
formBuilder.add("sign", sign);
|
||||||
|
RequestBody requestBody = formBuilder.build();
|
||||||
|
|
||||||
|
// 4. 构建 POST 请求
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(url + "/api/pay")
|
.url(url + "/api/pay")
|
||||||
// .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
|
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
|
||||||
.post(requestBody)
|
.post(requestBody)
|
||||||
.build();
|
.build();
|
||||||
// 4. 发送请求并获取响应
|
|
||||||
|
// 5. 发送请求并获取响应
|
||||||
Response response = OkHttpUtils.newCall(request).execute();
|
Response response = OkHttpUtils.newCall(request).execute();
|
||||||
ResponseBody body = response.body();
|
ResponseBody body = response.body();
|
||||||
if (null == body) {
|
if (null == body) {
|
||||||
log.error("kada支付请求的响应异常");
|
log.error("jinsha支付请求的响应异常");
|
||||||
throw new Exception("kadapay responsebody is null");
|
throw new Exception("jinshapay responsebody is null");
|
||||||
}
|
}
|
||||||
JinShaBodyRes jinShaBodyRes = objectMapper.readValue(body.string(), JinShaBodyRes.class);
|
String responseBody = body.string();
|
||||||
|
log.info("jinsha支付请求响应: {}", responseBody);
|
||||||
|
JinShaBodyRes jinShaBodyRes = objectMapper.readValue(responseBody, JinShaBodyRes.class);
|
||||||
|
|
||||||
|
// 检查响应code,0表示成功,其他值表示失败
|
||||||
|
Integer code = jinShaBodyRes.getCode();
|
||||||
|
if (code == null || code != 0) {
|
||||||
|
String msg = jinShaBodyRes.getMsg();
|
||||||
|
log.error("jinsha支付请求失败,code: {}, msg: {}", code, msg);
|
||||||
|
throw new ServiceException(msg != null ? msg : "支付请求失败", code != null ? code : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查返回的payurl
|
||||||
|
String payUrl = null;
|
||||||
|
if (jinShaBodyRes.getData() != null) {
|
||||||
|
payUrl = jinShaBodyRes.getData().getPayurl();
|
||||||
|
}
|
||||||
|
if (payUrl == null || payUrl.trim().isEmpty()) {
|
||||||
|
log.error("jinsha支付返回的payurl为空,订单号: {}", orderNo);
|
||||||
|
throw new ServiceException("支付返回的支付链接为空", -1);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建充值管理
|
// 创建充值管理
|
||||||
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
|
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
|
||||||
AiRecharge aiRecharge = new AiRecharge();
|
AiRecharge aiRecharge = new AiRecharge();
|
||||||
aiRecharge.setOrderNum(orderNo);
|
aiRecharge.setOrderNum(orderNo);
|
||||||
aiRecharge.setUserId(SecurityUtils.getAiUserId());
|
aiRecharge.setUserId(SecurityUtils.getAiUserId());
|
||||||
aiRecharge.setAmount(amount);
|
aiRecharge.setAmount(amount);
|
||||||
aiRecharge.setGearId(req.getGearId());
|
aiRecharge.setGearId(gearId);
|
||||||
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
|
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
|
||||||
aiRecharge.setSource(userInfo.getSource());
|
aiRecharge.setSource(userInfo.getSource());
|
||||||
|
AiRechargeGift aiRechargeGift = aiRechargeGiftService.selectAiRechargeGiftById(aiRechargeGiftGear.getRechargeId());
|
||||||
|
aiRecharge.setPayType(aiRechargeGift.getPayType());
|
||||||
|
aiRecharge.setPayUrl(payUrl);
|
||||||
aiRechargeService.insertAiRecharge(aiRecharge);
|
aiRechargeService.insertAiRecharge(aiRecharge);
|
||||||
|
|
||||||
PayResVO payResVO = new PayResVO();
|
PayResVO payResVO = new PayResVO();
|
||||||
payResVO.setOrderNo(orderNo);
|
payResVO.setOrderNo(orderNo);
|
||||||
payResVO.setPayUrl(jinShaBodyRes.getData().getPayurl());
|
payResVO.setPayUrl(payUrl);
|
||||||
return payResVO;
|
return payResVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public AjaxResult jinShaCall(JinShaBodyCall req) {
|
public AjaxResult jinShaCall(JinShaBodyCall req) {
|
||||||
|
log.info("jinsha支付回调,订单号: {}, 状态: {}", req.getOrderno(), req.getStatus());
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
TreeMap<String, Object> params = new TreeMap<>();
|
TreeMap<String, Object> params = new TreeMap<>();
|
||||||
params.put("status", req.getStatus());
|
params.put("status", req.getStatus());
|
||||||
params.put("orderno", req.getOrderno());
|
params.put("orderno", req.getOrderno());
|
||||||
|
|
@ -123,14 +167,58 @@ public class JinShaService implements IJinShaService {
|
||||||
StringBuilder append = this.append(params);
|
StringBuilder append = this.append(params);
|
||||||
String sign = this.sign(append);
|
String sign = this.sign(append);
|
||||||
if (!sign.equals(req.getSign())) {
|
if (!sign.equals(req.getSign())) {
|
||||||
log.error("支付回调签名错误 {}", req.getOrderno());
|
log.error("jinsha支付回调签名错误,订单号: {}, 期望签名: {}, 实际签名: {}",
|
||||||
return AjaxResult.error();
|
req.getOrderno(), sign, req.getSign());
|
||||||
|
return AjaxResult.error("签名验证失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理订单状态
|
||||||
|
// status:0=>处理中, status:1=>成功, status:2=>失败
|
||||||
|
Integer status = req.getStatus();
|
||||||
|
if (status == null) {
|
||||||
|
log.error("jinsha支付回调状态为空,订单号: {}", req.getOrderno());
|
||||||
|
return AjaxResult.error("状态为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == 1) {
|
||||||
|
// 充值成功处理
|
||||||
|
log.info("jinsha支付成功,订单号: {}", req.getOrderno());
|
||||||
|
aiRechargeService.addRecharge(req.getOrderno());
|
||||||
|
return AjaxResult.success();
|
||||||
|
} else if (status == 2) {
|
||||||
|
// 支付失败
|
||||||
|
log.warn("jinsha支付失败,订单号: {}", req.getOrderno());
|
||||||
|
// 这里可以添加失败处理逻辑,比如更新订单状态为失败
|
||||||
|
return AjaxResult.success();
|
||||||
|
} else {
|
||||||
|
// 处理中(status:0)
|
||||||
|
log.info("jinsha支付处理中,订单号: {}", req.getOrderno());
|
||||||
|
return AjaxResult.success();
|
||||||
}
|
}
|
||||||
//充值成功处理
|
|
||||||
aiRechargeService.addRecharge(req.getOrderno());
|
|
||||||
return AjaxResult.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名
|
||||||
|
* 按照文档要求:key=value&key=value&...&secret=secret,然后MD5
|
||||||
|
*/
|
||||||
|
private String generateSign(TreeMap<String, Object> sortedParams) {
|
||||||
|
StringBuilder paramSb = new StringBuilder();
|
||||||
|
for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
// 拼接格式:key=value&
|
||||||
|
paramSb.append(key).append("=").append(value).append("&");
|
||||||
|
}
|
||||||
|
// 最后添加secret
|
||||||
|
paramSb.append("secret=").append(secret);
|
||||||
|
String signSource = paramSb.toString();
|
||||||
|
log.debug("jinsha签名源字符串: {}", signSource);
|
||||||
|
return Md5Utils.hash(signSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旧的签名方法(回调使用)
|
||||||
|
*/
|
||||||
private StringBuilder append(TreeMap<String, Object> sortedParams) {
|
private StringBuilder append(TreeMap<String, Object> sortedParams) {
|
||||||
StringBuilder paramSb = new StringBuilder();
|
StringBuilder paramSb = new StringBuilder();
|
||||||
for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
|
for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ public class KaDaService implements IKaDaService {
|
||||||
private IAiRechargeService aiRechargeService;
|
private IAiRechargeService aiRechargeService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IAiRechargeGiftService aiRechargeGiftService;
|
private IAiRechargeGiftService aiRechargeGiftService;
|
||||||
|
@Autowired
|
||||||
|
private IExchangeRateService exchangeRateService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -65,7 +67,8 @@ public class KaDaService implements IKaDaService {
|
||||||
throw new ServiceException("The gear position does not exist.", -1);
|
throw new ServiceException("The gear position does not exist.", -1);
|
||||||
}
|
}
|
||||||
BigDecimal amount = aiRechargeGiftGear.getRechargeAmount();
|
BigDecimal amount = aiRechargeGiftGear.getRechargeAmount();
|
||||||
amount = amount.multiply(new BigDecimal(60.00));
|
// 使用汇率服务转换金额(从CNY转换为目标货币,默认PHP)
|
||||||
|
amount = exchangeRateService.convertAmount(amount, "USD", "PHP");
|
||||||
|
|
||||||
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
|
||||||
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
|
|
||||||
|
|
@ -60,37 +60,111 @@ public class YuZhouService implements IYuZhouService {
|
||||||
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
String orderNo = dateTime + "yz" + uuid;
|
String orderNo = dateTime + "yz" + uuid;
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
|
||||||
|
|
||||||
|
// 处理用户姓名,如果没有则使用默认值
|
||||||
|
String firstName = "John";
|
||||||
|
String lastName = "Doe";
|
||||||
|
if (userInfo.getNickname() != null && !userInfo.getNickname().trim().isEmpty()) {
|
||||||
|
String[] nameParts = userInfo.getNickname().trim().split("\\s+");
|
||||||
|
if (nameParts.length >= 2) {
|
||||||
|
firstName = nameParts[0];
|
||||||
|
lastName = nameParts[nameParts.length - 1];
|
||||||
|
} else if (nameParts.length == 1) {
|
||||||
|
firstName = nameParts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从用户表获取邮箱
|
||||||
|
String email = userInfo.getEmail();
|
||||||
|
if (email == null || email.trim().isEmpty()) {
|
||||||
|
log.warn("用户邮箱为空 userId: {}, nickname: {}", userInfo.getId(), userInfo.getNickname());
|
||||||
|
// 如果用户邮箱为空,使用默认邮箱格式
|
||||||
|
email = "user" + userInfo.getId() + "@example.com";
|
||||||
|
} else {
|
||||||
|
email = email.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理电话,如果没有则使用默认值
|
||||||
|
String phone = userInfo.getPhone();
|
||||||
|
if (phone == null || phone.trim().isEmpty()) {
|
||||||
|
phone = "1234567890";
|
||||||
|
} else {
|
||||||
|
// 确保电话是10位数字
|
||||||
|
phone = phone.replaceAll("[^0-9]", "");
|
||||||
|
if (phone.length() < 10) {
|
||||||
|
// 如果不足10位,用0补齐
|
||||||
|
phone = String.format("%010d", phone.length() > 0 ? Long.parseLong(phone) : 0);
|
||||||
|
} else if (phone.length() > 10) {
|
||||||
|
// 如果超过10位,取后10位
|
||||||
|
phone = phone.substring(phone.length() - 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建请求参数
|
||||||
Map<String, Object> createMap = new HashMap<>();
|
Map<String, Object> createMap = new HashMap<>();
|
||||||
createMap.put("merchantNo", appId);
|
createMap.put("merchantNo", appId);
|
||||||
createMap.put("orderNo", orderNo);
|
createMap.put("orderNo", orderNo);
|
||||||
createMap.put("money", amount.toString());
|
createMap.put("money", amount.toString());
|
||||||
createMap.put("description", "test");
|
createMap.put("description", "Recharge");
|
||||||
createMap.put("name", "test");
|
createMap.put("email", email);
|
||||||
createMap.put("email", "test@gmail.com");
|
createMap.put("phone", phone);
|
||||||
createMap.put("callbackUrl", callbackUrl + "/api/pay/yuzhou-callBack");
|
createMap.put("notifyUrl", callbackUrl + "/api/pay/yuzhou-callBack");
|
||||||
createMap.put("phone", "7383442114");
|
|
||||||
createMap.put("expiredPeriod", "1440");
|
createMap.put("expiredPeriod", "1440");
|
||||||
createMap.put("redirectUrl", redirectUrl + "/recharge");
|
createMap.put("currency", "USD");
|
||||||
|
createMap.put("firstName", firstName);
|
||||||
|
createMap.put("lastName", lastName);
|
||||||
|
createMap.put("street", "123 Main Street");
|
||||||
|
createMap.put("city", "New York");
|
||||||
|
createMap.put("state", "NY");
|
||||||
|
createMap.put("country", userInfo.getCountry() != null && !userInfo.getCountry().trim().isEmpty() ? userInfo.getCountry() : "US");
|
||||||
|
createMap.put("postcode", "10001");
|
||||||
|
|
||||||
|
// 生成签名(注意:签名时不包括signature字段)
|
||||||
String signedStr = RSAUtils.getSignStr(createMap, secretKey);
|
String signedStr = RSAUtils.getSignStr(createMap, secretKey);
|
||||||
createMap.put("signature", signedStr);
|
createMap.put("signature", signedStr);
|
||||||
|
|
||||||
String postStr = JSONUtil.toJsonStr(createMap);
|
String postStr = JSONUtil.toJsonStr(createMap);
|
||||||
String response = HttpRequest.post(url + "/gateway/order/US/payIn").header("Content-Type", "application/json")
|
log.info("yuzhou支付请求参数: {}", postStr);
|
||||||
.body(postStr).execute().body();
|
|
||||||
|
// 调用新的接口地址
|
||||||
|
String response = HttpRequest.post(url + "/gateway/US/payIn")
|
||||||
|
.header("Content-Type", "application/json; charset=utf-8")
|
||||||
|
.body(postStr)
|
||||||
|
.execute()
|
||||||
|
.body();
|
||||||
|
|
||||||
|
log.info("yuzhou支付响应: {}", response);
|
||||||
|
|
||||||
JSONObject returnObj = JSONUtil.parseObj(response);
|
JSONObject returnObj = JSONUtil.parseObj(response);
|
||||||
Integer code = returnObj.getInt("code");
|
Integer code = returnObj.getInt("code");
|
||||||
if (200 != code) {
|
if (200 != code) {
|
||||||
log.error("yuzhou支付请求的响应异常 {}", returnObj);
|
String msg = returnObj.getStr("msg");
|
||||||
throw new Exception("yuzhoupay responsebody is null");
|
log.error("yuzhou支付请求的响应异常 code: {}, msg: {}, response: {}", code, msg, returnObj);
|
||||||
|
throw new ServiceException("yuzhoupay request failed: " + (msg != null ? msg : "unknown error"), code);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject data = returnObj.getJSONObject("data");
|
JSONObject data = returnObj.getJSONObject("data");
|
||||||
|
if (data == null) {
|
||||||
|
log.error("yuzhou支付响应数据为空: {}", returnObj);
|
||||||
|
throw new ServiceException("yuzhoupay response data is null", -1);
|
||||||
|
}
|
||||||
|
|
||||||
String money = data.getStr("money");
|
String money = data.getStr("money");
|
||||||
String payUrl = data.getStr("url");
|
String payUrl = data.getStr("url");
|
||||||
|
String platformOrderNo = data.getStr("platformOrderNo");
|
||||||
|
|
||||||
|
if (payUrl == null || payUrl.trim().isEmpty()) {
|
||||||
|
log.error("yuzhou支付URL为空: {}", data);
|
||||||
|
throw new ServiceException("yuzhoupay url is null", -1);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建充值管理
|
// 创建充值管理
|
||||||
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
|
|
||||||
AiRecharge aiRecharge = new AiRecharge();
|
AiRecharge aiRecharge = new AiRecharge();
|
||||||
aiRecharge.setOrderNum(orderNo);
|
aiRecharge.setOrderNum(orderNo);
|
||||||
aiRecharge.setUserId(SecurityUtils.getAiUserId());
|
aiRecharge.setUserId(SecurityUtils.getAiUserId());
|
||||||
aiRecharge.setAmount(new BigDecimal(money));
|
aiRecharge.setAmount(new BigDecimal(money != null ? money : amount.toString()));
|
||||||
aiRecharge.setGearId(gearId);
|
aiRecharge.setGearId(gearId);
|
||||||
aiRecharge.setSource(userInfo.getSource());
|
aiRecharge.setSource(userInfo.getSource());
|
||||||
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
|
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
|
||||||
|
|
@ -98,6 +172,7 @@ public class YuZhouService implements IYuZhouService {
|
||||||
aiRecharge.setPayType(aiRechargeGift.getPayType());
|
aiRecharge.setPayType(aiRechargeGift.getPayType());
|
||||||
aiRecharge.setPayUrl(payUrl);
|
aiRecharge.setPayUrl(payUrl);
|
||||||
aiRechargeService.insertAiRecharge(aiRecharge);
|
aiRechargeService.insertAiRecharge(aiRecharge);
|
||||||
|
|
||||||
PayResVO payResVO = new PayResVO();
|
PayResVO payResVO = new PayResVO();
|
||||||
payResVO.setOrderNo(orderNo);
|
payResVO.setOrderNo(orderNo);
|
||||||
payResVO.setPayUrl(payUrl);
|
payResVO.setPayUrl(payUrl);
|
||||||
|
|
@ -106,31 +181,60 @@ public class YuZhouService implements IYuZhouService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String callBack(Map<String, Object> map) throws Exception {
|
public String callBack(Map<String, Object> map) throws Exception {
|
||||||
int code = Integer.parseInt(map.get("status").toString());
|
log.info("yuzhou支付回调接收参数: {}", map);
|
||||||
String orderNo = map.get("orderNo").toString();
|
|
||||||
if (10 != code) {
|
// 验证必要参数
|
||||||
log.error("yuzhou支付失败 {}", orderNo);
|
if (map == null || map.isEmpty()) {
|
||||||
return null;
|
log.error("yuzhou支付回调参数为空");
|
||||||
|
return "FAIL";
|
||||||
}
|
}
|
||||||
// Map<String, Object> createMap = new HashMap<>();
|
|
||||||
// createMap.put("orderNo", orderNo);
|
Object statusObj = map.get("status");
|
||||||
// createMap.put("platOrderNo", map.get("platOrderNo"));
|
Object orderNoObj = map.get("orderNo");
|
||||||
// createMap.put("money", map.get("money"));
|
|
||||||
// createMap.put("fee", map.get("fee"));
|
if (statusObj == null || orderNoObj == null) {
|
||||||
// createMap.put("status", map.get("status"));
|
log.error("yuzhou支付回调缺少必要参数: status={}, orderNo={}", statusObj, orderNoObj);
|
||||||
// createMap.put("message", map.get("message"));
|
return "FAIL";
|
||||||
String signature = map.get("signature").toString();
|
}
|
||||||
|
|
||||||
|
int status = Integer.parseInt(statusObj.toString());
|
||||||
|
String orderNo = orderNoObj.toString();
|
||||||
|
|
||||||
|
log.info("yuzhou支付回调 orderNo: {}, status: {}", orderNo, status);
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
Object signatureObj = map.get("signature");
|
||||||
|
if (signatureObj == null) {
|
||||||
|
log.error("yuzhou支付回调缺少签名参数 orderNo: {}", orderNo);
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
|
||||||
JSONObject entries = JSONUtil.parseObj(map);
|
JSONObject entries = JSONUtil.parseObj(map);
|
||||||
boolean b = RSAUtils.verifySign(entries, publicKey);
|
boolean verifyResult = RSAUtils.verifySign(entries, publicKey);
|
||||||
if (!b) {
|
if (!verifyResult) {
|
||||||
log.error("yuzhou支付回调签名错误 {}", orderNo);
|
log.error("yuzhou支付回调签名验证失败 orderNo: {}", orderNo);
|
||||||
log.error("yuzhou支付回调签名 {}", signature);
|
log.error("yuzhou支付回调签名: {}", signatureObj);
|
||||||
log.error("yuzhou支付回调报文 {}", map);
|
log.error("yuzhou支付回调完整报文: {}", map);
|
||||||
return null;
|
return "FAIL";
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("yuzhou支付回调签名验证成功 orderNo: {}", orderNo);
|
||||||
|
|
||||||
|
// 判断订单状态,10表示成功
|
||||||
|
if (10 != status) {
|
||||||
|
log.warn("yuzhou支付订单状态非成功 orderNo: {}, status: {}", orderNo, status);
|
||||||
|
return "SUCCESS"; // 即使失败也返回SUCCESS,避免重复回调
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值成功处理
|
||||||
|
try {
|
||||||
|
aiRechargeService.addRecharge(orderNo);
|
||||||
|
log.info("yuzhou支付回调处理成功 orderNo: {}", orderNo);
|
||||||
|
return "SUCCESS";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("yuzhou支付回调处理失败 orderNo: {}", orderNo, e);
|
||||||
|
return "FAIL";
|
||||||
}
|
}
|
||||||
//充值成功处理
|
|
||||||
aiRechargeService.addRecharge(orderNo);
|
|
||||||
return "SUCCESS";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="source" column="source" />
|
<result property="source" column="source" />
|
||||||
<result property="text" column="text" />
|
<result property="text" column="text" />
|
||||||
<result property="isTop" column="is_top" />
|
<result property="isTop" column="is_top" />
|
||||||
|
<result property="img1" column="img1" />
|
||||||
|
<result property="img2" column="img2" />
|
||||||
|
<result property="mode" column="mode" />
|
||||||
|
<result property="duration" column="duration" />
|
||||||
|
<result property="resolution" column="resolution" />
|
||||||
|
<result property="ratio" column="ratio" />
|
||||||
|
<result property="model" column="model" />
|
||||||
|
<result property="videoParams" column="video_params" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectAiOrderVo">
|
<sql id="selectAiOrderVo">
|
||||||
select ao.id, ao.del_flag, ao.create_by, ao.create_time, ao.update_by, ao.update_time, ao.remark, ao.order_num, ao.user_id, ao.type, ao.amount, ao.result, ao.status, ao.source, ao.text, ao.is_top, au.user_id uuid from ai_order ao
|
select ao.id, ao.del_flag, ao.create_by, ao.create_time, ao.update_by, ao.update_time, ao.remark, ao.order_num, ao.user_id, ao.type, ao.amount, ao.result, ao.status, ao.source, ao.text, ao.is_top, ao.img1, ao.img2, ao.mode, ao.duration, ao.resolution, ao.ratio, ao.model, ao.video_params, au.user_id uuid from ai_order ao
|
||||||
left join ai_user au on au.id = ao.user_id
|
left join ai_user au on au.id = ao.user_id
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
|
@ -61,6 +69,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
where result = #{result}
|
where result = #{result}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="getAiOrderByPortalVideoTask" resultMap="AiOrderResult">
|
||||||
|
<include refid="selectAiOrderVo"/>
|
||||||
|
where ao.del_flag = '0'
|
||||||
|
and ao.type = '21'
|
||||||
|
and (
|
||||||
|
ao.result = #{taskId}
|
||||||
|
or (
|
||||||
|
ao.video_params is not null and ao.video_params != ''
|
||||||
|
and JSON_VALID(ao.video_params)
|
||||||
|
and JSON_UNQUOTE(JSON_EXTRACT(ao.video_params, '$.volcTaskId')) = #{taskId}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="getSumAmountByUserId" resultType="java.math.BigDecimal">
|
<select id="getSumAmountByUserId" resultType="java.math.BigDecimal">
|
||||||
SELECT COALESCE(sum(amount), 0) from ai_order where user_id = #{userId}
|
SELECT COALESCE(sum(amount), 0) from ai_order where user_id = #{userId}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -118,7 +141,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="amount != null">amount = #{amount},</if>
|
<if test="amount != null">amount = #{amount},</if>
|
||||||
<if test="result != null">result = #{result},</if>
|
<if test="result != null">result = #{result},</if>
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="source != null">status = #{source},</if>
|
<if test="source != null">source = #{source},</if>
|
||||||
<if test="text != null">text = #{text},</if>
|
<if test="text != null">text = #{text},</if>
|
||||||
<if test="isTop != null">is_top = #{isTop},</if>
|
<if test="isTop != null">is_top = #{isTop},</if>
|
||||||
</trim>
|
</trim>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="chineseContent" column="chinese_content" />
|
<result property="chineseContent" column="chinese_content" />
|
||||||
<result property="englishContent" column="english_content" />
|
<result property="englishContent" column="english_content" />
|
||||||
<result property="imageUrl" column="image_url" />
|
<result property="imageUrl" column="image_url" />
|
||||||
|
<result property="aiId" column="ai_id" />
|
||||||
<result property="status" column="status" />
|
<result property="status" column="status" />
|
||||||
<result property="remark" column="remark" />
|
<result property="remark" column="remark" />
|
||||||
<result property="createTime" column="create_time" />
|
<result property="createTime" column="create_time" />
|
||||||
|
|
@ -20,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectAiTemplateVo">
|
<sql id="selectAiTemplateVo">
|
||||||
select id, name, chinese_content, english_content, image_url, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
|
select id, name, chinese_content, english_content, image_url, ai_id, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectAiTemplateList" parameterType="AiTemplate" resultMap="AiTemplateResult">
|
<select id="selectAiTemplateList" parameterType="AiTemplate" resultMap="AiTemplateResult">
|
||||||
|
|
@ -30,6 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="chineseContent != null and chineseContent != ''"> and chinese_content = #{chineseContent}</if>
|
<if test="chineseContent != null and chineseContent != ''"> and chinese_content = #{chineseContent}</if>
|
||||||
<if test="englishContent != null and englishContent != ''"> and english_content = #{englishContent}</if>
|
<if test="englishContent != null and englishContent != ''"> and english_content = #{englishContent}</if>
|
||||||
<if test="imageUrl != null and imageUrl != ''"> and image_url = #{imageUrl}</if>
|
<if test="imageUrl != null and imageUrl != ''"> and image_url = #{imageUrl}</if>
|
||||||
|
<if test="aiId != null and aiId != ''"> and ai_id = #{aiId}</if>
|
||||||
<if test="status != null "> and status = #{status}</if>
|
<if test="status != null "> and status = #{status}</if>
|
||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -46,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="chineseContent != null">chinese_content,</if>
|
<if test="chineseContent != null">chinese_content,</if>
|
||||||
<if test="englishContent != null">english_content,</if>
|
<if test="englishContent != null">english_content,</if>
|
||||||
<if test="imageUrl != null">image_url,</if>
|
<if test="imageUrl != null">image_url,</if>
|
||||||
|
<if test="aiId != null">ai_id,</if>
|
||||||
<if test="status != null">status,</if>
|
<if test="status != null">status,</if>
|
||||||
<if test="remark != null">remark,</if>
|
<if test="remark != null">remark,</if>
|
||||||
<if test="createTime != null">create_time,</if>
|
<if test="createTime != null">create_time,</if>
|
||||||
|
|
@ -59,6 +62,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="chineseContent != null">#{chineseContent},</if>
|
<if test="chineseContent != null">#{chineseContent},</if>
|
||||||
<if test="englishContent != null">#{englishContent},</if>
|
<if test="englishContent != null">#{englishContent},</if>
|
||||||
<if test="imageUrl != null">#{imageUrl},</if>
|
<if test="imageUrl != null">#{imageUrl},</if>
|
||||||
|
<if test="aiId != null">#{aiId},</if>
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="remark != null">#{remark},</if>
|
<if test="remark != null">#{remark},</if>
|
||||||
<if test="createTime != null">#{createTime},</if>
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
|
@ -76,6 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="chineseContent != null">chinese_content = #{chineseContent},</if>
|
<if test="chineseContent != null">chinese_content = #{chineseContent},</if>
|
||||||
<if test="englishContent != null">english_content = #{englishContent},</if>
|
<if test="englishContent != null">english_content = #{englishContent},</if>
|
||||||
<if test="imageUrl != null">image_url = #{imageUrl},</if>
|
<if test="imageUrl != null">image_url = #{imageUrl},</if>
|
||||||
|
<if test="aiId != null">ai_id = #{aiId},</if>
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="createTime != null">create_time = #{createTime},</if>
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="source" column="source" />
|
<result property="source" column="source" />
|
||||||
<result property="ip" column="ip" />
|
<result property="ip" column="ip" />
|
||||||
<result property="country" column="country" />
|
<result property="country" column="country" />
|
||||||
|
<result property="deptId" column="dept_id" />
|
||||||
|
<result property="deptName" column="dept_name" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectAiUserVo">
|
<sql id="selectAiUserVo">
|
||||||
select id, del_flag, create_by, create_time, update_by, update_time, remark, username, nickname, gender, avatar, phone, password, openid, status, email, birthday, invitation_code, payment_url, login_time, balance, superior_id, user_id, source, ip, country from ai_user
|
select id, del_flag, create_by, create_time, update_by, update_time, remark, username, nickname, gender, avatar, phone, password, openid, status, email, birthday, invitation_code, payment_url, login_time, balance, superior_id, user_id, source, ip, country, dept_id from ai_user
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectAiUserList" parameterType="AiUser" resultMap="AiUserResult">
|
<select id="selectAiUserList" parameterType="AiUser" resultMap="AiUserResult">
|
||||||
select u.id, u.del_flag, u.create_by, u.create_time, u.update_by, u.update_time, u.remark, u.username, u.nickname, u.gender, u.avatar, u.phone, u.password, u.openid, u.status, u.email, u.birthday, u.invitation_code, u.payment_url, u.login_time, u.balance, u.superior_id, u.user_id, u.source, u.ip, u.country, au.user_id superiorUuid, au.username superiorName from ai_user u
|
select u.id, u.del_flag, u.create_by, u.create_time, u.update_by, u.update_time, u.remark, u.username, u.nickname, u.gender, u.avatar, u.phone, u.password, u.openid, u.status, u.email, u.birthday, u.invitation_code, u.payment_url, u.login_time, u.balance, u.superior_id, u.user_id, u.source, u.ip, u.country, u.dept_id, d.dept_name, au.user_id superiorUuid, au.username superiorName from ai_user u
|
||||||
left join ai_user au on au.id = u.superior_id
|
left join ai_user au on au.id = u.superior_id
|
||||||
|
left join sys_dept d on d.dept_id = u.dept_id and d.del_flag = '0'
|
||||||
<where>
|
<where>
|
||||||
<if test="nickname != null and nickname != ''"> and u.nickname like concat('%', #{nickname}, '%')</if>
|
<if test="nickname != null and nickname != ''"> and u.nickname like concat('%', #{nickname}, '%')</if>
|
||||||
<if test="username != null and username != ''"> and u.username like concat('%', #{username}, '%')</if>
|
<if test="username != null and username != ''"> and u.username like concat('%', #{username}, '%')</if>
|
||||||
|
|
@ -63,19 +66,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="superiorId != null "> and u.superior_id = #{superiorId}</if>
|
<if test="superiorId != null "> and u.superior_id = #{superiorId}</if>
|
||||||
<if test="id != null "> and u.id = #{id}</if>
|
<if test="id != null "> and u.id = #{id}</if>
|
||||||
<if test="source != null "> and u.source = #{source}</if>
|
<if test="source != null "> and u.source = #{source}</if>
|
||||||
|
<if test="deptId != null "> and u.dept_id = #{deptId}</if>
|
||||||
</where>
|
</where>
|
||||||
order by u.id desc
|
order by u.id desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectAiUserById" parameterType="Long" resultMap="AiUserResult">
|
<select id="selectAiUserById" parameterType="Long" resultMap="AiUserResult">
|
||||||
<include refid="selectAiUserVo"/>
|
select u.id, u.del_flag, u.create_by, u.create_time, u.update_by, u.update_time, u.remark, u.username, u.nickname, u.gender, u.avatar, u.phone, u.password, u.openid, u.status, u.email, u.birthday, u.invitation_code, u.payment_url, u.login_time, u.balance, u.superior_id, u.user_id, u.source, u.ip, u.country, u.dept_id, d.dept_name
|
||||||
where id = #{id}
|
from ai_user u
|
||||||
|
left join sys_dept d on d.dept_id = u.dept_id and d.del_flag = '0'
|
||||||
|
where u.id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getUserInfo" parameterType="Long" resultMap="AiUserResult">
|
<select id="getUserInfo" parameterType="Long" resultMap="AiUserResult">
|
||||||
select del_flag, create_by, create_time, update_by, update_time, remark, username, nickname, gender, avatar, phone, password, openid, status, email, birthday, invitation_code, payment_url, login_time, balance, superior_id, user_id, source from ai_user
|
select del_flag, create_by, create_time, update_by, update_time, remark, username, nickname, gender, avatar, phone, password, openid, status, email, birthday, invitation_code, payment_url, login_time, balance, superior_id, user_id, source, ip, country, dept_id from ai_user
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="countAiUserByDeptId" resultType="int">
|
||||||
|
select count(1) from ai_user where del_flag = '0' and dept_id = #{deptId}
|
||||||
|
</select>
|
||||||
<select id="selectPasswordById" resultType="java.lang.String">
|
<select id="selectPasswordById" resultType="java.lang.String">
|
||||||
select password from ai_user where id = #{id}
|
select password from ai_user where id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -108,6 +118,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="source != null">source,</if>
|
<if test="source != null">source,</if>
|
||||||
<if test="ip != null">ip,</if>
|
<if test="ip != null">ip,</if>
|
||||||
<if test="country != null">country,</if>
|
<if test="country != null">country,</if>
|
||||||
|
<if test="deptId != null">dept_id,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
|
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
|
||||||
|
|
@ -134,6 +145,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="userId != null">#{userId},</if>
|
<if test="userId != null">#{userId},</if>
|
||||||
<if test="source != null">#{source},</if>
|
<if test="source != null">#{source},</if>
|
||||||
<if test="country != null">#{country},</if>
|
<if test="country != null">#{country},</if>
|
||||||
|
<if test="deptId != null">#{deptId},</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -163,8 +175,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="superiorId != null">superior_id = #{superiorId},</if>
|
<if test="superiorId != null">superior_id = #{superiorId},</if>
|
||||||
<if test="userId != null">user_id = #{userId},</if>
|
<if test="userId != null">user_id = #{userId},</if>
|
||||||
<if test="source != null">source = #{source},</if>
|
<if test="source != null">source = #{source},</if>
|
||||||
<if test="ip != null">source = #{ip},</if>
|
<if test="ip != null">ip = #{ip},</if>
|
||||||
<if test="country != null">country = #{country},</if>
|
<if test="country != null">country = #{country},</if>
|
||||||
|
<if test="deptId != null">dept_id = #{deptId},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="leader" column="leader" />
|
<result property="leader" column="leader" />
|
||||||
<result property="phone" column="phone" />
|
<result property="phone" column="phone" />
|
||||||
<result property="email" column="email" />
|
<result property="email" column="email" />
|
||||||
|
<result property="byteApiKey" column="byte_api_key" />
|
||||||
<result property="status" column="status" />
|
<result property="status" column="status" />
|
||||||
<result property="delFlag" column="del_flag" />
|
<result property="delFlag" column="del_flag" />
|
||||||
<result property="parentName" column="parent_name" />
|
<result property="parentName" column="parent_name" />
|
||||||
|
|
@ -23,7 +24,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectDeptVo">
|
<sql id="selectDeptVo">
|
||||||
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
|
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.byte_api_key, d.status, d.del_flag, d.create_by, d.create_time
|
||||||
from sys_dept d
|
from sys_dept d
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
|
<select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
|
||||||
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
|
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.byte_api_key, d.status,
|
||||||
(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
|
(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
|
||||||
from sys_dept d
|
from sys_dept d
|
||||||
where d.dept_id = #{deptId}
|
where d.dept_id = #{deptId}
|
||||||
|
|
@ -101,6 +102,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="leader != null and leader != ''">leader,</if>
|
<if test="leader != null and leader != ''">leader,</if>
|
||||||
<if test="phone != null and phone != ''">phone,</if>
|
<if test="phone != null and phone != ''">phone,</if>
|
||||||
<if test="email != null and email != ''">email,</if>
|
<if test="email != null and email != ''">email,</if>
|
||||||
|
<if test="byteApiKey != null">byte_api_key,</if>
|
||||||
<if test="status != null">status,</if>
|
<if test="status != null">status,</if>
|
||||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||||
create_time
|
create_time
|
||||||
|
|
@ -113,6 +115,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="leader != null and leader != ''">#{leader},</if>
|
<if test="leader != null and leader != ''">#{leader},</if>
|
||||||
<if test="phone != null and phone != ''">#{phone},</if>
|
<if test="phone != null and phone != ''">#{phone},</if>
|
||||||
<if test="email != null and email != ''">#{email},</if>
|
<if test="email != null and email != ''">#{email},</if>
|
||||||
|
<if test="byteApiKey != null">#{byteApiKey},</if>
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||||
sysdate()
|
sysdate()
|
||||||
|
|
@ -129,6 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="leader != null">leader = #{leader},</if>
|
<if test="leader != null">leader = #{leader},</if>
|
||||||
<if test="phone != null">phone = #{phone},</if>
|
<if test="phone != null">phone = #{phone},</if>
|
||||||
<if test="email != null">email = #{email},</if>
|
<if test="email != null">email = #{email},</if>
|
||||||
|
<if test="byteApiKey != null">byte_api_key = #{byteApiKey},</if>
|
||||||
<if test="status != null and status != ''">status = #{status},</if>
|
<if test="status != null and status != ''">status = #{status},</if>
|
||||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||||
update_time = sysdate()
|
update_time = sysdate()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue