diff --git a/src/main/java/com/kexue/skills/controller/AccountController.java b/src/main/java/com/kexue/skills/controller/AccountController.java index 34c36db..a4f5a84 100644 --- a/src/main/java/com/kexue/skills/controller/AccountController.java +++ b/src/main/java/com/kexue/skills/controller/AccountController.java @@ -1,5 +1,6 @@ package com.kexue.skills.controller; +import cn.dev33.satoken.stp.StpUtil; import com.github.pagehelper.PageInfo; import com.kexue.skills.annotation.RequireAuth; import com.kexue.skills.annotation.RequireRole; @@ -10,12 +11,18 @@ import com.kexue.skills.entity.dto.AccountDto; import com.kexue.skills.entity.dto.TokenConsumptionDto; import com.kexue.skills.entity.dto.GiftBalanceDto; import com.kexue.skills.service.AccountService; +import com.kexue.skills.service.SysUserService; +import com.kexue.skills.entity.SysUser; +import com.kexue.skills.exception.BizException; +import com.kexue.skills.common.CacheManager; +import java.math.BigDecimal; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; /** @@ -35,6 +42,9 @@ public class AccountController { @Resource private AccountService accountService; + @Resource + private SysUserService sysUserService; + /** * 分页查询 * @@ -67,7 +77,7 @@ public class AccountController { * @param accountId 主键 * @return 单条数据 */ - @Operation(summary = "通过ID查询账户", description = "通过ID查询账户") + @Operation(summary = "通过查询账户", description = "通过ID查询账户") @PostMapping("/queryById/{accountId}") @RequireAuth public CommonResult queryById(@Parameter(description = "账户ID") @PathVariable("accountId") Long accountId) { @@ -75,15 +85,15 @@ public class AccountController { } /** - * 通过用户ID查询单条数据 + * 通过当前登录用户ID查询账户 * - * @param userId 用户ID * @return 单条数据 */ - @Operation(summary = "通过用户ID查询账户", description = "通过用户ID查询账户") - @PostMapping("/queryByUserId/{userId}") + @Operation(summary = "通过当前登录用户查询账户", description = "通过当前登录用户查询账户") + @PostMapping("/currentAccount") @RequireAuth - public CommonResult queryByUserId(@Parameter(description = "用户ID") @PathVariable("userId") Long userId) { + public CommonResult currentAccount() { + Long userId = Long.parseLong(StpUtil.getLoginId().toString()); return CommonResult.success(this.accountService.queryByUserId(userId)); } @@ -111,12 +121,12 @@ public class AccountController { * 减少账户余额(token消费转换) * * @param tokenConsumptionDto token消费转换参数 - * @return 影响行数 + * @return 消耗的金额 */ @Operation(summary = "减少账户余额(token消费转换)", description = "减少账户余额(token消费转换)") @PostMapping("/reduceBalanceWithToken") @RequireAuth - public CommonResult reduceBalanceWithToken( @RequestBody TokenConsumptionDto tokenConsumptionDto) { + public CommonResult reduceBalanceWithToken( @RequestBody TokenConsumptionDto tokenConsumptionDto) { return CommonResult.success(this.accountService.reduceBalanceWithToken(tokenConsumptionDto)); } @@ -143,17 +153,17 @@ public class AccountController { } /** - * 获取用户交易记录 + * 获取当前登录用户交易记录 * - * @param userId 用户ID * @return 交易记录列表 */ - @Operation(summary = "获取用户交易记录", description = "获取用户交易记录") + @Operation(summary = "获取当前登录用户交易记录", description = "获取当前登录用户交易记录") @PostMapping("/getTransactions") @RequireAuth - public CommonResult> getTransactions( - @RequestBody java.util.Map params) { - Long userId = Long.valueOf(params.get("userId").toString()); + public CommonResult> getTransactions() { + Long userId = Long.parseLong(StpUtil.getLoginId().toString()); return CommonResult.success(this.accountService.getTransactions(userId)); } + + } \ No newline at end of file diff --git a/src/main/java/com/kexue/skills/controller/ContentPurchaseController.java b/src/main/java/com/kexue/skills/controller/ContentPurchaseController.java index 2581a88..2f68f47 100644 --- a/src/main/java/com/kexue/skills/controller/ContentPurchaseController.java +++ b/src/main/java/com/kexue/skills/controller/ContentPurchaseController.java @@ -1,5 +1,6 @@ package com.kexue.skills.controller; +import cn.dev33.satoken.stp.StpUtil; import com.github.pagehelper.PageInfo; import com.kexue.skills.annotation.RequireAuth; import com.kexue.skills.common.CommonResult; @@ -73,7 +74,6 @@ public class ContentPurchaseController { /** * 购买内容 * - * @param userId 用户ID * @param contentId 内容ID * @param payType 支付方式:1.余额支付 2.积分支付 * @return 购买结果 @@ -82,16 +82,15 @@ public class ContentPurchaseController { @PostMapping("/purchase") @RequireAuth public CommonResult purchaseContent( - @Parameter(description = "用户ID") @RequestParam("userId") Long userId, @Parameter(description = "内容ID") @RequestParam("contentId") Long contentId, @Parameter(description = "支付方式:1.余额支付 2.积分支付") @RequestParam("payType") Integer payType) { + Long userId = Long.parseLong(StpUtil.getLoginId().toString()); return CommonResult.success(this.contentPurchaseService.purchaseContent(userId, contentId, payType)); } /** * 检查用户是否有权限访问内容 * - * @param userId 用户ID * @param contentId 内容ID * @return 是否有权限 */ @@ -99,8 +98,8 @@ public class ContentPurchaseController { @PostMapping("/checkPermission") @RequireAuth public CommonResult checkAccessPermission( - @Parameter(description = "用户ID") @RequestParam("userId") Long userId, @Parameter(description = "内容ID") @RequestParam("contentId") Long contentId) { + Long userId = Long.parseLong(StpUtil.getLoginId().toString()); boolean hasPermission = this.contentPurchaseService.checkAccessPermission(userId, contentId); return CommonResult.success(hasPermission); } diff --git a/src/main/java/com/kexue/skills/controller/PayController.java b/src/main/java/com/kexue/skills/controller/PayController.java index 05ff942..9400f1c 100644 --- a/src/main/java/com/kexue/skills/controller/PayController.java +++ b/src/main/java/com/kexue/skills/controller/PayController.java @@ -12,7 +12,11 @@ import org.springframework.web.bind.annotation.*; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; +import com.kexue.skills.entity.dto.OrderStatusDto; +import com.kexue.skills.entity.dto.OrderStatusQueryDto; + import java.util.Map; +import java.util.Objects; /** * 支付控制器 @@ -146,4 +150,43 @@ public class PayController { return CommonResult.failed(result.get("message").toString()); } } + + /** + * 查询订单状态 + * @param queryDto 查询参数,包含 orderId 或 orderNo + * @return 订单状态信息 + */ + @Operation(summary = "查询订单状态", description = "根据订单 id 或 orderNo 查询订单状态") + @PostMapping("/queryOrderStatus") + public CommonResult queryOrderStatus(@RequestBody OrderStatusQueryDto queryDto) { + try { + // 检查参数是否有效 + if (Objects.isNull(queryDto.getOrderId()) && Objects.isNull(queryDto.getOrderNo())) { + return CommonResult.failed("请提供 orderId 或 orderNo 参数"); + } + + PaymentOrder order = null; + + // 根据订单 id 查询 + if (queryDto.getOrderId() != null) { + order = paymentOrderService.queryById(queryDto.getOrderId()); + } + // 根据订单号查询 + else if (queryDto.getOrderNo() != null && !queryDto.getOrderNo().trim().isEmpty()) { + order = paymentOrderService.queryByOrderNo(queryDto.getOrderNo()); + } + + if (order == null) { + return CommonResult.failed("订单不存在"); + } + + // 构建响应数据 + OrderStatusDto result = OrderStatusDto.fromPaymentOrder(order); + + return CommonResult.success(result); + } catch (Exception e) { + logger.error("查询订单状态失败", e); + return CommonResult.failed("系统繁忙,请稍后重试"); + } + } } diff --git a/src/main/java/com/kexue/skills/entity/dto/OrderStatusDto.java b/src/main/java/com/kexue/skills/entity/dto/OrderStatusDto.java new file mode 100644 index 0000000..6d11ea4 --- /dev/null +++ b/src/main/java/com/kexue/skills/entity/dto/OrderStatusDto.java @@ -0,0 +1,77 @@ +package com.kexue.skills.entity.dto; + +import com.kexue.skills.entity.PaymentOrder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 订单状态DTO + */ +@Data +public class OrderStatusDto { + + @Schema(description = "订单ID") + private Long orderId; + + @Schema(description = "订单号") + private String orderNo; + + @Schema(description = "状态码") + private Integer status; + + @Schema(description = "状态文本") + private String statusText; + + @Schema(description = "支付金额") + private BigDecimal amount; + + @Schema(description = "支付方式:1.微信 2.支付宝") + private Integer payType; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "支付时间") + private Date payTime; + + @Schema(description = "渠道订单号") + private String channelOrderNo; + + /** + * 从PaymentOrder构建OrderStatusDto + * @param order 支付订单 + * @return 订单状态DTO + */ + public static OrderStatusDto fromPaymentOrder(PaymentOrder order) { + OrderStatusDto dto = new OrderStatusDto(); + dto.setOrderId(order.getOrderId()); + dto.setOrderNo(order.getOrderNo()); + dto.setStatus(order.getStatus()); + dto.setStatusText(getStatusText(order.getStatus())); + dto.setAmount(order.getAmount()); + dto.setPayType(order.getPayType()); + dto.setCreateTime(order.getCreateTime()); + dto.setPayTime(order.getPayTime()); + dto.setChannelOrderNo(order.getChannelOrderNo()); + return dto; + } + + /** + * 获取状态文本 + * @param status 状态码 + * @return 状态文本 + */ + private static String getStatusText(Integer status) { + switch (status) { + case 1: return "待支付"; + case 2: return "已支付"; + case 3: return "支付失败"; + case 4: return "已取消"; + case 5: return "已退款"; + default: return "未知状态"; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/kexue/skills/entity/dto/OrderStatusQueryDto.java b/src/main/java/com/kexue/skills/entity/dto/OrderStatusQueryDto.java new file mode 100644 index 0000000..5de6668 --- /dev/null +++ b/src/main/java/com/kexue/skills/entity/dto/OrderStatusQueryDto.java @@ -0,0 +1,18 @@ +package com.kexue.skills.entity.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 订单状态查询DTO + */ +@Data +public class OrderStatusQueryDto { + + @Schema(description = "订单ID") + private Long orderId; + + @Schema(description = "订单号") + private String orderNo; + +} \ No newline at end of file diff --git a/src/main/java/com/kexue/skills/mapper/PaymentOrderMapper.java b/src/main/java/com/kexue/skills/mapper/PaymentOrderMapper.java index 97f0dc9..5717fa5 100644 --- a/src/main/java/com/kexue/skills/mapper/PaymentOrderMapper.java +++ b/src/main/java/com/kexue/skills/mapper/PaymentOrderMapper.java @@ -47,6 +47,8 @@ public interface PaymentOrderMapper { */ PaymentOrder queryByOrderNo(String orderNo); + PaymentOrder queryByChannelOrderNo(String channelOrderNo); + /** * 新增数据 * diff --git a/src/main/java/com/kexue/skills/service/AccountService.java b/src/main/java/com/kexue/skills/service/AccountService.java index ce92cf9..4b749ba 100644 --- a/src/main/java/com/kexue/skills/service/AccountService.java +++ b/src/main/java/com/kexue/skills/service/AccountService.java @@ -106,9 +106,9 @@ public interface AccountService extends BaseService { /** * 减少账户余额(token消费转换) * @param tokenConsumptionDto token消费 - * + * @return 消耗的金额 */ - int reduceBalanceWithToken(TokenConsumptionDto tokenConsumptionDto); + BigDecimal reduceBalanceWithToken(TokenConsumptionDto tokenConsumptionDto); /** * 增加账户余额(签到奖励token转换) diff --git a/src/main/java/com/kexue/skills/service/PaymentOrderService.java b/src/main/java/com/kexue/skills/service/PaymentOrderService.java index e7c1fbf..efa8a2c 100644 --- a/src/main/java/com/kexue/skills/service/PaymentOrderService.java +++ b/src/main/java/com/kexue/skills/service/PaymentOrderService.java @@ -40,11 +40,19 @@ public interface PaymentOrderService extends BaseService { /** * 通过订单号查询单条数据 * - * @param orderNo 订单号 + * @param orderNo 微信或者支付宝订单号 * @return 实例对象 */ PaymentOrder queryByOrderNo(String orderNo); + /** + * 通过渠道订单号查询单条数据 + * + * @param ChannelOrderNo 渠道订单号 + * @return 订单信息 + */ + PaymentOrder queryByChannelOrderNo(String ChannelOrderNo); + /** * 新增数据 * diff --git a/src/main/java/com/kexue/skills/service/impl/AccountServiceImpl.java b/src/main/java/com/kexue/skills/service/impl/AccountServiceImpl.java index bb21486..481e8e9 100644 --- a/src/main/java/com/kexue/skills/service/impl/AccountServiceImpl.java +++ b/src/main/java/com/kexue/skills/service/impl/AccountServiceImpl.java @@ -182,11 +182,14 @@ public class AccountServiceImpl implements AccountService { // 3. 更新账户余额 if (isWithdrawable) { - account.setWithdrawableBalance(account.getWithdrawableBalance().add(amount)); + BigDecimal withdrawableBalance = account.getWithdrawableBalance() == null ? BigDecimal.ZERO : account.getWithdrawableBalance(); + account.setWithdrawableBalance(withdrawableBalance.add(amount)); } else { - account.setNonWithdrawableBalance(account.getNonWithdrawableBalance().add(amount)); + BigDecimal nonWithdrawableBalance = account.getNonWithdrawableBalance() == null ? BigDecimal.ZERO : account.getNonWithdrawableBalance(); + account.setNonWithdrawableBalance(nonWithdrawableBalance.add(amount)); } - account.setBalance(account.getBalance().add(amount)); + BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance(); + account.setBalance(balance.add(amount)); account.setUpdateTime(new Date()); this.update(account); return 1; @@ -226,7 +229,8 @@ public class AccountServiceImpl implements AccountService { Assert.notNull(account, "账户不存在"); // 2. 检查余额是否足够 - Assert.isTrue(account.getBalance().compareTo(amount) >= 0, "账户余额不足"); + BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance(); + Assert.isTrue(balance.compareTo(amount) >= 0, "账户余额不足"); // 3. 保存交易记录 AccountTransaction transaction = new AccountTransaction(); @@ -251,10 +255,10 @@ public class AccountServiceImpl implements AccountService { /** * 减少账户余额(token消费转换) - * @return 影响行数 + * @return 消耗的金额 */ @Override - public int reduceBalanceWithToken(TokenConsumptionDto dto) { + public BigDecimal reduceBalanceWithToken(TokenConsumptionDto dto) { validate(dto); // 1. 查询账户信息 Long userId = null ;//根据会话ID查询用户ID @@ -289,7 +293,8 @@ public class AccountServiceImpl implements AccountService { BigDecimal amount = BigDecimal.valueOf(totalFee).divide(BigDecimal.valueOf(100)); // 4. 检查余额是否足够 - Assert.isTrue(account.getBalance().compareTo(amount) >= 0, "账户余额不足"); + BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance(); + Assert.isTrue(balance.compareTo(amount) >= 0, "账户余额不足"); // 5. 保存交易记录 AccountTransaction transaction = new AccountTransaction(); @@ -312,13 +317,13 @@ public class AccountServiceImpl implements AccountService { this.accountTransactionMapper.insert(transaction); // 6. 更新账户余额 - return this.accountMapper.updateBalance(userId, amount, 2); + this.accountMapper.updateBalance(userId, amount, 2); + return amount; } void validate(TokenConsumptionDto dto) { Assert.notNull(dto, "参数不能为空"); Assert.notNull(dto.getSessionId(), "会话ID不能为空"); - Assert.notNull(dto.getUserId(), "用户ID不能为空"); Assert.notNull(dto.getQuestion(), "问题不能为空"); Assert.notNull(dto.getModelName(), "模型名称不能为空"); Assert.notNull(dto.getQuestion(), "问题不能为空"); @@ -371,8 +376,10 @@ public class AccountServiceImpl implements AccountService { this.accountTransactionMapper.insert(transaction); // 3. 更新账户余额(签到奖励不可提现) - account.setNonWithdrawableBalance(account.getNonWithdrawableBalance().add(amount)); - account.setBalance(account.getBalance().add(amount)); + BigDecimal nonWithdrawableBalance = account.getNonWithdrawableBalance() == null ? BigDecimal.ZERO : account.getNonWithdrawableBalance(); + account.setNonWithdrawableBalance(nonWithdrawableBalance.add(amount)); + BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance(); + account.setBalance(balance.add(amount)); account.setUpdateTime(new Date()); this.update(account); return 1; @@ -427,6 +434,9 @@ public class AccountServiceImpl implements AccountService { account.setNonWithdrawableBalance(BigDecimal.ZERO); } account.setNonWithdrawableBalance(account.getNonWithdrawableBalance().add(amount)); + if (account.getBalance() == null){ + account.setBalance(BigDecimal.ZERO); + } account.setBalance(account.getBalance().add(amount)); account.setUpdateTime(new Date()); this.update(account); diff --git a/src/main/java/com/kexue/skills/service/impl/CmsContentServiceImpl.java b/src/main/java/com/kexue/skills/service/impl/CmsContentServiceImpl.java index a06c15e..1e1ccc9 100644 --- a/src/main/java/com/kexue/skills/service/impl/CmsContentServiceImpl.java +++ b/src/main/java/com/kexue/skills/service/impl/CmsContentServiceImpl.java @@ -122,22 +122,24 @@ public class CmsContentServiceImpl implements CmsContentService { // 查询包含这些标签的所有 skill List taggedContents = this.cmsContentMapper.getPageList(tagQueryDto); - // 过滤掉已返回的内容 -// List relatedContents = taggedContents.stream() -// .filter(content -> !existingIds.contains(content.getContentId())) -// .collect(Collectors.toList()); - - //如果tag传参,则根据tag再次过滤 + //如果 tag 传参,则根据 tag 再次过滤 if (queryDto.getTagId() != null) { taggedContents = taggedContents.stream().filter(content -> content.getTags().contains(queryDto.getTagId().toString())).toList() ; } - List finalList = new ArrayList<>(taggedContents); + // 过滤掉已返回的内容,避免重复 + List finalList = taggedContents.stream() + .filter(content -> !existingIds.contains(content.getContentId())) + .collect(Collectors.toList()); // 如果有相关 skill,添加到结果中 if (!finalList.isEmpty()) { - // 按 sort 和 create_time 排序 - finalList.sort((a, b) -> { + // 将外层查询结果和内层查询结果合并 + List allContents = new ArrayList<>(list); + allContents.addAll(finalList); + + // 按 view_count 和 create_time 排序 + allContents.sort((a, b) -> { int sortCompare = Integer.compare( a.getViewCount() != null ? a.getViewCount() : 0, b.getViewCount() != null ? b.getViewCount() : 0); @@ -151,8 +153,8 @@ public class CmsContentServiceImpl implements CmsContentService { }); // 计算需要添加的数量,确保总数量不超过 pageSize - PageInfo newPageInfo = new PageInfo<>(memoryPagination(finalList, queryDto.getPageNum(), queryDto.getPageSize())); - newPageInfo.setTotal(finalList.size()); + PageInfo newPageInfo = new PageInfo<>(memoryPagination(allContents, queryDto.getPageNum(), queryDto.getPageSize())); + newPageInfo.setTotal(allContents.size()); pageInfo = newPageInfo; } } diff --git a/src/main/java/com/kexue/skills/service/impl/PayServiceImpl.java b/src/main/java/com/kexue/skills/service/impl/PayServiceImpl.java index 02c7da6..c3bfe5a 100644 --- a/src/main/java/com/kexue/skills/service/impl/PayServiceImpl.java +++ b/src/main/java/com/kexue/skills/service/impl/PayServiceImpl.java @@ -10,6 +10,7 @@ import com.kexue.skills.config.PaymentConfig; import com.kexue.skills.entity.PaymentOrder; import com.kexue.skills.service.PayService; import com.kexue.skills.service.PaymentOrderService; +import com.kexue.skills.service.AccountService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -59,6 +60,9 @@ public class PayServiceImpl implements PayService { @Resource private PaymentOrderService paymentOrderService; + @Resource + private AccountService accountService; + /** * 生成随机字符串 * @return 随机字符串 @@ -492,6 +496,24 @@ public class PayServiceImpl implements PayService { PaymentOrder order = paymentOrderService.queryByOrderNo(orderNo); if (order != null) { paymentOrderService.updateStatus(order.getOrderId(), 2, transactionId); + + // 更新账户余额并添加交易记录 + try { + // 增加账户余额(充值) + accountService.addBalance( + order.getUserId(), + order.getAmount(), + true, // 可提现 + transactionId, + order.getOrderId(), + "recharge", + "微信支付充值" + ); + logger.info("微信支付回调:更新账户余额成功,userId={}, amount={}", order.getUserId(), order.getAmount()); + } catch (Exception e) { + logger.error("微信支付回调:更新账户余额失败", e); + // 继续处理,不影响回调响应 + } } } @@ -616,6 +638,24 @@ public class PayServiceImpl implements PayService { PaymentOrder order = paymentOrderService.queryByOrderNo(orderNo); if (order != null) { paymentOrderService.updateStatus(order.getOrderId(), 2, transactionId); + + // 更新账户余额并添加交易记录 + try { + // 增加账户余额(充值) + accountService.addBalance( + order.getUserId(), + order.getAmount(), + true, // 可提现 + transactionId, + order.getOrderId(), + "recharge", + "支付宝支付充值" + ); + logger.info("支付宝支付回调:更新账户余额成功,userId={}, amount={}", order.getUserId(), order.getAmount()); + } catch (Exception e) { + logger.error("支付宝支付回调:更新账户余额失败", e); + // 继续处理,不影响回调响应 + } } } diff --git a/src/main/java/com/kexue/skills/service/impl/PaymentOrderServiceImpl.java b/src/main/java/com/kexue/skills/service/impl/PaymentOrderServiceImpl.java index 5a02b61..eea0c33 100644 --- a/src/main/java/com/kexue/skills/service/impl/PaymentOrderServiceImpl.java +++ b/src/main/java/com/kexue/skills/service/impl/PaymentOrderServiceImpl.java @@ -86,6 +86,17 @@ public class PaymentOrderServiceImpl implements PaymentOrderService { return this.paymentOrderMapper.queryByOrderNo(orderNo); } + /** + * 通过订单号查询单条数据 + * + * @param channelOrderNo 订单号 + * @return 实例对象 + */ + @Override + public PaymentOrder queryByChannelOrderNo(String channelOrderNo) { + return this.paymentOrderMapper.queryByChannelOrderNo(channelOrderNo); + } + /** * 新增数据 * diff --git a/src/main/java/com/kexue/skills/service/impl/SkillGenServiceImpl.java b/src/main/java/com/kexue/skills/service/impl/SkillGenServiceImpl.java index 17c32ba..731c1b8 100644 --- a/src/main/java/com/kexue/skills/service/impl/SkillGenServiceImpl.java +++ b/src/main/java/com/kexue/skills/service/impl/SkillGenServiceImpl.java @@ -230,11 +230,12 @@ public class SkillGenServiceImpl implements SkillGenService { } } String systemContent = """ - 你是AI技能包设计专家,仅输出【纯YAML文本】,不包含任何多余文字(无解释、无注释、无引言、无结尾)。 + 你是AI技能包设计专家,仅输出【完整的纯YAML文本】,输出内容是完整可解析的技能YAML描述文件,绝非片段,不包含任何多余文字(无解释、无注释、无引言、无结尾)。 ### 一、YAML顶层强制规则(仅一个节点:package) - 1. 顶层只能有 `package` 一个节点,所有信息(名称、版本、目录结构等)均嵌套在 `package` 下 - 2. `package` 节点必含子字段:name、version、description、author、created、tags、structure(缺一不可) + 1. 顶层只能有 package 一个节点,所有信息(名称、版本、目录结构等)均嵌套在 package 下 + 2. package 节点必含子字段:name、version、description、author、created、tags、structure(缺一不可) + 3. 最终必须输出完整闭合的YAML结构,禁止输出残缺片段、部分节点 ### 二、package子字段规范(固定格式) 1. name:技能名称(与用户提供的Skill名称完全一致,不修改) @@ -243,58 +244,70 @@ public class SkillGenServiceImpl implements SkillGenService { 4. author:固定为 "AI技能生成助手" 5. created:格式为 "YYYY-MM-DD"(使用当前日期,如2026-04-01) 6. tags:数组格式,值为用户提供的Skill标签(中文,自动去重,逗号后加空格) - 7. structure:技能包目录树根节点(必为directory类型,统一命名"skills") + 7. structure:技能包目录树,根目录固定为 /,structure下直接编写根目录的一级子文件/子文件夹,禁止重复描述根目录本身 ### 三、structure目录树规则(仅Python脚本,无其他语言) #### 1. 基础必含文件(所有技能通用) - - /skills(根目录,type: directory,path: /skills,format: dir,description: 技能包根目录) - - /skills/skills.md(type: file,path: /skills/skills.md,format: markdown,description: 技能说明文档) + - 根目录 / 下必生成:skills.md 文件,归属父路径 /,文件必须保留后缀名 .md - #### 2. Python脚本目录/文件判断逻辑 - - 若用户提供的“Skill描述”“Skill摘要”中**包含“脚本”“代码”“执行”“运行”“处理”“计算”** 等需执行逻辑的关键词: - 1. 必须新增 /skills/scripts 目录(type: directory,path: /skills/scripts,format: dir,description: Python执行脚本目录) - 2. 必须在该目录下生成 /skills/scripts/main.py(type: file,format: python,description: 技能核心Python脚本) - - 若用户需求中**无任何执行逻辑相关描述**(如纯文档、纯说明类技能):不生成 /skills/scripts 目录,避免冗余 + #### 2. structure核心编写规则 + 1. structure节点下不写根目录/的描述,直接从一级子文件/子文件夹开始编写 + 2. 【path路径强制规则】path只写父级目录路径,不拼接文件名称/目录名称 + - 目录自身名称使用 name 字段体现,目录一律无后缀名 + - 文件自身名称使用 name 字段体现,文件必须保留标准后缀名 + - 示例:scripts目录在根目录下 → path: /,name: scripts + - 示例:skills.md在根目录下 → path: /,name: skills.md + - 示例:main.py在scripts目录下 → path: /scripts,name: main.py + 3. 所有directory/file节点均为根目录/的直接/间接子节点 - #### 3. 节点必含字段 - - directory类型:name、type、path(绝对路径,Unix风格 `/`,如 /skills/scripts)、format(固定"dir")、description、children(空目录写 `children: []`) - - file类型:name、type、path(绝对路径)、format(仅markdown/python两种)、description、content(非空,有实际可用内容) + #### 3. Python脚本目录/文件判断逻辑 + - 若用户提供的“Skill描述”“Skill摘要”中包含“脚本”“代码”“执行”“运行”“处理”“计算”等需执行逻辑的关键词: + 1. 必须新增 scripts 目录,该目录为文件夹,无任何后缀名,path: /,name: scripts + 2. 必须在该目录下固定生成 main.py 文件,文件必须带 .py 后缀,不允许省略或修改文件名 + - 若用户需求中无任何执行逻辑相关描述(如纯文档、纯说明类技能):不生成 scripts 目录,避免冗余 + + #### 4. 节点必含字段 + - directory类型:name、type、path(仅父级路径)、format(固定"dir")、description、children(空目录写 children: []) + - file类型:name、type、path(仅父级路径)、format(仅markdown/python两种)、description、content(非空,有实际可用内容) ### 四、文件content内容规范(Python脚本必实用) - #### 1. /skills/skills.md(结构固定) + #### 1. 根目录下 skills.md - # 技能名称(不加多余符号,居中可加空格但不强制) - ## 技能描述(整合用户“描述+摘要”,补充逻辑连贯性) - - ## 标签(格式:`- 标签1\n- 标签2`,对应tags数组内容) - - ## 使用说明(分点写:适用场景、操作步骤,需脚本则写“运行main.py脚本”,无需则写“直接参考文档使用”) + - ## 标签(格式:- 标签1\n- 标签2) + - ## 使用说明(分点写:适用场景、操作步骤,需脚本则写“运行scripts/main.py脚本”,无需则写“直接参考文档使用”) - ## 目录结构(用代码块 ``` 列出所有文件/目录路径) - #### 2. /skills/scripts/main.py(Python脚本必含) - - 必含依赖导入(如 `import pandas as pd`,无依赖则不写) - - 必含入口函数 `def execute(params: dict) -> dict:`(参数为dict,返回dict结果) + #### 2. scripts目录下 main.py + - 必含依赖导入(如 import pandas as pd,无依赖则不写) + - 必含入口函数 def execute(params: dict) -> dict:(参数为dict,返回dict结果) - 函数内必含: 1. 参数校验(判断必填键是否存在,缺失返回错误) 2. 核心逻辑(匹配技能需求,如数据处理、文本分析) - 3. 结果返回(成功:`{"status": "success", "data": 结果}`;失败:`{"status": "fail", "error": 信息}`) - - 必含注释:函数说明、参数/返回值说明、示例调用(`if __name__ == "__main__":` 块) + 3. 结果返回(成功:{"status": "success", "data": 结果};失败:{"status": "fail", "error": 信息}) + - 必含注释:函数说明、参数/返回值说明、示例调用(if __name__ == "__main__": 块) - 禁止空函数、语法错误,确保复制后可直接运行 ### 五、YAML语法死规定(100%无解析错误) 1. 缩进:统一2个空格(禁止Tab,禁止1/3/4空格,嵌套层级严格对齐) - package 下子字段缩进2空格 - structure 下目录/文件节点缩进4空格(package→structure→children,每层+2空格) - 2. 路径:全部为绝对路径,Unix风格 `/`(如 /skills/scripts/main.py),禁止 `\\` 或 `./` + 2. 路径:全部遵循path只写父目录规则,Unix风格 /,禁止 \\ 或 ./ 3. 字符串:特殊字符(:、#、空格)无需转义,直接书写 - 4. 数组:tags格式严格为 `tags: [标签1, 标签2]`(逗号后加空格,无多余逗号) - 5. content:多行内容用 `|` 开头,内容行首顶格,内部遵循对应格式缩进(Python用4空格) + 4. 数组:tags格式严格为 tags: [标签1, 标签2](逗号后加空格,无多余逗号) + 5. content:多行内容用 | 开头,内容行首顶格,内部遵循对应格式缩进(Python用4空格) ### 六、错误规避红线(绝对不能触碰) - 1. 禁止顶层出现除 `package` 外的任何节点(如name、version不能直接在顶层) - 2. 禁止在YAML前后加任何多余文字(如“生成完毕”“---”分隔符) - 3. 禁止生成非Python脚本(仅支持main.py,无.js/.sh文件) - 4. 禁止字段缺失(如package必含structure,file必含content) - 5. 禁止目录结构错误(脚本必须在/skills/scripts下) + 1. 禁止顶层出现除 package 外的任何节点 + 2. 禁止在YAML前后加任何多余文字 + 3. 禁止生成非Python脚本,禁止生成无后缀的脚本文件 + 4. 禁止字段缺失 + 5. 禁止输出YAML片段,必须输出完整可解析文件 + 6. structure禁止描述根目录/,直接从一级子节点开始 + 7. 严禁path中携带文件/目录名称,必须只写父级路径 + 8. 严禁将scripts目录错误添加后缀名,严禁修改Python脚本名为非main.py - 最终输出仅纯YAML,直接可复制存储、解析使用,无任何冗余或格式问题! + 最终输出仅完整纯YAML,直接可复制存储、解析使用,无任何冗余或格式问题! """; String userContent = """ diff --git a/src/main/resources/mapper/PaymentOrderMapper.xml b/src/main/resources/mapper/PaymentOrderMapper.xml index e4d7a3d..94a53ed 100644 --- a/src/main/resources/mapper/PaymentOrderMapper.xml +++ b/src/main/resources/mapper/PaymentOrderMapper.xml @@ -111,6 +111,13 @@ +