Compare commits

...

19 Commits

Author SHA1 Message Date
ba86ac45f4 feat(core): 新增VIP到期定时检查任务 2026-04-09 14:22:14 +08:00
d1f1e72732 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	keyboard-server/src/main/java/com/yolo/keyboard/dal/dataobject/aicompanion/AiCompanionI18nDO.java
2026-04-08 17:51:01 +08:00
ab09d0ee74 refactor(aicompanion): 将开场白字段迁移至 i18n 表
- 从 KeyboardAiCompanionDO 移除 prologue 与 prologueAudio
- 在 AiCompanionI18nDO 新增对应字段
- 同步调整 Mapper 查询逻辑
2026-04-08 17:50:47 +08:00
407438fb99 refactor(aicompanion): 将开场白字段迁移至 i18n 表
- 从 KeyboardAiCompanionDO 移除 prologue 与 prologueAudio
- 在 AiCompanionI18nDO 新增对应字段
- 同步调整 Mapper 查询逻辑
2026-04-08 17:49:37 +08:00
d0f4ad25c0 refactor(vo): 移除创建与更新时间的必填校验 2026-04-08 09:52:10 +08:00
da7bfbc9ce refactor(vo): 移除创建与更新时间的必填校验 2026-04-07 14:47:48 +08:00
8cf9ad4436 新增人设标签国际化增删改查并修正标签序列配置 2026-04-02 15:02:12 +08:00
efc97b3584 新增AI陪聊角色国际化管理接口 2026-04-02 13:26:00 +08:00
980364efc1 feat(service): 添加键盘人设国际化配置 2026-04-01 16:45:04 +08:00
08ec6cd010 fix(service): 更新字符缓存策略为删除而非刷新
原“刷新缓存”逻辑改为“删除缓存”,保证下次读取必从数据库加载最新数据,避免脏读
2026-03-31 11:53:59 +08:00
7c1b515e6a feat(character): 为键盘人设服务集成Redis缓存
引入RedisTemplate,新增缓存刷新与删除方法,在增删改操作后同步更新缓存,提升读取性能并避免脏数据
2026-03-16 10:09:15 +08:00
9da3f3c0c5 chore(config): 将开发环境数据库用户名改为 root 2026-03-13 16:35:57 +08:00
331bba16c0 config: 切换开发环境数据库与Redis连接配置 2026-03-11 10:29:48 +08:00
3a0d2b9c01 feat(themestyles): 新增国家/地区字段 local 2026-03-09 11:44:03 +08:00
abfcf7bfc8 fix(themes): 新增字段local标识皮肤所属国家 2026-03-09 11:21:33 +08:00
0d3a2cfd9f refactor(AppVersionsDO): 启用MyBatis-Plus JSONB字段自动映射并优化导入顺序 2026-03-04 20:39:13 +08:00
5c191740e9 feat(app-versions): 新增App版本管理功能模块 2026-03-04 16:35:59 +08:00
6eec62ca83 feat(warning-message): 新增用户注销提示信息管理功能
新增 Controller/Service/Mapper/DO/VO 等全套后端模块,支持分页、增删改查及状态切换;补充对应错误码常量。
2026-02-28 15:25:52 +08:00
134469536b feat(product-items): 新增订阅等级字段 2026-02-12 19:39:39 +08:00
61 changed files with 2006 additions and 119 deletions

1
.gitignore vendored
View File

@@ -53,3 +53,4 @@ application-my.yaml
/yolo-ui-app/unpackage/ /yolo-ui-app/unpackage/
**/.DS_Store **/.DS_Store
/.omc/ /.omc/
/AGENTS.md

View File

@@ -1,5 +1,6 @@
package com.yolo.keyboard.controller.admin.aicompanion; package com.yolo.keyboard.controller.admin.aicompanion;
import com.yolo.keyboard.dal.dataobject.aicompanion.AiCompanionI18nDO;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -101,4 +102,56 @@ public class KeyboardAiCompanionController {
BeanUtils.toBean(list, KeyboardAiCompanionRespVO.class)); BeanUtils.toBean(list, KeyboardAiCompanionRespVO.class));
} }
// ==================== 子表AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍 ====================
@GetMapping("/ai-companion-i18n/page")
@Operation(summary = "获得AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍分页")
@Parameter(name = "id", description = "主键ID")
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:query')")
public CommonResult<PageResult<AiCompanionI18nDO>> getAiCompanionI18nPage(PageParam pageReqVO,
@RequestParam("id") Long id) {
return success(aiCompanionService.getAiCompanionI18nPage(pageReqVO, id));
}
@PostMapping("/ai-companion-i18n/create")
@Operation(summary = "创建AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍")
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:create')")
public CommonResult<Long> createAiCompanionI18n(@Valid @RequestBody AiCompanionI18nDO aiCompanionI18n) {
return success(aiCompanionService.createAiCompanionI18n(aiCompanionI18n));
}
@PutMapping("/ai-companion-i18n/update")
@Operation(summary = "更新AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍")
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:update')")
public CommonResult<Boolean> updateAiCompanionI18n(@Valid @RequestBody AiCompanionI18nDO aiCompanionI18n) {
aiCompanionService.updateAiCompanionI18n(aiCompanionI18n);
return success(true);
}
@DeleteMapping("/ai-companion-i18n/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍")
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:delete')")
public CommonResult<Boolean> deleteAiCompanionI18n(@RequestParam("id") Long id) {
aiCompanionService.deleteAiCompanionI18n(id);
return success(true);
}
@DeleteMapping("/ai-companion-i18n/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍")
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:delete')")
public CommonResult<Boolean> deleteAiCompanionI18nList(@RequestParam("ids") List<Long> ids) {
aiCompanionService.deleteAiCompanionI18nListByIds(ids);
return success(true);
}
@GetMapping("/ai-companion-i18n/get")
@Operation(summary = "获得AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('keyboard:ai-companion:query')")
public CommonResult<AiCompanionI18nDO> getAiCompanionI18n(@RequestParam("id") Long id) {
return success(aiCompanionService.getAiCompanionI18n(id));
}
} }

View File

@@ -13,9 +13,6 @@ import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR
@Data @Data
public class KeyboardAiCompanionPageReqVO extends PageParam { public class KeyboardAiCompanionPageReqVO extends PageParam {
@Schema(description = "角色名称展示用Katie Leona", example = "张三")
private String name;
@Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn") @Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn")
private String avatarUrl; private String avatarUrl;
@@ -28,14 +25,8 @@ public class KeyboardAiCompanionPageReqVO extends PageParam {
@Schema(description = "角色年龄段描述20s、25-30") @Schema(description = "角色年龄段描述20s、25-30")
private String ageRange; private String ageRange;
@Schema(description = "一句话人设描述,用于卡片或列表展示")
private String shortDesc;
@Schema(description = "角色详细介绍文案,用于角色详情页")
private String introText;
@Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)") @Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)")
private List<String> personalityTags; private Object personalityTags;
@Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)") @Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)")
private String speakingStyle; private String speakingStyle;
@@ -67,7 +58,7 @@ public class KeyboardAiCompanionPageReqVO extends PageParam {
@Schema(description = "开场白音频") @Schema(description = "开场白音频")
private String prologueAudio; private String prologueAudio;
@Schema(description = "音频Id", example = "31328") @Schema(description = "音频Id", example = "4155")
private String voiceId; private String voiceId;
} }

View File

@@ -12,14 +12,10 @@ import cn.idev.excel.annotation.*;
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
public class KeyboardAiCompanionRespVO { public class KeyboardAiCompanionRespVO {
@Schema(description = "陪聊角色唯一ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "7816") @Schema(description = "陪聊角色唯一ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28180")
@ExcelProperty("陪聊角色唯一ID") @ExcelProperty("陪聊角色唯一ID")
private Long id; private Long id;
@Schema(description = "角色名称展示用Katie Leona", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@ExcelProperty("角色名称展示用Katie Leona")
private String name;
@Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn") @Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn")
@ExcelProperty("角色头像URL用于列表页和聊天页") @ExcelProperty("角色头像URL用于列表页和聊天页")
private String avatarUrl; private String avatarUrl;
@@ -36,17 +32,9 @@ public class KeyboardAiCompanionRespVO {
@ExcelProperty("角色年龄段描述20s、25-30") @ExcelProperty("角色年龄段描述20s、25-30")
private String ageRange; private String ageRange;
@Schema(description = "一句话人设描述,用于卡片或列表展示")
@ExcelProperty("一句话人设描述,用于卡片或列表展示")
private String shortDesc;
@Schema(description = "角色详细介绍文案,用于角色详情页")
@ExcelProperty("角色详细介绍文案,用于角色详情页")
private String introText;
@Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)") @Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)")
@ExcelProperty("角色性格标签数组(如:温柔、黏人、治愈)") @ExcelProperty("角色性格标签数组(如:温柔、黏人、治愈)")
private List<String> personalityTags; private Object personalityTags;
@Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)") @Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)")
@ExcelProperty("角色说话风格(如:撒娇型、理性型、活泼型)") @ExcelProperty("角色说话风格(如:撒娇型、理性型、活泼型)")
@@ -88,7 +76,7 @@ public class KeyboardAiCompanionRespVO {
@ExcelProperty("开场白音频") @ExcelProperty("开场白音频")
private String prologueAudio; private String prologueAudio;
@Schema(description = "音频Id", example = "31328") @Schema(description = "音频Id", example = "4155")
@ExcelProperty("音频Id") @ExcelProperty("音频Id")
private String voiceId; private String voiceId;

View File

@@ -11,13 +11,9 @@ import java.time.LocalDateTime;
@Data @Data
public class KeyboardAiCompanionSaveReqVO { public class KeyboardAiCompanionSaveReqVO {
@Schema(description = "陪聊角色唯一ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "7816") @Schema(description = "陪聊角色唯一ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28180")
private Long id; private Long id;
@Schema(description = "角色名称展示用Katie Leona", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@NotEmpty(message = "角色名称展示用Katie Leona不能为空")
private String name;
@Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn") @Schema(description = "角色头像URL用于列表页和聊天页", example = "https://www.iocoder.cn")
private String avatarUrl; private String avatarUrl;
@@ -30,14 +26,8 @@ public class KeyboardAiCompanionSaveReqVO {
@Schema(description = "角色年龄段描述20s、25-30") @Schema(description = "角色年龄段描述20s、25-30")
private String ageRange; private String ageRange;
@Schema(description = "一句话人设描述,用于卡片或列表展示")
private String shortDesc;
@Schema(description = "角色详细介绍文案,用于角色详情页")
private String introText;
@Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)") @Schema(description = "角色性格标签数组(如:温柔、黏人、治愈)")
private List<String> personalityTags; private Object personalityTags;
@Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)") @Schema(description = "角色说话风格(如:撒娇型、理性型、活泼型)")
private String speakingStyle; private String speakingStyle;
@@ -60,13 +50,19 @@ public class KeyboardAiCompanionSaveReqVO {
@Schema(description = "角色热度评分,用于推荐排序") @Schema(description = "角色热度评分,用于推荐排序")
private Integer popularityScore; private Integer popularityScore;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createdAt;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updatedAt;
@Schema(description = "开场白") @Schema(description = "开场白")
private String prologue; private String prologue;
@Schema(description = "开场白音频") @Schema(description = "开场白音频")
private String prologueAudio; private String prologueAudio;
@Schema(description = "音频Id", example = "31328") @Schema(description = "音频Id", example = "4155")
private String voiceId; private String voiceId;
} }

View File

@@ -0,0 +1,104 @@
package com.yolo.keyboard.controller.admin.appversions;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.CommonResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import static com.yolo.keyboard.framework.common.pojo.CommonResult.success;
import com.yolo.keyboard.framework.excel.core.util.ExcelUtils;
import com.yolo.keyboard.framework.apilog.core.annotation.ApiAccessLog;
import static com.yolo.keyboard.framework.apilog.core.enums.OperateTypeEnum.*;
import com.yolo.keyboard.controller.admin.appversions.vo.*;
import com.yolo.keyboard.dal.dataobject.appversions.AppVersionsDO;
import com.yolo.keyboard.service.appversions.AppVersionsService;
@Tag(name = "管理后台 - App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@RestController
@RequestMapping("/keyboard/app-versions")
@Validated
public class AppVersionsController {
@Resource
private AppVersionsService appVersionsService;
@PostMapping("/create")
@Operation(summary = "创建App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:create')")
public CommonResult<Long> createAppVersions(@RequestBody AppVersionsSaveReqVO createReqVO) {
return success(appVersionsService.createAppVersions(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:update')")
public CommonResult<Boolean> updateAppVersions(@RequestBody AppVersionsSaveReqVO updateReqVO) {
appVersionsService.updateAppVersions(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:delete')")
public CommonResult<Boolean> deleteAppVersions(@RequestParam("id") Long id) {
appVersionsService.deleteAppVersions(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:delete')")
public CommonResult<Boolean> deleteAppVersionsList(@RequestParam("ids") List<Long> ids) {
appVersionsService.deleteAppVersionsListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:query')")
public CommonResult<AppVersionsRespVO> getAppVersions(@RequestParam("id") Long id) {
AppVersionsDO appVersions = appVersionsService.getAppVersions(id);
return success(BeanUtils.toBean(appVersions, AppVersionsRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。分页")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:query')")
public CommonResult<PageResult<AppVersionsRespVO>> getAppVersionsPage(@Valid AppVersionsPageReqVO pageReqVO) {
PageResult<AppVersionsDO> pageResult = appVersionsService.getAppVersionsPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AppVersionsRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 Excel")
@PreAuthorize("@ss.hasPermission('keyboard:app-versions:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportAppVersionsExcel(@Valid AppVersionsPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<AppVersionsDO> list = appVersionsService.getAppVersionsPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。.xls", "数据", AppVersionsRespVO.class,
BeanUtils.toBean(list, AppVersionsRespVO.class));
}
}

View File

@@ -0,0 +1,65 @@
package com.yolo.keyboard.controller.admin.appversions.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。分页 Request VO")
@Data
public class AppVersionsPageReqVO extends PageParam {
@Schema(description = "应用标识支持多App/多包名场景单App可固定为 main。", example = "10064")
private String appId;
@Schema(description = "平台android 或 ios用 CHECK 约束限制取值)。")
private String platform;
@Schema(description = "渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。")
private String channel;
@Schema(description = "展示用版本号(语义版本字符串),如 1.2.3。", example = "芋艿")
private String versionName;
@Schema(description = "比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。")
private Long versionCode;
@Schema(description = "iOS 可选构建号(例如 CFBundleVersion通常为字符串用于追溯构建或与CI编号对齐。")
private String buildNumber;
@Schema(description = "最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。")
private Long minSupportedCode;
@Schema(description = "是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Boolean[] isForceUpdate;
@Schema(description = "是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。")
private Boolean isActive;
@Schema(description = "更新说明(展示给用户的版本更新内容)。")
private String releaseNotes;
@Schema(description = "下载链接Android 可为 apk 直链/市场 schemeiOS 通常为 App Store 链接或统一跳转页。", example = "https://www.iocoder.cn")
private String downloadUrl;
@Schema(description = "应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。", example = "https://www.iocoder.cn")
private String storeUrl;
@Schema(description = "扩展元数据JSON如包大小、md5、签名信息、最低系统版本等。")
private Object metadata;
@Schema(description = "发布时间(对外宣布/上线时间),用于展示与排序。")
private LocalDateTime releasedAt;
@Schema(description = "记录创建时间。")
private LocalDateTime createdAt;
@Schema(description = "记录更新时间(建议配合触发器自动维护)。")
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,83 @@
package com.yolo.keyboard.controller.admin.appversions.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
@Schema(description = "管理后台 - App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 Response VO")
@Data
@ExcelIgnoreUnannotated
public class AppVersionsRespVO {
@Schema(description = "主键自增版本记录ID。", requiredMode = Schema.RequiredMode.REQUIRED, example = "25012")
@ExcelProperty("主键自增版本记录ID。")
private Long id;
@Schema(description = "应用标识支持多App/多包名场景单App可固定为 main。", requiredMode = Schema.RequiredMode.REQUIRED, example = "10064")
@ExcelProperty("应用标识支持多App/多包名场景单App可固定为 main。")
private String appId;
@Schema(description = "平台android 或 ios用 CHECK 约束限制取值)。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("平台android 或 ios用 CHECK 约束限制取值)。")
private String platform;
@Schema(description = "渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。")
private String channel;
@Schema(description = "展示用版本号(语义版本字符串),如 1.2.3。", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("展示用版本号(语义版本字符串),如 1.2.3。")
private String versionName;
@Schema(description = "比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。")
private Long versionCode;
@Schema(description = "iOS 可选构建号(例如 CFBundleVersion通常为字符串用于追溯构建或与CI编号对齐。")
@ExcelProperty("iOS 可选构建号(例如 CFBundleVersion通常为字符串用于追溯构建或与CI编号对齐。")
private String buildNumber;
@Schema(description = "最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。")
private Long minSupportedCode;
@Schema(description = "是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code")
private Boolean isForceUpdate;
@Schema(description = "是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。")
private Boolean isActive;
@Schema(description = "更新说明(展示给用户的版本更新内容)。")
@ExcelProperty("更新说明(展示给用户的版本更新内容)。")
private String releaseNotes;
@Schema(description = "下载链接Android 可为 apk 直链/市场 schemeiOS 通常为 App Store 链接或统一跳转页。", example = "https://www.iocoder.cn")
@ExcelProperty("下载链接Android 可为 apk 直链/市场 schemeiOS 通常为 App Store 链接或统一跳转页。")
private String downloadUrl;
@Schema(description = "应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。", example = "https://www.iocoder.cn")
@ExcelProperty("应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。")
private String storeUrl;
@Schema(description = "扩展元数据JSON如包大小、md5、签名信息、最低系统版本等。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("扩展元数据JSON如包大小、md5、签名信息、最低系统版本等。")
private Object metadata;
@Schema(description = "发布时间(对外宣布/上线时间),用于展示与排序。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("发布时间(对外宣布/上线时间),用于展示与排序。")
private LocalDateTime releasedAt;
@Schema(description = "记录创建时间。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("记录创建时间。")
private LocalDateTime createdAt;
@Schema(description = "记录更新时间(建议配合触发器自动维护)。", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("记录更新时间(建议配合触发器自动维护)。")
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,74 @@
package com.yolo.keyboard.controller.admin.appversions.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。新增/修改 Request VO")
@Data
public class AppVersionsSaveReqVO {
@Schema(description = "主键自增版本记录ID。", requiredMode = Schema.RequiredMode.REQUIRED, example = "25012")
private Long id;
@Schema(description = "应用标识支持多App/多包名场景单App可固定为 main。", requiredMode = Schema.RequiredMode.REQUIRED, example = "10064")
@NotEmpty(message = "应用标识支持多App/多包名场景单App可固定为 main。不能为空")
private String appId;
@Schema(description = "平台android 或 ios用 CHECK 约束限制取值)。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "平台android 或 ios用 CHECK 约束限制取值)。不能为空")
private String platform;
@Schema(description = "渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。不能为空")
private String channel;
@Schema(description = "展示用版本号(语义版本字符串),如 1.2.3。", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "展示用版本号(语义版本字符串),如 1.2.3。不能为空")
private String versionName;
@Schema(description = "比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。不能为空")
private Long versionCode;
@Schema(description = "iOS 可选构建号(例如 CFBundleVersion通常为字符串用于追溯构建或与CI编号对齐。")
private String buildNumber;
@Schema(description = "最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。不能为空")
private Long minSupportedCode;
@Schema(description = "是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code。不能为空")
private Boolean isForceUpdate;
@Schema(description = "是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。不能为空")
private Boolean isActive;
@Schema(description = "更新说明(展示给用户的版本更新内容)。")
private String releaseNotes;
@Schema(description = "下载链接Android 可为 apk 直链/市场 schemeiOS 通常为 App Store 链接或统一跳转页。", example = "https://www.iocoder.cn")
private String downloadUrl;
@Schema(description = "应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。", example = "https://www.iocoder.cn")
private String storeUrl;
@Schema(description = "扩展元数据JSON如包大小、md5、签名信息、最低系统版本等。", requiredMode = Schema.RequiredMode.REQUIRED)
private Object metadata;
@Schema(description = "发布时间(对外宣布/上线时间),用于展示与排序。", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "发布时间(对外宣布/上线时间),用于展示与排序。不能为空")
private LocalDateTime releasedAt;
@Schema(description = "记录创建时间。", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createdAt;
@Schema(description = "记录更新时间(建议配合触发器自动维护)。", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updatedAt;
}

View File

@@ -1,5 +1,6 @@
package com.yolo.keyboard.controller.admin.character; package com.yolo.keyboard.controller.admin.character;
import com.yolo.keyboard.dal.dataobject.character.CharacterI18nDO;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -100,5 +101,56 @@ public class KeyboardCharacterController {
ExcelUtils.write(response, "键盘人设.xls", "数据", KeyboardCharacterRespVO.class, ExcelUtils.write(response, "键盘人设.xls", "数据", KeyboardCharacterRespVO.class,
BeanUtils.toBean(list, KeyboardCharacterRespVO.class)); BeanUtils.toBean(list, KeyboardCharacterRespVO.class));
} }
// ==================== 子表(键盘人设国际化内容) ====================
@GetMapping("/character-i18n/page")
@Operation(summary = "获得键盘人设国际化内容分页")
@Parameter(name = "characterId", description = "角色主表id")
@PreAuthorize("@ss.hasPermission('keyboard:character:query')")
public CommonResult<PageResult<CharacterI18nDO>> getCharacterI18nPage(PageParam pageReqVO,
@RequestParam("characterId") Long characterId) {
return success(characterService.getCharacterI18nPage(pageReqVO, characterId));
}
@PostMapping("/character-i18n/create")
@Operation(summary = "创建键盘人设国际化内容")
@PreAuthorize("@ss.hasPermission('keyboard:character:create')")
public CommonResult<Long> createCharacterI18n(@Valid @RequestBody CharacterI18nDO characterI18n) {
return success(characterService.createCharacterI18n(characterI18n));
}
@PutMapping("/character-i18n/update")
@Operation(summary = "更新键盘人设国际化内容")
@PreAuthorize("@ss.hasPermission('keyboard:character:update')")
public CommonResult<Boolean> updateCharacterI18n(@Valid @RequestBody CharacterI18nDO characterI18n) {
characterService.updateCharacterI18n(characterI18n);
return success(true);
}
@DeleteMapping("/character-i18n/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除键盘人设国际化内容")
@PreAuthorize("@ss.hasPermission('keyboard:character:delete')")
public CommonResult<Boolean> deleteCharacterI18n(@RequestParam("id") Long id) {
characterService.deleteCharacterI18n(id);
return success(true);
}
@DeleteMapping("/character-i18n/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除键盘人设国际化内容")
@PreAuthorize("@ss.hasPermission('keyboard:character:delete')")
public CommonResult<Boolean> deleteCharacterI18nList(@RequestParam("ids") List<Long> ids) {
characterService.deleteCharacterI18nListByIds(ids);
return success(true);
}
@GetMapping("/character-i18n/get")
@Operation(summary = "获得键盘人设国际化内容")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('keyboard:character:query')")
public CommonResult<CharacterI18nDO> getCharacterI18n(@RequestParam("id") Long id) {
return success(characterService.getCharacterI18n(id));
}
} }

View File

@@ -13,12 +13,6 @@ import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR
@Data @Data
public class KeyboardCharacterPageReqVO extends PageParam { public class KeyboardCharacterPageReqVO extends PageParam {
@Schema(description = "标题", example = "王五")
private String characterName;
@Schema(description = "背景描述")
private String characterBackground;
@Schema(description = "角色头像", example = "https://www.iocoder.cn") @Schema(description = "角色头像", example = "https://www.iocoder.cn")
private String avatarUrl; private String avatarUrl;

View File

@@ -16,14 +16,6 @@ public class KeyboardCharacterRespVO {
@ExcelProperty("主键 Id") @ExcelProperty("主键 Id")
private Long id; private Long id;
@Schema(description = "标题", example = "王五")
@ExcelProperty("标题")
private String characterName;
@Schema(description = "背景描述")
@ExcelProperty("背景描述")
private String characterBackground;
@Schema(description = "角色头像", example = "https://www.iocoder.cn") @Schema(description = "角色头像", example = "https://www.iocoder.cn")
@ExcelProperty("角色头像") @ExcelProperty("角色头像")
private String avatarUrl; private String avatarUrl;

View File

@@ -14,12 +14,6 @@ public class KeyboardCharacterSaveReqVO {
@Schema(description = "主键 Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "7592") @Schema(description = "主键 Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "7592")
private Long id; private Long id;
@Schema(description = "标题", example = "王五")
private String characterName;
@Schema(description = "背景描述")
private String characterBackground;
@Schema(description = "角色头像", example = "https://www.iocoder.cn") @Schema(description = "角色头像", example = "https://www.iocoder.cn")
private String avatarUrl; private String avatarUrl;

View File

@@ -50,4 +50,9 @@ public class KeyboardProductItemsPageReqVO extends PageParam {
@Schema(description = "订阅时长的具体天数") @Schema(description = "订阅时长的具体天数")
private Integer durationDays; private Integer durationDays;
@Schema(description = "订阅等级")
private Integer level;
@Schema(description = "平台")
private String platform;
} }

View File

@@ -65,4 +65,9 @@ public class KeyboardProductItemsRespVO {
@ExcelProperty("订阅时长的具体天数") @ExcelProperty("订阅时长的具体天数")
private Integer durationDays; private Integer durationDays;
@Schema(description = "订阅等级")
private Integer level;
@Schema(description = "平台")
private String platform;
} }

View File

@@ -57,4 +57,9 @@ public class KeyboardProductItemsSaveReqVO {
@NotNull(message = "订阅时长的具体天数不能为空") @NotNull(message = "订阅时长的具体天数不能为空")
private Integer durationDays; private Integer durationDays;
@Schema(description = "订阅等级")
private Integer level;
@Schema(description = "平台")
private String platform;
} }

View File

@@ -1,5 +1,6 @@
package com.yolo.keyboard.controller.admin.tag; package com.yolo.keyboard.controller.admin.tag;
import com.yolo.keyboard.dal.dataobject.tag.TagI18nDO;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -100,5 +101,55 @@ public class KeyboardTagController {
ExcelUtils.write(response, "人设标签.xls", "数据", KeyboardTagRespVO.class, ExcelUtils.write(response, "人设标签.xls", "数据", KeyboardTagRespVO.class,
BeanUtils.toBean(list, KeyboardTagRespVO.class)); BeanUtils.toBean(list, KeyboardTagRespVO.class));
} }
// ==================== 子表(人设标签国际化) ====================
@GetMapping("/tag-i18n/page")
@Operation(summary = "获得人设标签国际化分页")
@Parameter(name = "tagId", description = "标签主表ID对应 keyboard_tag.id")
@PreAuthorize("@ss.hasPermission('keyboard:tag:query')")
public CommonResult<PageResult<TagI18nDO>> getTagI18nPage(PageParam pageReqVO,
@RequestParam("tagId") Integer tagId) {
return success(tagService.getTagI18nPage(pageReqVO, tagId));
}
@PostMapping("/tag-i18n/create")
@Operation(summary = "创建人设标签国际化")
@PreAuthorize("@ss.hasPermission('keyboard:tag:create')")
public CommonResult<Long> createTagI18n(@Valid @RequestBody TagI18nDO tagI18n) {
return success(tagService.createTagI18n(tagI18n));
}
@PutMapping("/tag-i18n/update")
@Operation(summary = "更新人设标签国际化")
@PreAuthorize("@ss.hasPermission('keyboard:tag:update')")
public CommonResult<Boolean> updateTagI18n(@Valid @RequestBody TagI18nDO tagI18n) {
tagService.updateTagI18n(tagI18n);
return success(true);
}
@DeleteMapping("/tag-i18n/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除人设标签国际化")
@PreAuthorize("@ss.hasPermission('keyboard:tag:delete')")
public CommonResult<Boolean> deleteTagI18n(@RequestParam("id") Long id) {
tagService.deleteTagI18n(id);
return success(true);
}
@DeleteMapping("/tag-i18n/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除人设标签国际化")
@PreAuthorize("@ss.hasPermission('keyboard:tag:delete')")
public CommonResult<Boolean> deleteTagI18nList(@RequestParam("ids") List<Long> ids) {
tagService.deleteTagI18nListByIds(ids);
return success(true);
}
@GetMapping("/tag-i18n/get")
@Operation(summary = "获得人设标签国际化")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('keyboard:tag:query')")
public CommonResult<TagI18nDO> getTagI18n(@RequestParam("id") Long id) {
return success(tagService.getTagI18n(id));
}
} }

View File

@@ -56,4 +56,5 @@ public class KeyboardThemesPageReqVO extends PageParam {
@Schema(description = "真实下载数量", example = "3353") @Schema(description = "真实下载数量", example = "3353")
private Long realDownloadCount; private Long realDownloadCount;
} @Schema(description = "所属国家")
private String local;}

View File

@@ -73,4 +73,7 @@ public class KeyboardThemesRespVO {
@ExcelProperty("真实下载数量") @ExcelProperty("真实下载数量")
private Long realDownloadCount; private Long realDownloadCount;
@Schema(description = "所属国家")
@ExcelProperty("所属国家")
private String local;
} }

View File

@@ -57,4 +57,6 @@ public class KeyboardThemesSaveReqVO {
@Schema(description = "真实下载数量", example = "3353") @Schema(description = "真实下载数量", example = "3353")
private Long realDownloadCount; private Long realDownloadCount;
@Schema(description = "所属国家")
private String local;
} }

View File

@@ -18,4 +18,6 @@ public class KeyboardThemeStylesPageReqVO extends PageParam {
@Schema(description = "更新时间") @Schema(description = "更新时间")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@Schema(description = "所属国家地区")
private String local;
} }

View File

@@ -27,4 +27,6 @@ public class KeyboardThemeStylesRespVO {
@ExcelProperty("更新时间") @ExcelProperty("更新时间")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@Schema(description = "所属国家地区")
private String local;
} }

View File

@@ -19,4 +19,6 @@ public class KeyboardThemeStylesSaveReqVO {
@Schema(description = "更新时间") @Schema(description = "更新时间")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@Schema(description = "所属国家地区")
private String local;
} }

View File

@@ -0,0 +1,104 @@
package com.yolo.keyboard.controller.admin.warningmessage;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.CommonResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import static com.yolo.keyboard.framework.common.pojo.CommonResult.success;
import com.yolo.keyboard.framework.excel.core.util.ExcelUtils;
import com.yolo.keyboard.framework.apilog.core.annotation.ApiAccessLog;
import static com.yolo.keyboard.framework.apilog.core.enums.OperateTypeEnum.*;
import com.yolo.keyboard.controller.admin.warningmessage.vo.*;
import com.yolo.keyboard.dal.dataobject.warningmessage.KeyboardWarningMessageDO;
import com.yolo.keyboard.service.warningmessage.KeyboardWarningMessageService;
@Tag(name = "管理后台 - 用户注销提示信息")
@RestController
@RequestMapping("/keyboard/warning-message")
@Validated
public class KeyboardWarningMessageController {
@Resource
private KeyboardWarningMessageService warningMessageService;
@PostMapping("/create")
@Operation(summary = "创建用户注销提示信息")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:create')")
public CommonResult<Long> createWarningMessage(@Valid @RequestBody KeyboardWarningMessageSaveReqVO createReqVO) {
return success(warningMessageService.createWarningMessage(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新用户注销提示信息")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:update')")
public CommonResult<Boolean> updateWarningMessage(@Valid @RequestBody KeyboardWarningMessageSaveReqVO updateReqVO) {
warningMessageService.updateWarningMessage(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除用户注销提示信息")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:delete')")
public CommonResult<Boolean> deleteWarningMessage(@RequestParam("id") Long id) {
warningMessageService.deleteWarningMessage(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除用户注销提示信息")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:delete')")
public CommonResult<Boolean> deleteWarningMessageList(@RequestParam("ids") List<Long> ids) {
warningMessageService.deleteWarningMessageListByIds(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得用户注销提示信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:query')")
public CommonResult<KeyboardWarningMessageRespVO> getWarningMessage(@RequestParam("id") Long id) {
KeyboardWarningMessageDO warningMessage = warningMessageService.getWarningMessage(id);
return success(BeanUtils.toBean(warningMessage, KeyboardWarningMessageRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得用户注销提示信息分页")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:query')")
public CommonResult<PageResult<KeyboardWarningMessageRespVO>> getWarningMessagePage(@Valid KeyboardWarningMessagePageReqVO pageReqVO) {
PageResult<KeyboardWarningMessageDO> pageResult = warningMessageService.getWarningMessagePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, KeyboardWarningMessageRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出用户注销提示信息 Excel")
@PreAuthorize("@ss.hasPermission('keyboard:warning-message:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportWarningMessageExcel(@Valid KeyboardWarningMessagePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<KeyboardWarningMessageDO> list = warningMessageService.getWarningMessagePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "用户注销提示信息.xls", "数据", KeyboardWarningMessageRespVO.class,
BeanUtils.toBean(list, KeyboardWarningMessageRespVO.class));
}
}

View File

@@ -0,0 +1,28 @@
package com.yolo.keyboard.controller.admin.warningmessage.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.yolo.keyboard.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 用户注销提示信息分页 Request VO")
@Data
public class KeyboardWarningMessagePageReqVO extends PageParam {
@Schema(description = "地区")
private String locale;
@Schema(description = "正文")
private String content;
@Schema(description = "更新时间")
private LocalDateTime updatedAt;
@Schema(description = "创建时间")
private LocalDateTime created;
}

View File

@@ -0,0 +1,35 @@
package com.yolo.keyboard.controller.admin.warningmessage.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import cn.idev.excel.annotation.*;
@Schema(description = "管理后台 - 用户注销提示信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class KeyboardWarningMessageRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7770")
@ExcelProperty("主键")
private Long id;
@Schema(description = "地区")
@ExcelProperty("地区")
private String locale;
@Schema(description = "正文")
@ExcelProperty("正文")
private String content;
@Schema(description = "更新时间")
@ExcelProperty("更新时间")
private LocalDateTime updatedAt;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime created;
}

View File

@@ -0,0 +1,29 @@
package com.yolo.keyboard.controller.admin.warningmessage.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 用户注销提示信息新增/修改 Request VO")
@Data
public class KeyboardWarningMessageSaveReqVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7770")
private Long id;
@Schema(description = "地区")
private String locale;
@Schema(description = "正文")
private String content;
@Schema(description = "更新时间")
private LocalDateTime updatedAt;
@Schema(description = "创建时间")
private LocalDateTime created;
}

View File

@@ -0,0 +1,63 @@
package com.yolo.keyboard.dal.dataobject.aicompanion;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO;
/**
* AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍 DO
*
* @author ziin
*/
@TableName("keyboard_ai_companion_i18n")
@KeySequence("keyboard_ai_companion_i18n_id_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TenantIgnore
public class AiCompanionI18nDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 陪聊角色主表ID对应 keyboard_ai_companion.id
*/
private Long companionId;
/**
* 语言标识,如 zh-CN、en-US、ja-JP
*/
private String locale;
/**
* 角色名称(多语言)
*/
private String name;
/**
* 一句话人设描述(多语言)
*/
private String shortDesc;
/**
* 角色详细介绍文案(多语言)
*/
private String introText;
/**
* 创建时间
*/
private LocalDateTime createdAt;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
private String prologue;
private String prologueAudio;
}

View File

@@ -29,10 +29,7 @@ public class KeyboardAiCompanionDO {
*/ */
@TableId @TableId
private Long id; private Long id;
/**
* 角色名称展示用Katie Leona
*/
private String name;
/** /**
* 角色头像URL用于列表页和聊天页 * 角色头像URL用于列表页和聊天页
*/ */
@@ -49,14 +46,6 @@ public class KeyboardAiCompanionDO {
* 角色年龄段描述20s、25-30 * 角色年龄段描述20s、25-30
*/ */
private String ageRange; private String ageRange;
/**
* 一句话人设描述,用于卡片或列表展示
*/
private String shortDesc;
/**
* 角色详细介绍文案,用于角色详情页
*/
private String introText;
/** /**
* 角色性格标签数组(如:温柔、黏人、治愈) * 角色性格标签数组(如:温柔、黏人、治愈)
*/ */
@@ -94,14 +83,6 @@ public class KeyboardAiCompanionDO {
* 更新时间 * 更新时间
*/ */
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
/**
* 开场白
*/
private String prologue;
/**
* 开场白音频
*/
private String prologueAudio;
/** /**
* 音频Id * 音频Id
*/ */

View File

@@ -0,0 +1,100 @@
package com.yolo.keyboard.dal.dataobject.appversions;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yolo.keyboard.framework.mybatis.core.type.JsonbTypeHandler;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import lombok.*;
import java.time.LocalDateTime;
/**
* App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 DO
*
* @author ziin
*/
@TableName(value = "keyboard_app_versions", autoResultMap = true)
@KeySequence("keyboard_app_versions_id_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TenantIgnore
public class AppVersionsDO {
/**
* 主键自增版本记录ID。
*/
@TableId
private Long id;
/**
* 应用标识支持多App/多包名场景单App可固定为 main。
*/
private String appId;
/**
* 平台android 或 ios用 CHECK 约束限制取值)。
*/
private String platform;
/**
* 渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。
*/
private String channel;
/**
* 展示用版本号(语义版本字符串),如 1.2.3。
*/
private String versionName;
/**
* 比较用版本号整数递增Android 对应 versionCodeiOS 建议维护同样的递增值以便比较。
*/
private Long versionCode;
/**
* iOS 可选构建号(例如 CFBundleVersion通常为字符串用于追溯构建或与CI编号对齐。
*/
private String buildNumber;
/**
* 最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。
*/
private Long minSupportedCode;
/**
* 是否强制更新:当客户端未达到最新版本且此字段为 true可要求强更即使 >= min_supported_code
*/
private Boolean isForceUpdate;
/**
* 是否生效true 表示该版本记录可用于对外更新检查false 用于下架/撤回。
*/
private Boolean isActive;
/**
* 更新说明(展示给用户的版本更新内容)。
*/
private String releaseNotes;
/**
* 下载链接Android 可为 apk 直链/市场 schemeiOS 通常为 App Store 链接或统一跳转页。
*/
private String downloadUrl;
/**
* 应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。
*/
private String storeUrl;
/**
* 扩展元数据JSON如包大小、md5、签名信息、最低系统版本等。
*/
@TableField(typeHandler = JsonbTypeHandler.class)
private Object metadata;
/**
* 发布时间(对外宣布/上线时间),用于展示与排序。
*/
private LocalDateTime releasedAt;
/**
* 记录创建时间。
*/
private LocalDateTime createdAt;
/**
* 记录更新时间(建议配合触发器自动维护)。
*/
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,56 @@
package com.yolo.keyboard.dal.dataobject.character;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO;
/**
* 键盘人设国际化内容 DO
*
* @author ziin
*/
@TableName("keyboard_character_i18n")
@KeySequence("keyboard_character_i18n_id_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TenantIgnore
public class CharacterI18nDO {
/**
* 主键 Id
*/
@TableId
private Long id;
/**
* 角色主表id
*/
private Long characterId;
/**
* 语言标识,如 zh-CN/en-US/ja-JP
*/
private String locale;
/**
* 标题
*/
private String characterName;
/**
* 背景描述
*/
private String characterBackground;
/**
* 创建时间
*/
private LocalDateTime createdAt;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
}

View File

@@ -28,14 +28,6 @@ public class KeyboardCharacterDO {
*/ */
@TableId @TableId
private Long id; private Long id;
/**
* 标题
*/
private String characterName;
/**
* 背景描述
*/
private String characterBackground;
/** /**
* 角色头像 * 角色头像
*/ */

View File

@@ -1,6 +1,7 @@
package com.yolo.keyboard.dal.dataobject.productitems; package com.yolo.keyboard.dal.dataobject.productitems;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore; import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -77,6 +78,10 @@ public class KeyboardProductItemsDO{
* 订阅时长的具体天数 * 订阅时长的具体天数
*/ */
private Integer durationDays; private Integer durationDays;
/**
* 订阅等级
*/
private Integer level;
private String platform;
} }

View File

@@ -14,7 +14,7 @@ import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO;
* @author ziin * @author ziin
*/ */
@TableName("keyboard_tag") @TableName("keyboard_tag")
@KeySequence("keyboard_tag_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @KeySequence("keyboard_tag_id_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data @Data
@ToString(callSuper = true) @ToString(callSuper = true)
@Builder @Builder
@@ -28,10 +28,6 @@ public class KeyboardTagDO{
*/ */
@TableId @TableId
private Integer id; private Integer id;
/**
* 标签名
*/
private String tagName;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@@ -0,0 +1,52 @@
package com.yolo.keyboard.dal.dataobject.tag;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO;
/**
* 人设标签国际化 DO
*
* @author 芋道源码
*/
@TableName("keyboard_tag_i18n")
@KeySequence("keyboard_tag_i18n_id_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TenantIgnore
public class TagI18nDO {
/**
* 主键 Id
*/
@TableId
private Long id;
/**
* 标签主表ID对应 keyboard_tag.id
*/
private Integer tagId;
/**
* 语言标识,如 zh-CN、en-US、ja-JP
*/
private String locale;
/**
* 标签名称(多语言)
*/
private String tagName;
/**
* 创建时间
*/
private LocalDateTime createdAt;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
}

View File

@@ -1,6 +1,7 @@
package com.yolo.keyboard.dal.dataobject.themes; package com.yolo.keyboard.dal.dataobject.themes;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore; import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -88,5 +89,5 @@ public class KeyboardThemesDO {
*/ */
private Long realDownloadCount; private Long realDownloadCount;
private String local;
} }

View File

@@ -1,5 +1,6 @@
package com.yolo.keyboard.dal.dataobject.themestyles; package com.yolo.keyboard.dal.dataobject.themestyles;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore; import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -38,5 +39,5 @@ public class KeyboardThemeStylesDO {
*/ */
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
private String local;
} }

View File

@@ -0,0 +1,48 @@
package com.yolo.keyboard.dal.dataobject.warningmessage;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.yolo.keyboard.framework.mybatis.core.dataobject.BaseDO;
/**
* 用户注销提示信息 DO
*
* @author ziin
*/
@TableName("keyboard_warning_message")
@KeySequence("keyboard_warning_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TenantIgnore
public class KeyboardWarningMessageDO{
/**
* 主键
*/
@TableId
private Long id;
/**
* 地区
*/
private String locale;
/**
* 正文
*/
private String content;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
/**
* 创建时间
*/
private LocalDateTime created;
}

View File

@@ -0,0 +1,34 @@
package com.yolo.keyboard.dal.mysql.aicompanion;
import java.util.*;
import com.yolo.keyboard.dal.dataobject.aicompanion.AiCompanionI18nDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍 Mapper
*
* @author ziin
*/
@Mapper
public interface AiCompanionI18nMapper extends BaseMapperX<AiCompanionI18nDO> {
default PageResult<AiCompanionI18nDO> selectPage(PageParam reqVO, Long id) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiCompanionI18nDO>()
.eq(AiCompanionI18nDO::getCompanionId, id)
.orderByDesc(AiCompanionI18nDO::getId));
}
default int deleteById(Long id) {
return delete(AiCompanionI18nDO::getId, id);
}
default int deleteByIds(List<Long> ids) {
return deleteBatch(AiCompanionI18nDO::getId, ids);
}
}

View File

@@ -19,13 +19,10 @@ public interface KeyboardAiCompanionMapper extends BaseMapperX<KeyboardAiCompani
default PageResult<KeyboardAiCompanionDO> selectPage(KeyboardAiCompanionPageReqVO reqVO) { default PageResult<KeyboardAiCompanionDO> selectPage(KeyboardAiCompanionPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardAiCompanionDO>() return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardAiCompanionDO>()
.likeIfPresent(KeyboardAiCompanionDO::getName, reqVO.getName())
.eqIfPresent(KeyboardAiCompanionDO::getAvatarUrl, reqVO.getAvatarUrl()) .eqIfPresent(KeyboardAiCompanionDO::getAvatarUrl, reqVO.getAvatarUrl())
.eqIfPresent(KeyboardAiCompanionDO::getCoverImageUrl, reqVO.getCoverImageUrl()) .eqIfPresent(KeyboardAiCompanionDO::getCoverImageUrl, reqVO.getCoverImageUrl())
.eqIfPresent(KeyboardAiCompanionDO::getGender, reqVO.getGender()) .eqIfPresent(KeyboardAiCompanionDO::getGender, reqVO.getGender())
.eqIfPresent(KeyboardAiCompanionDO::getAgeRange, reqVO.getAgeRange()) .eqIfPresent(KeyboardAiCompanionDO::getAgeRange, reqVO.getAgeRange())
.eqIfPresent(KeyboardAiCompanionDO::getShortDesc, reqVO.getShortDesc())
.eqIfPresent(KeyboardAiCompanionDO::getIntroText, reqVO.getIntroText())
.eqIfPresent(KeyboardAiCompanionDO::getPersonalityTags, reqVO.getPersonalityTags()) .eqIfPresent(KeyboardAiCompanionDO::getPersonalityTags, reqVO.getPersonalityTags())
.eqIfPresent(KeyboardAiCompanionDO::getSpeakingStyle, reqVO.getSpeakingStyle()) .eqIfPresent(KeyboardAiCompanionDO::getSpeakingStyle, reqVO.getSpeakingStyle())
.eqIfPresent(KeyboardAiCompanionDO::getSystemPrompt, reqVO.getSystemPrompt()) .eqIfPresent(KeyboardAiCompanionDO::getSystemPrompt, reqVO.getSystemPrompt())
@@ -35,8 +32,6 @@ public interface KeyboardAiCompanionMapper extends BaseMapperX<KeyboardAiCompani
.eqIfPresent(KeyboardAiCompanionDO::getPopularityScore, reqVO.getPopularityScore()) .eqIfPresent(KeyboardAiCompanionDO::getPopularityScore, reqVO.getPopularityScore())
.eqIfPresent(KeyboardAiCompanionDO::getCreatedAt, reqVO.getCreatedAt()) .eqIfPresent(KeyboardAiCompanionDO::getCreatedAt, reqVO.getCreatedAt())
.eqIfPresent(KeyboardAiCompanionDO::getUpdatedAt, reqVO.getUpdatedAt()) .eqIfPresent(KeyboardAiCompanionDO::getUpdatedAt, reqVO.getUpdatedAt())
.eqIfPresent(KeyboardAiCompanionDO::getPrologue, reqVO.getPrologue())
.eqIfPresent(KeyboardAiCompanionDO::getPrologueAudio, reqVO.getPrologueAudio())
.eqIfPresent(KeyboardAiCompanionDO::getVoiceId, reqVO.getVoiceId()) .eqIfPresent(KeyboardAiCompanionDO::getVoiceId, reqVO.getVoiceId())
.orderByDesc(KeyboardAiCompanionDO::getId)); .orderByDesc(KeyboardAiCompanionDO::getId));
} }

View File

@@ -0,0 +1,41 @@
package com.yolo.keyboard.dal.mysql.appversions;
import java.util.*;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX;
import com.yolo.keyboard.dal.dataobject.appversions.AppVersionsDO;
import org.apache.ibatis.annotations.Mapper;
import com.yolo.keyboard.controller.admin.appversions.vo.*;
/**
* App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 Mapper
*
* @author ziin
*/
@Mapper
public interface AppVersionsMapper extends BaseMapperX<AppVersionsDO> {
default PageResult<AppVersionsDO> selectPage(AppVersionsPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AppVersionsDO>()
.eqIfPresent(AppVersionsDO::getAppId, reqVO.getAppId())
.eqIfPresent(AppVersionsDO::getPlatform, reqVO.getPlatform())
.eqIfPresent(AppVersionsDO::getChannel, reqVO.getChannel())
.likeIfPresent(AppVersionsDO::getVersionName, reqVO.getVersionName())
.eqIfPresent(AppVersionsDO::getVersionCode, reqVO.getVersionCode())
.eqIfPresent(AppVersionsDO::getBuildNumber, reqVO.getBuildNumber())
.eqIfPresent(AppVersionsDO::getMinSupportedCode, reqVO.getMinSupportedCode())
.betweenIfPresent(AppVersionsDO::getIsForceUpdate, reqVO.getIsForceUpdate())
.eqIfPresent(AppVersionsDO::getIsActive, reqVO.getIsActive())
.eqIfPresent(AppVersionsDO::getReleaseNotes, reqVO.getReleaseNotes())
.eqIfPresent(AppVersionsDO::getDownloadUrl, reqVO.getDownloadUrl())
.eqIfPresent(AppVersionsDO::getStoreUrl, reqVO.getStoreUrl())
.eqIfPresent(AppVersionsDO::getMetadata, reqVO.getMetadata())
.eqIfPresent(AppVersionsDO::getReleasedAt, reqVO.getReleasedAt())
.eqIfPresent(AppVersionsDO::getCreatedAt, reqVO.getCreatedAt())
.eqIfPresent(AppVersionsDO::getUpdatedAt, reqVO.getUpdatedAt())
.orderByDesc(AppVersionsDO::getId));
}
}

View File

@@ -0,0 +1,34 @@
package com.yolo.keyboard.dal.mysql.character;
import java.util.*;
import com.yolo.keyboard.dal.dataobject.character.CharacterI18nDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* 键盘人设国际化内容 Mapper
*
* @author ziin
*/
@Mapper
public interface CharacterI18nMapper extends BaseMapperX<CharacterI18nDO> {
default PageResult<CharacterI18nDO> selectPage(PageParam reqVO, Long characterId) {
return selectPage(reqVO, new LambdaQueryWrapperX<CharacterI18nDO>()
.eq(CharacterI18nDO::getCharacterId, characterId)
.orderByDesc(CharacterI18nDO::getId));
}
default int deleteByCharacterId(Long characterId) {
return delete(CharacterI18nDO::getCharacterId, characterId);
}
default int deleteByCharacterIds(List<Long> characterIds) {
return deleteBatch(CharacterI18nDO::getCharacterId, characterIds);
}
}

View File

@@ -19,8 +19,6 @@ public interface KeyboardCharacterMapper extends BaseMapperX<KeyboardCharacterDO
default PageResult<KeyboardCharacterDO> selectPage(KeyboardCharacterPageReqVO reqVO) { default PageResult<KeyboardCharacterDO> selectPage(KeyboardCharacterPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardCharacterDO>() return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardCharacterDO>()
.likeIfPresent(KeyboardCharacterDO::getCharacterName, reqVO.getCharacterName())
.eqIfPresent(KeyboardCharacterDO::getCharacterBackground, reqVO.getCharacterBackground())
.eqIfPresent(KeyboardCharacterDO::getAvatarUrl, reqVO.getAvatarUrl()) .eqIfPresent(KeyboardCharacterDO::getAvatarUrl, reqVO.getAvatarUrl())
.eqIfPresent(KeyboardCharacterDO::getDownload, reqVO.getDownload()) .eqIfPresent(KeyboardCharacterDO::getDownload, reqVO.getDownload())
.eqIfPresent(KeyboardCharacterDO::getTag, reqVO.getTag()) .eqIfPresent(KeyboardCharacterDO::getTag, reqVO.getTag())

View File

@@ -19,7 +19,6 @@ public interface KeyboardTagMapper extends BaseMapperX<KeyboardTagDO> {
default PageResult<KeyboardTagDO> selectPage(KeyboardTagPageReqVO reqVO) { default PageResult<KeyboardTagDO> selectPage(KeyboardTagPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTagDO>() return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardTagDO>()
.likeIfPresent(KeyboardTagDO::getTagName, reqVO.getTagName())
.eqIfPresent(KeyboardTagDO::getCreatedAt, reqVO.getCreatedAt()) .eqIfPresent(KeyboardTagDO::getCreatedAt, reqVO.getCreatedAt())
.eqIfPresent(KeyboardTagDO::getUpdatedAt, reqVO.getUpdatedAt()) .eqIfPresent(KeyboardTagDO::getUpdatedAt, reqVO.getUpdatedAt())
.orderByDesc(KeyboardTagDO::getId)); .orderByDesc(KeyboardTagDO::getId));

View File

@@ -0,0 +1,34 @@
package com.yolo.keyboard.dal.mysql.tag;
import java.util.*;
import com.yolo.keyboard.dal.dataobject.tag.TagI18nDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* 人设标签国际化 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface TagI18nMapper extends BaseMapperX<TagI18nDO> {
default PageResult<TagI18nDO> selectPage(PageParam reqVO, Integer tagId) {
return selectPage(reqVO, new LambdaQueryWrapperX<TagI18nDO>()
.eq(TagI18nDO::getTagId, tagId)
.orderByDesc(TagI18nDO::getId));
}
default int deleteByTagId(Integer tagId) {
return delete(TagI18nDO::getTagId, tagId);
}
default int deleteByTagIds(List<Integer> tagIds) {
return deleteBatch(TagI18nDO::getTagId, tagIds);
}
}

View File

@@ -0,0 +1,29 @@
package com.yolo.keyboard.dal.mysql.warningmessage;
import java.util.*;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.mybatis.core.mapper.BaseMapperX;
import com.yolo.keyboard.dal.dataobject.warningmessage.KeyboardWarningMessageDO;
import org.apache.ibatis.annotations.Mapper;
import com.yolo.keyboard.controller.admin.warningmessage.vo.*;
/**
* 用户注销提示信息 Mapper
*
* @author ziin
*/
@Mapper
public interface KeyboardWarningMessageMapper extends BaseMapperX<KeyboardWarningMessageDO> {
default PageResult<KeyboardWarningMessageDO> selectPage(KeyboardWarningMessagePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<KeyboardWarningMessageDO>()
.eqIfPresent(KeyboardWarningMessageDO::getLocale, reqVO.getLocale())
.eqIfPresent(KeyboardWarningMessageDO::getContent, reqVO.getContent())
.eqIfPresent(KeyboardWarningMessageDO::getUpdatedAt, reqVO.getUpdatedAt())
.eqIfPresent(KeyboardWarningMessageDO::getCreated, reqVO.getCreated())
.orderByDesc(KeyboardWarningMessageDO::getId));
}
}

View File

@@ -0,0 +1,63 @@
package com.yolo.keyboard.job;
import cn.hutool.core.collection.CollUtil;
import com.yolo.keyboard.dal.dataobject.user.KeyboardUserDO;
import com.yolo.keyboard.dal.mysql.user.KeyboardUserMapper;
import com.yolo.keyboard.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yolo.keyboard.framework.quartz.core.handler.JobHandler;
import com.yolo.keyboard.framework.tenant.core.aop.TenantIgnore;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
/**
* VIP 到期检查定时任务
* 定期执行,将 VIP 已过期的用户关闭 VIP 权限
*
* @author ziin
*/
@Component
@Slf4j
public class VipExpiryCheckJob implements JobHandler {
@Resource
private KeyboardUserMapper userMapper;
@Override
@TenantIgnore
public String execute(String param) {
log.info("[VipExpiryCheckJob] 开始执行 VIP 到期检查任务");
LocalDateTime now = LocalDateTime.now();
// 查询 VIP 已过期且仍处于 VIP 状态的用户
List<KeyboardUserDO> expiredUsers = userMapper.selectList(
new LambdaQueryWrapperX<KeyboardUserDO>()
.eq(KeyboardUserDO::getIsVip, true)
.le(KeyboardUserDO::getVipExpiry, now)
);
if (CollUtil.isEmpty(expiredUsers)) {
log.info("[VipExpiryCheckJob] 没有需要处理的过期 VIP 用户");
return "没有需要处理的过期 VIP 用户";
}
int count = 0;
for (KeyboardUserDO user : expiredUsers) {
user.setIsVip(false);
user.setUpdatedAt(now);
userMapper.updateById(user);
count++;
log.info("[VipExpiryCheckJob] 用户 {} VIP 已过期,已关闭 VIP 权限,过期时间: {}",
user.getUid(), user.getVipExpiry());
}
String result = String.format("共处理 %d 个过期 VIP 用户", count);
log.info("[VipExpiryCheckJob] 任务执行完成: {}", result);
return result;
}
}

View File

@@ -2,7 +2,9 @@ package com.yolo.keyboard.service.aicompanion;
import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionPageReqVO; import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionPageReqVO;
import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionSaveReqVO; import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionSaveReqVO;
import com.yolo.keyboard.dal.dataobject.aicompanion.AiCompanionI18nDO;
import com.yolo.keyboard.dal.dataobject.aicompanion.KeyboardAiCompanionDO; import com.yolo.keyboard.dal.dataobject.aicompanion.KeyboardAiCompanionDO;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult; import com.yolo.keyboard.framework.common.pojo.PageResult;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@@ -59,5 +61,51 @@ public interface KeyboardAiCompanionService {
* @return AI陪聊角色表用于定义恋爱/陪伴型虚拟角色的基础信息与人设分页 * @return AI陪聊角色表用于定义恋爱/陪伴型虚拟角色的基础信息与人设分页
*/ */
PageResult<KeyboardAiCompanionDO> getAiCompanionPage(KeyboardAiCompanionPageReqVO pageReqVO); PageResult<KeyboardAiCompanionDO> getAiCompanionPage(KeyboardAiCompanionPageReqVO pageReqVO);
// ==================== 子表AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍 ====================
/**
* 获得AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍分页
*
* @param pageReqVO 分页查询
* @param id 主键ID
* @return AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍分页
*/
PageResult<AiCompanionI18nDO> getAiCompanionI18nPage(PageParam pageReqVO, Long id);
/**
* 创建AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*
* @param aiCompanionI18n 创建信息
* @return 编号
*/
Long createAiCompanionI18n(@Valid AiCompanionI18nDO aiCompanionI18n);
/**
* 更新AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*
* @param aiCompanionI18n 更新信息
*/
void updateAiCompanionI18n(@Valid AiCompanionI18nDO aiCompanionI18n);
/**
* 删除AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*
* @param id 编号
*/
void deleteAiCompanionI18n(Long id);
/**
* 批量删除AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*
* @param ids 编号
*/
void deleteAiCompanionI18nListByIds(List<Long> ids);
/**
* 获得AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*
* @param id 编号
* @return AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍
*/
AiCompanionI18nDO getAiCompanionI18n(Long id);
} }

View File

@@ -2,17 +2,22 @@ package com.yolo.keyboard.service.aicompanion;
import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionPageReqVO; import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionPageReqVO;
import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionSaveReqVO; import com.yolo.keyboard.controller.admin.aicompanion.vo.KeyboardAiCompanionSaveReqVO;
import com.yolo.keyboard.dal.dataobject.aicompanion.AiCompanionI18nDO;
import com.yolo.keyboard.dal.dataobject.aicompanion.KeyboardAiCompanionDO; import com.yolo.keyboard.dal.dataobject.aicompanion.KeyboardAiCompanionDO;
import com.yolo.keyboard.dal.mysql.aicompanion.AiCompanionI18nMapper;
import com.yolo.keyboard.dal.mysql.aicompanion.KeyboardAiCompanionMapper; import com.yolo.keyboard.dal.mysql.aicompanion.KeyboardAiCompanionMapper;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult; import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils; import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.util.List; import java.util.List;
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.AI_COMPANION_I18N_NOT_EXISTS;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.AI_COMPANION_NOT_EXISTS; import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.AI_COMPANION_NOT_EXISTS;
/** /**
@@ -26,6 +31,8 @@ public class KeyboardAiCompanionServiceImpl implements KeyboardAiCompanionServic
@Resource @Resource
private KeyboardAiCompanionMapper aiCompanionMapper; private KeyboardAiCompanionMapper aiCompanionMapper;
@Resource
private AiCompanionI18nMapper aiCompanionI18nMapper;
@Override @Override
public Long createAiCompanion(KeyboardAiCompanionSaveReqVO createReqVO) { public Long createAiCompanion(KeyboardAiCompanionSaveReqVO createReqVO) {
@@ -47,18 +54,26 @@ public class KeyboardAiCompanionServiceImpl implements KeyboardAiCompanionServic
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void deleteAiCompanion(Long id) { public void deleteAiCompanion(Long id) {
// 校验存在 // 校验存在
validateAiCompanionExists(id); validateAiCompanionExists(id);
// 删除 // 删除
aiCompanionMapper.deleteById(id); aiCompanionMapper.deleteById(id);
// 删除子表
deleteAiCompanionI18nById(id);
} }
@Override @Override
public void deleteAiCompanionListByIds(List<Long> ids) { @Transactional(rollbackFor = Exception.class)
public void deleteAiCompanionListByIds(List<Long> ids) {
// 删除 // 删除
aiCompanionMapper.deleteByIds(ids); aiCompanionMapper.deleteByIds(ids);
}
// 删除子表
deleteAiCompanionI18nByIds(ids);
}
private void validateAiCompanionExists(Long id) { private void validateAiCompanionExists(Long id) {
@@ -77,4 +92,56 @@ public class KeyboardAiCompanionServiceImpl implements KeyboardAiCompanionServic
return aiCompanionMapper.selectPage(pageReqVO); return aiCompanionMapper.selectPage(pageReqVO);
} }
// ==================== 子表AI陪聊角色国际化表用于存储不同语言下的角色名称、一句话描述和详细介绍 ====================
@Override
public PageResult<AiCompanionI18nDO> getAiCompanionI18nPage(PageParam pageReqVO, Long id) {
return aiCompanionI18nMapper.selectPage(pageReqVO, id);
}
@Override
public Long createAiCompanionI18n(AiCompanionI18nDO aiCompanionI18n) {
aiCompanionI18nMapper.insert(aiCompanionI18n);
return aiCompanionI18n.getId();
}
@Override
public void updateAiCompanionI18n(AiCompanionI18nDO aiCompanionI18n) {
// 校验存在
validateAiCompanionI18nExists(aiCompanionI18n.getId());
// 更新
aiCompanionI18nMapper.updateById(aiCompanionI18n);
}
@Override
public void deleteAiCompanionI18n(Long id) {
// 删除
aiCompanionI18nMapper.deleteById(id);
}
@Override
public void deleteAiCompanionI18nListByIds(List<Long> ids) {
// 删除
aiCompanionI18nMapper.deleteByIds(ids);
}
@Override
public AiCompanionI18nDO getAiCompanionI18n(Long id) {
return aiCompanionI18nMapper.selectById(id);
}
private void validateAiCompanionI18nExists(Long id) {
if (aiCompanionI18nMapper.selectById(id) == null) {
throw exception(AI_COMPANION_I18N_NOT_EXISTS);
}
}
private void deleteAiCompanionI18nById(Long id) {
aiCompanionI18nMapper.deleteById(id);
}
private void deleteAiCompanionI18nByIds(List<Long> ids) {
aiCompanionI18nMapper.deleteByIds(ids);
}
} }

View File

@@ -0,0 +1,62 @@
package com.yolo.keyboard.service.appversions;
import java.util.*;
import jakarta.validation.*;
import com.yolo.keyboard.controller.admin.appversions.vo.*;
import com.yolo.keyboard.dal.dataobject.appversions.AppVersionsDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
/**
* App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 Service 接口
*
* @author ziin
*/
public interface AppVersionsService {
/**
* 创建App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createAppVersions(@Valid AppVersionsSaveReqVO createReqVO);
/**
* 更新App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*
* @param updateReqVO 更新信息
*/
void updateAppVersions(@Valid AppVersionsSaveReqVO updateReqVO);
/**
* 删除App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*
* @param id 编号
*/
void deleteAppVersions(Long id);
/**
* 批量删除App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*
* @param ids 编号
*/
void deleteAppVersionsListByIds(List<Long> ids);
/**
* 获得App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*
* @param id 编号
* @return App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。
*/
AppVersionsDO getAppVersions(Long id);
/**
* 获得App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。分页
*
* @param pageReqVO 分页查询
* @return App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。分页
*/
PageResult<AppVersionsDO> getAppVersionsPage(AppVersionsPageReqVO pageReqVO);
}

View File

@@ -0,0 +1,84 @@
package com.yolo.keyboard.service.appversions;
import cn.hutool.core.collection.CollUtil;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import com.yolo.keyboard.controller.admin.appversions.vo.*;
import com.yolo.keyboard.dal.dataobject.appversions.AppVersionsDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import com.yolo.keyboard.dal.mysql.appversions.AppVersionsMapper;
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.convertList;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.diffList;import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.APP_VERSIONS_NOT_EXISTS;
/**
* App 版本发布与更新检查表:区分 Android/iOS、渠道支持最低支持版本与强更策略。 Service 实现类
*
* @author ziin
*/
@Service
@Validated
public class AppVersionsServiceImpl implements AppVersionsService {
@Resource
private AppVersionsMapper appVersionsMapper;
@Override
public Long createAppVersions(AppVersionsSaveReqVO createReqVO) {
// 插入
AppVersionsDO appVersions = BeanUtils.toBean(createReqVO, AppVersionsDO.class);
appVersionsMapper.insert(appVersions);
// 返回
return appVersions.getId();
}
@Override
public void updateAppVersions(AppVersionsSaveReqVO updateReqVO) {
// 校验存在
validateAppVersionsExists(updateReqVO.getId());
// 更新
AppVersionsDO updateObj = BeanUtils.toBean(updateReqVO, AppVersionsDO.class);
appVersionsMapper.updateById(updateObj);
}
@Override
public void deleteAppVersions(Long id) {
// 校验存在
validateAppVersionsExists(id);
// 删除
appVersionsMapper.deleteById(id);
}
@Override
public void deleteAppVersionsListByIds(List<Long> ids) {
// 删除
appVersionsMapper.deleteByIds(ids);
}
private void validateAppVersionsExists(Long id) {
if (appVersionsMapper.selectById(id) == null) {
throw exception(APP_VERSIONS_NOT_EXISTS);
}
}
@Override
public AppVersionsDO getAppVersions(Long id) {
return appVersionsMapper.selectById(id);
}
@Override
public PageResult<AppVersionsDO> getAppVersionsPage(AppVersionsPageReqVO pageReqVO) {
return appVersionsMapper.selectPage(pageReqVO);
}
}

View File

@@ -1,6 +1,9 @@
package com.yolo.keyboard.service.character; package com.yolo.keyboard.service.character;
import java.util.*; import java.util.*;
import com.yolo.keyboard.dal.dataobject.character.CharacterI18nDO;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import jakarta.validation.*; import jakarta.validation.*;
import com.yolo.keyboard.controller.admin.character.vo.*; import com.yolo.keyboard.controller.admin.character.vo.*;
import com.yolo.keyboard.dal.dataobject.character.KeyboardCharacterDO; import com.yolo.keyboard.dal.dataobject.character.KeyboardCharacterDO;
@@ -57,5 +60,51 @@ public interface KeyboardCharacterService {
* @return 键盘人设分页 * @return 键盘人设分页
*/ */
PageResult<KeyboardCharacterDO> getCharacterPage(KeyboardCharacterPageReqVO pageReqVO); PageResult<KeyboardCharacterDO> getCharacterPage(KeyboardCharacterPageReqVO pageReqVO);
// ==================== 子表(键盘人设国际化内容) ====================
/**
* 获得键盘人设国际化内容分页
*
* @param pageReqVO 分页查询
* @param characterId 角色主表id
* @return 键盘人设国际化内容分页
*/
PageResult<CharacterI18nDO> getCharacterI18nPage(PageParam pageReqVO, Long characterId);
/**
* 创建键盘人设国际化内容
*
* @param characterI18n 创建信息
* @return 编号
*/
Long createCharacterI18n(@Valid CharacterI18nDO characterI18n);
/**
* 更新键盘人设国际化内容
*
* @param characterI18n 更新信息
*/
void updateCharacterI18n(@Valid CharacterI18nDO characterI18n);
/**
* 删除键盘人设国际化内容
*
* @param id 编号
*/
void deleteCharacterI18n(Long id);
/**
* 批量删除键盘人设国际化内容
*
* @param ids 编号
*/
void deleteCharacterI18nListByIds(List<Long> ids);
/**
* 获得键盘人设国际化内容
*
* @param id 编号
* @return 键盘人设国际化内容
*/
CharacterI18nDO getCharacterI18n(Long id);
} }

View File

@@ -1,18 +1,23 @@
package com.yolo.keyboard.service.character; package com.yolo.keyboard.service.character;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import com.yolo.keyboard.controller.admin.character.vo.*; import com.yolo.keyboard.controller.admin.character.vo.*;
import com.yolo.keyboard.dal.dataobject.character.CharacterI18nDO;
import com.yolo.keyboard.dal.dataobject.character.KeyboardCharacterDO; import com.yolo.keyboard.dal.dataobject.character.KeyboardCharacterDO;
import com.yolo.keyboard.dal.mysql.character.CharacterI18nMapper;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult; import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils; import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import com.yolo.keyboard.dal.mysql.character.KeyboardCharacterMapper; import com.yolo.keyboard.dal.mysql.character.KeyboardCharacterMapper;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_I18N_NOT_EXISTS;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_NOT_EXISTS; import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_NOT_EXISTS;
@@ -25,15 +30,27 @@ import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_
@Validated @Validated
public class KeyboardCharacterServiceImpl implements KeyboardCharacterService { public class KeyboardCharacterServiceImpl implements KeyboardCharacterService {
private static final String CHARACTER_CACHE_KEY_PREFIX = "character:";
private static final long CHARACTER_CACHE_TTL_MINUTES = 5L;
@Resource
private CharacterI18nMapper characterI18nMapper;
@Resource @Resource
private KeyboardCharacterMapper characterMapper; private KeyboardCharacterMapper characterMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override @Override
public Long createCharacter(KeyboardCharacterSaveReqVO createReqVO) { public Long createCharacter(KeyboardCharacterSaveReqVO createReqVO) {
// 插入 // 插入
KeyboardCharacterDO character = BeanUtils.toBean(createReqVO, KeyboardCharacterDO.class); KeyboardCharacterDO character = BeanUtils.toBean(createReqVO, KeyboardCharacterDO.class);
characterMapper.insert(character); characterMapper.insert(character);
// 同步刷新 Redis 缓存,保证缓存内容与数据库最终落库数据一致
refreshCharacterCache(character.getId());
// 返回 // 返回
return character.getId(); return character.getId();
} }
@@ -45,6 +62,9 @@ public class KeyboardCharacterServiceImpl implements KeyboardCharacterService {
// 更新 // 更新
KeyboardCharacterDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardCharacterDO.class); KeyboardCharacterDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardCharacterDO.class);
characterMapper.updateById(updateObj); characterMapper.updateById(updateObj);
// 删除 Redis 缓存,下次读取时再从数据库加载最新数据
deleteCharacterCache(updateReqVO.getId());
} }
@Override @Override
@@ -53,13 +73,72 @@ public class KeyboardCharacterServiceImpl implements KeyboardCharacterService {
validateCharacterExists(id); validateCharacterExists(id);
// 删除 // 删除
characterMapper.deleteById(id); characterMapper.deleteById(id);
// 同步删除 Redis 缓存,避免读取到已删除数据
deleteCharacterCache(id);
} }
@Override @Override
public void deleteCharacterListByIds(List<Long> ids) { public void deleteCharacterListByIds(List<Long> ids) {
// 删除 // 删除
characterMapper.deleteByIds(ids); characterMapper.deleteByIds(ids);
// 同步删除 Redis 缓存,避免批量删除后残留脏数据
deleteCharacterCaches(ids);
}
// ==================== 子表(键盘人设国际化内容) ====================
@Override
public PageResult<CharacterI18nDO> getCharacterI18nPage(PageParam pageReqVO, Long characterId) {
return characterI18nMapper.selectPage(pageReqVO, characterId);
}
@Override
public Long createCharacterI18n(CharacterI18nDO characterI18n) {
characterI18nMapper.insert(characterI18n);
return characterI18n.getId();
}
@Override
public void updateCharacterI18n(CharacterI18nDO characterI18n) {
// 校验存在
validateCharacterI18nExists(characterI18n.getId());
// 更新
characterI18nMapper.updateById(characterI18n);
}
@Override
public void deleteCharacterI18n(Long id) {
// 删除
characterI18nMapper.deleteById(id);
}
@Override
public void deleteCharacterI18nListByIds(List<Long> ids) {
// 删除
characterI18nMapper.deleteByIds(ids);
}
@Override
public CharacterI18nDO getCharacterI18n(Long id) {
return characterI18nMapper.selectById(id);
}
private void validateCharacterI18nExists(Long id) {
if (characterI18nMapper.selectById(id) == null) {
throw exception(CHARACTER_I18N_NOT_EXISTS);
} }
}
private void deleteCharacterI18nByCharacterId(Long characterId) {
characterI18nMapper.deleteByCharacterId(characterId);
}
private void deleteCharacterI18nByCharacterIds(List<Long> characterIds) {
characterI18nMapper.deleteByCharacterIds(characterIds);
}
private void validateCharacterExists(Long id) { private void validateCharacterExists(Long id) {
@@ -78,4 +157,43 @@ public class KeyboardCharacterServiceImpl implements KeyboardCharacterService {
return characterMapper.selectPage(pageReqVO); return characterMapper.selectPage(pageReqVO);
} }
} private void refreshCharacterCache(Long characterId) {
if (characterId == null) {
return;
}
String cacheKey = CHARACTER_CACHE_KEY_PREFIX + characterId;
KeyboardCharacterDO latestCharacter = characterMapper.selectById(characterId);
if (latestCharacter == null) {
redisTemplate.delete(cacheKey);
return;
}
redisTemplate.opsForValue().set(cacheKey, latestCharacter, CHARACTER_CACHE_TTL_MINUTES, TimeUnit.MINUTES);
}
private void deleteCharacterCache(Long characterId) {
if (characterId == null) {
return;
}
redisTemplate.delete(buildCharacterCacheKey(characterId));
}
private void deleteCharacterCaches(List<Long> characterIds) {
if (characterIds == null || characterIds.isEmpty()) {
return;
}
Set<String> cacheKeys = new HashSet<>();
for (Long characterId : characterIds) {
if (characterId != null) {
cacheKeys.add(buildCharacterCacheKey(characterId));
}
}
if (!cacheKeys.isEmpty()) {
redisTemplate.delete(cacheKeys);
}
}
private String buildCharacterCacheKey(Long characterId) {
return CHARACTER_CACHE_KEY_PREFIX + characterId;
}
}

View File

@@ -1,6 +1,8 @@
package com.yolo.keyboard.service.tag; package com.yolo.keyboard.service.tag;
import java.util.*; import java.util.*;
import com.yolo.keyboard.dal.dataobject.tag.TagI18nDO;
import jakarta.validation.*; import jakarta.validation.*;
import com.yolo.keyboard.controller.admin.tag.vo.*; import com.yolo.keyboard.controller.admin.tag.vo.*;
import com.yolo.keyboard.dal.dataobject.tag.KeyboardTagDO; import com.yolo.keyboard.dal.dataobject.tag.KeyboardTagDO;
@@ -58,5 +60,51 @@ public interface KeyboardTagService {
* @return 人设标签分页 * @return 人设标签分页
*/ */
PageResult<KeyboardTagDO> getTagPage(KeyboardTagPageReqVO pageReqVO); PageResult<KeyboardTagDO> getTagPage(KeyboardTagPageReqVO pageReqVO);
// ==================== 子表(人设标签国际化) ====================
/**
* 获得人设标签国际化分页
*
* @param pageReqVO 分页查询
* @param tagId 标签主表ID对应 keyboard_tag.id
* @return 人设标签国际化分页
*/
PageResult<TagI18nDO> getTagI18nPage(PageParam pageReqVO, Integer tagId);
/**
* 创建人设标签国际化
*
* @param tagI18n 创建信息
* @return 编号
*/
Long createTagI18n(@Valid TagI18nDO tagI18n);
/**
* 更新人设标签国际化
*
* @param tagI18n 更新信息
*/
void updateTagI18n(@Valid TagI18nDO tagI18n);
/**
* 删除人设标签国际化
*
* @param id 编号
*/
void deleteTagI18n(Long id);
/**
* 批量删除人设标签国际化
*
* @param ids 编号
*/
void deleteTagI18nListByIds(List<Long> ids);
/**
* 获得人设标签国际化
*
* @param id 编号
* @return 人设标签国际化
*/
TagI18nDO getTagI18n(Long id);
} }

View File

@@ -1,6 +1,8 @@
package com.yolo.keyboard.service.tag; package com.yolo.keyboard.service.tag;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.yolo.keyboard.dal.dataobject.tag.TagI18nDO;
import com.yolo.keyboard.dal.mysql.tag.TagI18nMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -18,6 +20,7 @@ import com.yolo.keyboard.dal.mysql.tag.KeyboardTagMapper;
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.convertList; import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.convertList;
import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.diffList; import static com.yolo.keyboard.framework.common.util.collection.CollectionUtils.diffList;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TAG_I18N_NOT_EXISTS;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TAG_NOT_EXISTS; import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.TAG_NOT_EXISTS;
/** /**
@@ -32,6 +35,9 @@ public class KeyboardTagServiceImpl implements KeyboardTagService {
@Resource @Resource
private KeyboardTagMapper tagMapper; private KeyboardTagMapper tagMapper;
@Resource
private TagI18nMapper tagI18nMapper;
@Override @Override
public Integer createTag(KeyboardTagSaveReqVO createReqVO) { public Integer createTag(KeyboardTagSaveReqVO createReqVO) {
// 插入 // 插入
@@ -82,4 +88,55 @@ public class KeyboardTagServiceImpl implements KeyboardTagService {
return tagMapper.selectPage(pageReqVO); return tagMapper.selectPage(pageReqVO);
} }
// ==================== 子表(人设标签国际化) ====================
@Override
public PageResult<TagI18nDO> getTagI18nPage(PageParam pageReqVO, Integer tagId) {
return tagI18nMapper.selectPage(pageReqVO, tagId);
}
@Override
public Long createTagI18n(TagI18nDO tagI18n) {
tagI18nMapper.insert(tagI18n);
return tagI18n.getId();
}
@Override
public void updateTagI18n(TagI18nDO tagI18n) {
// 校验存在
validateTagI18nExists(tagI18n.getId());
// 更新
tagI18nMapper.updateById(tagI18n);
}
@Override
public void deleteTagI18n(Long id) {
// 删除
tagI18nMapper.deleteById(id);
}
@Override
public void deleteTagI18nListByIds(List<Long> ids) {
// 删除
tagI18nMapper.deleteByIds(ids);
}
@Override
public TagI18nDO getTagI18n(Long id) {
return tagI18nMapper.selectById(id);
}
private void validateTagI18nExists(Long id) {
if (tagI18nMapper.selectById(id) == null) {
throw exception(TAG_I18N_NOT_EXISTS);
}
}
private void deleteTagI18nByTagId(Integer tagId) {
tagI18nMapper.deleteByTagId(tagId);
}
private void deleteTagI18nByTagIds(List<Integer> tagIds) {
tagI18nMapper.deleteByTagIds(tagIds);
}
} }

View File

@@ -0,0 +1,62 @@
package com.yolo.keyboard.service.warningmessage;
import java.util.*;
import jakarta.validation.*;
import com.yolo.keyboard.controller.admin.warningmessage.vo.*;
import com.yolo.keyboard.dal.dataobject.warningmessage.KeyboardWarningMessageDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.pojo.PageParam;
/**
* 用户注销提示信息 Service 接口
*
* @author ziin
*/
public interface KeyboardWarningMessageService {
/**
* 创建用户注销提示信息
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createWarningMessage(@Valid KeyboardWarningMessageSaveReqVO createReqVO);
/**
* 更新用户注销提示信息
*
* @param updateReqVO 更新信息
*/
void updateWarningMessage(@Valid KeyboardWarningMessageSaveReqVO updateReqVO);
/**
* 删除用户注销提示信息
*
* @param id 编号
*/
void deleteWarningMessage(Long id);
/**
* 批量删除用户注销提示信息
*
* @param ids 编号
*/
void deleteWarningMessageListByIds(List<Long> ids);
/**
* 获得用户注销提示信息
*
* @param id 编号
* @return 用户注销提示信息
*/
KeyboardWarningMessageDO getWarningMessage(Long id);
/**
* 获得用户注销提示信息分页
*
* @param pageReqVO 分页查询
* @return 用户注销提示信息分页
*/
PageResult<KeyboardWarningMessageDO> getWarningMessagePage(KeyboardWarningMessagePageReqVO pageReqVO);
}

View File

@@ -0,0 +1,81 @@
package com.yolo.keyboard.service.warningmessage;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import com.yolo.keyboard.controller.admin.warningmessage.vo.*;
import com.yolo.keyboard.dal.dataobject.warningmessage.KeyboardWarningMessageDO;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import com.yolo.keyboard.dal.mysql.warningmessage.KeyboardWarningMessageMapper;
import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.WARNING_MESSAGE_NOT_EXISTS;
/**
* 用户注销提示信息 Service 实现类
*
* @author ziin
*/
@Service
@Validated
public class KeyboardWarningMessageServiceImpl implements KeyboardWarningMessageService {
@Resource
private KeyboardWarningMessageMapper warningMessageMapper;
@Override
public Long createWarningMessage(KeyboardWarningMessageSaveReqVO createReqVO) {
// 插入
KeyboardWarningMessageDO warningMessage = BeanUtils.toBean(createReqVO, KeyboardWarningMessageDO.class);
warningMessageMapper.insert(warningMessage);
// 返回
return warningMessage.getId();
}
@Override
public void updateWarningMessage(KeyboardWarningMessageSaveReqVO updateReqVO) {
// 校验存在
validateWarningMessageExists(updateReqVO.getId());
// 更新
KeyboardWarningMessageDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardWarningMessageDO.class);
warningMessageMapper.updateById(updateObj);
}
@Override
public void deleteWarningMessage(Long id) {
// 校验存在
validateWarningMessageExists(id);
// 删除
warningMessageMapper.deleteById(id);
}
@Override
public void deleteWarningMessageListByIds(List<Long> ids) {
// 删除
warningMessageMapper.deleteByIds(ids);
}
private void validateWarningMessageExists(Long id) {
if (warningMessageMapper.selectById(id) == null) {
throw exception(WARNING_MESSAGE_NOT_EXISTS);
}
}
@Override
public KeyboardWarningMessageDO getWarningMessage(Long id) {
return warningMessageMapper.selectById(id);
}
@Override
public PageResult<KeyboardWarningMessageDO> getWarningMessagePage(KeyboardWarningMessagePageReqVO pageReqVO) {
return warningMessageMapper.selectPage(pageReqVO);
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yolo.keyboard.module.keyboard.dal.mysql.appversions.AppVersionsMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yolo.keyboard.module.keyboard.dal.mysql.warningmessage.KeyboardWarningMessageMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@@ -95,5 +95,9 @@ public interface ErrorCodeConstants {
ErrorCode USER_INVITES_NOT_EXISTS = new ErrorCode(1_001_202_021, "用户邀请关系绑定台账表,记录新用户最终归属的邀请人不存在"); ErrorCode USER_INVITES_NOT_EXISTS = new ErrorCode(1_001_202_021, "用户邀请关系绑定台账表,记录新用户最终归属的邀请人不存在");
ErrorCode TENANT_COMMISSION_NOT_EXISTS = new ErrorCode(1_001_202_022, "租户内购分成记录不存在"); ErrorCode TENANT_COMMISSION_NOT_EXISTS = new ErrorCode(1_001_202_022, "租户内购分成记录不存在");
ErrorCode AI_COMPANION_NOT_EXISTS = new ErrorCode(1_001_202_023, "AI陪聊角色表用于定义恋爱/陪伴型虚拟角色的基础信息与人设不存在"); ErrorCode AI_COMPANION_NOT_EXISTS = new ErrorCode(1_001_202_023, "AI陪聊角色表用于定义恋爱/陪伴型虚拟角色的基础信息与人设不存在");
ErrorCode WARNING_MESSAGE_NOT_EXISTS = new ErrorCode(1_001_202_024, "用户注销提示信息不存在");
ErrorCode APP_VERSIONS_NOT_EXISTS = new ErrorCode(1_001_202_025, "App 版本发布与更新检查表信息不存在");
ErrorCode CHARACTER_I18N_NOT_EXISTS = new ErrorCode(1_001_202_026, "键盘人设国际化内容不存在");
ErrorCode AI_COMPANION_I18N_NOT_EXISTS = new ErrorCode(1_001_202_027, "AI陪聊人设国际化内容不存在");
ErrorCode TAG_I18N_NOT_EXISTS = new ErrorCode(1_001_202_028, "人设标签国际化内容不存在");
} }

View File

@@ -47,9 +47,9 @@ spring:
primary: master primary: master
datasource: datasource:
master: master:
url: jdbc:postgresql://localhost:5432/keyborad_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 url: jdbc:postgresql://43.162.81.217:5231/keyborad_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: root username: root
password: 123asd password: dpW7FHZ7FtK5czc4
# slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 # slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
# lazy: true # 开启懒加载,保证启动速度 # lazy: true # 开启懒加载,保证启动速度
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
@@ -59,9 +59,9 @@ spring:
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data: data:
redis: redis:
host: 400-infra.server.iocoder.cn # 地址 port: 6379
port: 6379 # 端口 host: localhost
database: 1 # 数据库索引 database: 0
# password: 123456 # 密码,建议生产环境开启 # password: 123456 # 密码,建议生产环境开启
--- #################### 定时任务相关配置 #################### --- #################### 定时任务相关配置 ####################