From 29152e56a430cf01c06a41e45618e7a863f646df Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Thu, 23 Oct 2025 19:58:07 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=96=87?= =?UTF-8?q?=E7=AB=A0):=20=E6=96=B0=E5=A2=9E=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加小程序文章管理的完整功能模块,包括: 1. API接口定义文件(AppletArticle.api.ts) 2. 数据模型和表单配置(AppletArticle.data.ts) 3. 表单组件(AppletArticleForm.vue) 4. 模态框组件(AppletArticleModal.vue) 5. 列表页面(AppletArticleList.vue) 实现文章的增删改查、批量操作、导入导出等功能 --- .../src/views/applet/article/AppletArticle.api.ts | 64 +++++++ .../src/views/applet/article/AppletArticle.data.ts | 77 ++++++++ .../src/views/applet/article/AppletArticleList.vue | 206 +++++++++++++++++++++ .../article/components/AppletArticleForm.vue | 70 +++++++ .../article/components/AppletArticleModal.vue | 99 ++++++++++ 5 files changed, 516 insertions(+) create mode 100644 jeecgboot-vue3/src/views/applet/article/AppletArticle.api.ts create mode 100644 jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts create mode 100644 jeecgboot-vue3/src/views/applet/article/AppletArticleList.vue create mode 100644 jeecgboot-vue3/src/views/applet/article/components/AppletArticleForm.vue create mode 100644 jeecgboot-vue3/src/views/applet/article/components/AppletArticleModal.vue diff --git a/jeecgboot-vue3/src/views/applet/article/AppletArticle.api.ts b/jeecgboot-vue3/src/views/applet/article/AppletArticle.api.ts new file mode 100644 index 0000000..433950b --- /dev/null +++ b/jeecgboot-vue3/src/views/applet/article/AppletArticle.api.ts @@ -0,0 +1,64 @@ +import {defHttp} from '/@/utils/http/axios'; +import { useMessage } from "/@/hooks/web/useMessage"; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/appletArticle/appletArticle/list', + save='/appletArticle/appletArticle/add', + edit='/appletArticle/appletArticle/edit', + deleteOne = '/appletArticle/appletArticle/delete', + deleteBatch = '/appletArticle/appletArticle/deleteBatch', + importExcel = '/appletArticle/appletArticle/importExcel', + exportXls = '/appletArticle/appletArticle/exportXls', +} +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; +/** + * 导入api + */ +export const getImportUrl = Api.importExcel; +/** + * 列表接口 + * @param params + */ +export const list = (params) => + defHttp.get({url: Api.list, params}); + +/** + * 删除单个 + */ +export const deleteOne = (params,handleSuccess) => { + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); +} +/** + * 批量删除 + * @param params + */ +export const batchDelete = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); + } + }); +} +/** + * 保存或者更新 + * @param params + */ +export const saveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({url: url, params}); +} diff --git a/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts b/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts new file mode 100644 index 0000000..ea351e5 --- /dev/null +++ b/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts @@ -0,0 +1,77 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +import { getWeekMonthQuarterYear } from '/@/utils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '标题', + align:"center", + dataIndex: 'title' + }, + { + title: '排序', + align:"center", + dataIndex: 'sort' + }, + { + title: '上架', + align:"center", + dataIndex: 'status', + customRender:({text}) => { + return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}]) + }, + }, +]; +//查询数据 +export const searchFormSchema: FormSchema[] = [ +]; +//表单数据 +export const formSchema: FormSchema[] = [ + { + label: '标题', + field: 'title', + component: 'Input', + }, + { + label: '排序', + field: 'sort', + component: 'InputNumber', + }, + { + label: '上架', + field: 'status', + component: 'JSwitch', + componentProps:{ + }, + }, + { + label: '内容', + field: 'content', + component: 'JEditor', + }, + // TODO 主键隐藏字段,目前写死为ID + { + label: '', + field: 'id', + component: 'Input', + show: false + }, +]; + +// 高级查询数据 +export const superQuerySchema = { + title: {title: '标题',order: 0,view: 'text', type: 'string',}, + sort: {title: '排序',order: 1,view: 'number', type: 'number',}, + status: {title: '上架',order: 2,view: 'switch', type: 'string',}, +}; + +/** +* 流程表单调用这个方法获取formSchema +* @param param +*/ +export function getBpmFormSchema(_formData): FormSchema[]{ + // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema + return formSchema; +} \ No newline at end of file diff --git a/jeecgboot-vue3/src/views/applet/article/AppletArticleList.vue b/jeecgboot-vue3/src/views/applet/article/AppletArticleList.vue new file mode 100644 index 0000000..78f1a9c --- /dev/null +++ b/jeecgboot-vue3/src/views/applet/article/AppletArticleList.vue @@ -0,0 +1,206 @@ + + + + + \ No newline at end of file diff --git a/jeecgboot-vue3/src/views/applet/article/components/AppletArticleForm.vue b/jeecgboot-vue3/src/views/applet/article/components/AppletArticleForm.vue new file mode 100644 index 0000000..4e98b11 --- /dev/null +++ b/jeecgboot-vue3/src/views/applet/article/components/AppletArticleForm.vue @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/jeecgboot-vue3/src/views/applet/article/components/AppletArticleModal.vue b/jeecgboot-vue3/src/views/applet/article/components/AppletArticleModal.vue new file mode 100644 index 0000000..34c8f5e --- /dev/null +++ b/jeecgboot-vue3/src/views/applet/article/components/AppletArticleModal.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file From f2ef5b523af50274f723a4bf8d1067ed1e35a108 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Thu, 23 Oct 2025 20:01:52 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=96=87?= =?UTF-8?q?=E7=AB=A0):=20=E6=96=B0=E5=A2=9E=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E6=A8=A1=E5=9D=97=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增小程序文章模块,包括文章管理、列表展示、详情查看功能 添加文章TTS语音合成支持,包括长文本TTS任务生成和回调处理 实现文章内容的多音色语音合成功能 优化TTS回调接口,增强异常处理和日志记录 更新pom.xml配置,设置maven-compiler-plugin为Java 9 修复AppletTtsCache实体类字段命名问题 --- .../jeecgboot-boot-applet/pom.xml | 12 + .../controller/AppletApiIndexController.java | 16 ++ .../applet/controller/AppletApiTTSController.java | 146 ++++++++++- .../applet/service/AppletApiIndexService.java | 5 + .../modules/applet/service/AppletApiTTService.java | 15 ++ .../service/impl/AppletApiIndexServiceImpl.java | 36 +++ .../service/impl/AppletApiTTServiceImpl.java | 287 +++++++++++++++++---- .../controller/AppletArticleController.java | 207 +++++++++++++++ .../demo/appletArticle/entity/AppletArticle.java | 77 ++++++ .../appletArticle/mapper/AppletArticleMapper.java | 17 ++ .../mapper/xml/AppletArticleMapper.xml | 5 + .../service/IAppletArticleService.java | 14 + .../service/impl/AppletArticleServiceImpl.java | 19 ++ .../appletArticle/uniapp/AppletArticleForm.vue | 101 ++++++++ .../appletArticle/uniapp/AppletArticleList.vue | 44 ++++ .../appletArticle/uniapp3/AppletArticleData.ts | 22 ++ .../appletArticle/uniapp3/AppletArticleForm.vue | 253 ++++++++++++++++++ .../appletArticle/uniapp3/AppletArticleList.vue | 148 +++++++++++ .../demo/appletArticle/vue3/AppletArticle.api.ts | 64 +++++ .../demo/appletArticle/vue3/AppletArticle.data.ts | 77 ++++++ .../demo/appletArticle/vue3/AppletArticleList.vue | 210 +++++++++++++++ .../V20251023_1__menu_insert_AppletArticle.sql | 26 ++ .../vue3/components/AppletArticleForm.vue | 70 +++++ .../vue3/components/AppletArticleModal.vue | 99 +++++++ .../demo/appletTtsCache/entity/AppletTtsCache.java | 2 +- .../src/views/applet/article/AppletArticle.data.ts | 10 +- 26 files changed, 1907 insertions(+), 75 deletions(-) create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/controller/AppletArticleController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/entity/AppletArticle.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/AppletArticleMapper.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/xml/AppletArticleMapper.xml create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/IAppletArticleService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/impl/AppletArticleServiceImpl.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleData.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.api.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.data.ts create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticleList.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/V20251023_1__menu_insert_AppletArticle.sql create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleForm.vue create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleModal.vue diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml index 58e624c..69374d3 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml @@ -3,6 +3,18 @@ 4.0.0 jeecgboot-boot-applet + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + jeecg-boot-module diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiIndexController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiIndexController.java index 4154c96..ee8ff3e 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiIndexController.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiIndexController.java @@ -8,6 +8,7 @@ import org.jeecg.common.api.vo.Result; import org.jeecg.config.shiro.IgnoreAuth; import org.jeecg.modules.applet.service.AppletApiConfigService; import org.jeecg.modules.applet.service.AppletApiIndexService; +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; import org.jeecg.modules.demo.appletBanner.entity.AppletBanner; import org.jeecg.modules.demo.appletConfig.entity.AppletConfig; import org.jeecg.modules.demo.appletLink.entity.AppletLink; @@ -49,6 +50,7 @@ public class AppletApiIndexController { List list = appletApiIndexService.getBanner(); return Result.OK(list); } + /** * 轮播图详情 * @@ -87,4 +89,18 @@ public class AppletApiIndexController { appletApiIndexService.linkSignup(appletRegistration); return Result.OK(); } + + @IgnoreAuth + @Operation(summary = "查询文章列表", description = "查询文章列表") + @GetMapping(value = "/articleList") + public Result> articleList() { + return Result.OK(appletApiIndexService.articleList()); + } + + @IgnoreAuth + @Operation(summary = "查询文章详情", description = "查询文章详情") + @GetMapping(value = "/articleDetail") + public Result articleDetail(String id) { + return Result.OK(appletApiIndexService.articleDetail(id)); + } } \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java index e8f515d..552c0f2 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java @@ -17,6 +17,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; @@ -122,23 +123,142 @@ public class AppletApiTTSController { // } @Operation(summary = "接收长文本TTS回调", description = "接收腾讯云回调并保存音频与状态") - @PostMapping(value = "/long/callback") + @PostMapping(value = "/long/callback", consumes = {"application/json", "application/x-www-form-urlencoded", "text/plain"}) @IgnoreAuth - public Result ttsCallback(@RequestBody Map payload) { + public Result ttsCallback(HttpServletRequest request) { try { - log.info("回调内容: {}", payload); - String taskId = (String) payload.get("TaskId"); - Boolean success = payload.get("Success") instanceof Boolean ? (Boolean) payload.get("Success") : null; - String audioBase64 = (String) payload.get("Audio"); - Integer sampleRate = payload.get("SampleRate") instanceof Number ? ((Number) payload.get("SampleRate")).intValue() : null; - String codec = (String) payload.get("Codec"); - String message = (String) payload.get("Message"); - - boolean ok = appletApiTTService.handleTtsCallback(taskId, audioBase64, sampleRate, codec, success != null && success, message); + String contentType = request.getContentType(); + log.info("回调请求 Content-Type: {}", contentType); + + String raw = null; + + // 直接从 InputStream 读取原始字节,避免 Spring 的参数解析 + try (java.io.InputStream is = request.getInputStream()) { + byte[] bytes = is.readAllBytes(); + raw = new String(bytes, java.nio.charset.StandardCharsets.UTF_8); + log.info("原始请求体长度: {} 字节", bytes.length); + } + + if (raw == null || raw.trim().isEmpty()) { + log.error("请求体为空"); + return Result.error("请求体为空"); + } + + log.info("原始请求体前200字符: {}", raw.length() > 200 ? raw.substring(0, 200) + "..." : raw); + + String json = raw; + + // 处理 form-urlencoded 格式 + if (contentType != null && contentType.contains("application/x-www-form-urlencoded")) { + // 查找 data= 参数 + int dataIdx = raw.indexOf("data="); + if (dataIdx >= 0) { + String encoded = raw.substring(dataIdx + 5); + + // 处理可能存在的其他参数(用 & 分隔) + int ampIdx = encoded.indexOf('&'); + if (ampIdx > 0) { + encoded = encoded.substring(0, ampIdx); + } + + try { + // 多次 URL 解码,处理可能的双重编码 + json = java.net.URLDecoder.decode(encoded, "UTF-8"); + + // 如果解码后仍然包含 %,可能需要再次解码 + if (json.contains("%")) { + try { + String doubleDecoded = java.net.URLDecoder.decode(json, "UTF-8"); + if (doubleDecoded.trim().startsWith("{")) { + json = doubleDecoded; + } + } catch (Exception e) { + log.warn("二次URL解码失败,使用一次解码结果: {}", e.getMessage()); + } + } + + log.info("URL解码后的JSON前200字符: {}", json.length() > 200 ? json.substring(0, 200) + "..." : json); + } catch (Exception decodeEx) { + log.warn("URL解码失败,使用原始编码数据: {}", decodeEx.getMessage()); + json = encoded; + } + } + } else if (raw.startsWith("data=")) { + // 处理直接以 data= 开头的情况 + String encoded = raw.substring(5); + try { + json = java.net.URLDecoder.decode(encoded, "UTF-8"); + log.info("直接data=格式解码后: {}", json.length() > 200 ? json.substring(0, 200) + "..." : json); + } catch (Exception decodeEx) { + log.warn("直接data=格式解码失败: {}", decodeEx.getMessage()); + json = encoded; + } + } + + // 解析 JSON + com.alibaba.fastjson.JSONObject obj = null; + + // 清理可能的前后空白字符和引号 + json = json.trim(); + if (json.startsWith("\"") && json.endsWith("\"")) { + json = json.substring(1, json.length() - 1); + } + + if (json.startsWith("{")) { + try { + obj = com.alibaba.fastjson.JSON.parseObject(json); + } catch (Exception parseEx) { + log.error("JSON解析失败: {}, JSON内容: {}", parseEx.getMessage(), json); + // 尝试提取 JSON 部分 + int start = json.indexOf('{'); + int end = json.lastIndexOf('}'); + if (start >= 0 && end > start) { + try { + obj = com.alibaba.fastjson.JSON.parseObject(json.substring(start, end + 1)); + } catch (Exception e2) { + log.error("提取JSON部分后仍解析失败: {}", e2.getMessage()); + } + } + } + } else { + // 尝试查找 JSON 部分 + int start = json.indexOf('{'); + int end = json.lastIndexOf('}'); + if (start >= 0 && end > start) { + try { + obj = com.alibaba.fastjson.JSON.parseObject(json.substring(start, end + 1)); + } catch (Exception parseEx) { + log.error("提取JSON部分解析失败: {}", parseEx.getMessage()); + } + } + } + + if (obj == null) { + log.error("回调解析失败,无法提取有效JSON,原始内容: {}", json); + return Result.error("回调解析失败,非JSON格式"); + } + + log.info("成功解析回调JSON,TaskId: {}", obj.getString("TaskId")); + + String taskId = obj.getString("TaskId"); + Long status = obj.getLong("Status"); + String statusStr = obj.getString("StatusStr"); + String resultUrl = obj.getString("ResultUrl"); + com.alibaba.fastjson.JSONArray subtitles = obj.getJSONArray("Subtitles"); + String subtitlesJson = subtitles != null ? subtitles.toJSONString() : null; + + // 清理URL中的反引号等异常字符 + if (resultUrl != null) { + resultUrl = resultUrl.replace("`", "").trim(); + log.info("清理后的ResultUrl: {}", resultUrl); + } + + boolean ok = appletApiTTService.handleLongTextTtsCallback(taskId, resultUrl, status, statusStr, subtitlesJson); return ok ? Result.OK(true) : Result.error("回调处理失败"); + } catch (Exception e) { - log.error("回调解析失败: {}", e.getMessage(), e); - return Result.error("回调解析失败: " + e.getMessage()); + log.error("回调处理异常: {}", e.getMessage(), e); + return Result.error("回调处理异常: " + e.getMessage()); } } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiIndexService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiIndexService.java index 9d45b8c..749adaa 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiIndexService.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiIndexService.java @@ -1,5 +1,6 @@ package org.jeecg.modules.applet.service; +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; import org.jeecg.modules.demo.appletBanner.entity.AppletBanner; import org.jeecg.modules.demo.appletLink.entity.AppletLink; import org.jeecg.modules.demo.appletRegistration.entity.AppletRegistration; @@ -37,4 +38,8 @@ public interface AppletApiIndexService { * @param appletRegistration */ void linkSignup(AppletRegistration appletRegistration); + + List articleList(); + + AppletArticle articleDetail(String id); } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java index 28d8fe1..7f253ee 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java @@ -6,6 +6,7 @@ import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre; import org.jeecg.modules.demo.appletTtsCache.entity.AppletTtsCache; import java.util.List; +import java.util.Map; public interface AppletApiTTService { /** @@ -27,6 +28,11 @@ public interface AppletApiTTService { */ String createLongTextTtsTask(String text, Float speed, Integer voiceType, Float volume, String codec, String callbackUrl); + /** + * 为文章内容遍历所有启用音色创建长文本TTS任务(异步) + */ + void generateLongTextForArticleContentAllTimbres(String content); + /** * 按页面创建长文本TTS任务(异步),仅接收页面ID与音色ID */ @@ -46,4 +52,13 @@ public interface AppletApiTTService { * 接收腾讯云TTS异步回调并处理结果,保存到缓存并更新状态 */ boolean handleTtsCallback(String taskId, String audioBase64, Integer sampleRate, String codec, boolean success, String message); + + /** + * 接收腾讯云长文本TTS异步回调并处理结果,保存ResultUrl和字幕到缓存 + */ + boolean handleLongTextTtsCallback(String taskId, String resultUrl, Long status, String statusStr, String subtitlesJson); + + Map selectMapByHtml(String content); + + } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiIndexServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiIndexServiceImpl.java index 7754723..6b9ac1b 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiIndexServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiIndexServiceImpl.java @@ -3,6 +3,9 @@ package org.jeecg.modules.applet.service.impl; import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.system.util.AppletUserUtil; import org.jeecg.modules.applet.service.AppletApiIndexService; +import org.jeecg.modules.applet.service.AppletApiTTService; +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; +import org.jeecg.modules.demo.appletArticle.service.IAppletArticleService; import org.jeecg.modules.demo.appletBanner.entity.AppletBanner; import org.jeecg.modules.demo.appletBanner.service.IAppletBannerService; import org.jeecg.modules.demo.appletCoursePage.entity.AppletCoursePage; @@ -14,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Map; @Service public class AppletApiIndexServiceImpl implements AppletApiIndexService { @@ -27,6 +31,12 @@ public class AppletApiIndexServiceImpl implements AppletApiIndexService { @Autowired private IAppletRegistrationService appletRegistrationService; + @Autowired + private IAppletArticleService appletArticleService; + + @Autowired + private AppletApiTTService appletApiTTService; + /*** * 轮播图 * @return @@ -87,4 +97,30 @@ public class AppletApiIndexServiceImpl implements AppletApiIndexService { appletRegistrationService.save(appletRegistration); } + + @Override + public List articleList() { + return appletArticleService + .lambdaQuery() + .orderByDesc(AppletArticle::getSort, AppletArticle::getCreateTime) + .eq(AppletArticle::getStatus, "Y") + .select(AppletArticle::getTitle, AppletArticle::getId) + .list(); + } + + @Override + public AppletArticle articleDetail(String id) { + + AppletArticle byId = appletArticleService.getById(id); + + if (byId == null){ + return null; + } + + //查询音频 + Map map = appletApiTTService.selectMapByHtml(byId.getContent()); + byId.setAudios(map); + + return byId; + } } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java index 1297b2f..b253dfa 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java @@ -26,6 +26,7 @@ import org.jeecg.modules.demo.appletCoursePage.entity.AppletCoursePage; import org.jeecg.modules.demo.appletCoursePage.service.IAppletCoursePageService; import org.jeecg.modules.demo.appletCoursePageWord.entity.AppletCoursePageWord; import org.jeecg.modules.demo.appletCoursePageWord.service.IAppletCoursePageWordService; +import org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog; import org.jeecg.modules.demo.appletTtsPlayLog.service.IAppletTtsPlayLogService; import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre; import org.jeecg.modules.demo.appletTtsTimbre.service.IAppletTtsTimbreService; @@ -35,11 +36,11 @@ import org.jeecg.modules.applet.util.AudioDurationUtil; import java.io.ByteArrayInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import java.util.concurrent.TimeUnit; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.UUID; -import java.util.Base64; +import java.util.*; import java.net.URL; import java.io.InputStream; import java.io.ByteArrayOutputStream; @@ -65,6 +66,8 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { private IAppletCoursePageService appletCoursePageService; @Autowired private IAppletCoursePageWordService appletCoursePageWordService; + @Autowired + private RedisTemplate redisTemplate; public TtsVo textToVoice(String text, Float speed, Integer voiceType, Float volume, String codec) { @@ -78,23 +81,23 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 1. 先查询缓存数据库,看是否已经有相同参数的音频 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(AppletTtsCache::getText, text) - .eq(AppletTtsCache::getVoiceType, voiceType) - .eq(volume != null, AppletTtsCache::getVolume, volume != null ? volume.doubleValue() : null) - .eq(speed != null, AppletTtsCache::getSpeed, speed != null ? speed.doubleValue() : null) - .eq(AppletTtsCache::getSuccess, "Y"); - + .eq(AppletTtsCache::getVoiceType, voiceType) + .eq(volume != null, AppletTtsCache::getVolume, volume != null ? volume.doubleValue() : null) + .eq(speed != null, AppletTtsCache::getSpeed, speed != null ? speed.doubleValue() : null) + .eq(AppletTtsCache::getSuccess, "Y"); + AppletTtsCache existingCache = appletTtsCacheService.getOne(queryWrapper); - + if (existingCache != null) { // 缓存命中,直接返回缓存的音频ID log.info("TTS缓存命中,直接返回缓存音频,audioId: {}", existingCache.getAudioId()); - + // 记录播放日志 long endTime = System.currentTimeMillis(); double elapsedTime = (endTime - startTime) / 1000.0; savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null, - speed != null ? speed.doubleValue() : null, elapsedTime, true, existingCache.getId()); - + speed != null ? speed.doubleValue() : null, elapsedTime, true, existingCache.getId()); + return TtsVo.builder() .url(existingCache.getAudioId()) .time(existingCache.getDuration()) @@ -103,7 +106,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 2. 缓存未命中,调用腾讯云TTS接口生成音频 log.info("TTS缓存未命中,调用腾讯云接口生成音频"); - + // 创建认证对象 Credential cred = new Credential(secretId, secretKey); @@ -160,7 +163,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 3. 将音频数据上传到OSS byte[] audioData = java.util.Base64.getDecoder().decode(audioBase64); String fileName = IdUtils.generateNo("TTS_") + System.currentTimeMillis() + ".wav"; - + // 使用ByteArrayInputStream上传到OSS ByteArrayInputStream inputStream = new ByteArrayInputStream(audioData); String audioUrl = OssBootUtil.upload(inputStream, "tts/" + fileName); @@ -200,7 +203,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 记录成功的TTS调用日志 savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null, - speed != null ? speed.doubleValue() : null, elapsedTime, true, cacheId); + speed != null ? speed.doubleValue() : null, elapsedTime, true, cacheId); log.info("TTS调用成功,文本长度: {}, 耗时: {}秒", text != null ? text.length() : 0, elapsedTime); return TtsVo.builder() @@ -210,7 +213,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { } else { // 记录失败的TTS调用日志 savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null, - speed != null ? speed.doubleValue() : null, elapsedTime, false, null); + speed != null ? speed.doubleValue() : null, elapsedTime, false, null); log.warn("TTS返回的音频数据为空"); return null; @@ -223,22 +226,98 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 记录失败的TTS调用日志 savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null, - speed != null ? speed.doubleValue() : null, elapsedTime, false, null); + speed != null ? speed.doubleValue() : null, elapsedTime, false, null); log.error("调用腾讯云TTS接口失败: {}", e.getMessage(), e); return null; } } + @Override + public boolean handleLongTextTtsCallback(String taskId, String resultUrl, Long status, String statusStr, String subtitlesJson) { + // Redis锁的key + String lockKey = "tts_callback_lock:" + taskId; + // 锁的过期时间(秒) + int lockExpireTime = 300; // 5分钟 + + try { + // 尝试获取Redis分布式锁 + Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", lockExpireTime, TimeUnit.SECONDS); + if (!lockAcquired) { + log.warn("TTS回调处理已在进行中,跳过重复处理,taskId: {}", taskId); + return false; + } + + log.info("获取TTS回调处理锁成功,开始处理,taskId: {}", taskId); + + // 检查数据库中是否已经处理完成 + AppletTtsCache cache = appletTtsCacheService + .lambdaQuery() + .eq(AppletTtsCache::getTaskId, taskId) + .one(); + if (cache == null) { + log.error("未找到对应的TTS缓存记录,taskId: {}", taskId); + return false; + } + + // 如果已经处理完成(成功状态且state=1),直接返回 + if ("Y".equals(cache.getSuccess()) && cache.getState() != null && cache.getState() == 1) { + log.info("TTS任务已处理完成,无需重复处理,taskId: {}, audioId: {}", taskId, cache.getAudioId()); + return true; + } + + // 保存字幕信息 + if (subtitlesJson != null && !subtitlesJson.isEmpty()) { + cache.setSubtitlesJson(subtitlesJson); + } + + // 判断是否成功 (Status == 2 表示成功) + if (status != null && status == 2) { + // 使用统一的音频处理方法 + AudioProcessResult audioResult = processAndUploadAudio(resultUrl, taskId); + + cache.setAudioId(audioResult.getAudioUrl()); + if (audioResult.getDuration() != null) { + cache.setDuration(audioResult.getDuration()); + } + cache.setSuccess("Y"); + cache.setState(1); + } else { + cache.setSuccess("N"); + cache.setState(0); + log.warn("TTS任务失败,taskId: {}, status: {}, statusStr: {}", taskId, status, statusStr); + } + + cache.setUpdateTime(new java.util.Date()); + appletTtsCacheService.updateById(cache); + + log.info("长文本TTS回调处理完成,taskId: {}, success: {}, status: {}", taskId, cache.getSuccess(), status); + return true; + } catch (Exception e) { + log.error("处理长文本TTS回调失败: {}", e.getMessage(), e); + return false; + } finally { + // 释放Redis锁 + try { + redisTemplate.delete(lockKey); + log.info("释放TTS回调处理锁,taskId: {}", taskId); + } catch (Exception e) { + log.error("释放TTS回调处理锁失败,taskId: {}, error: {}", taskId, e.getMessage()); + } + } + } + @Override public String createLongTextTtsTask(String text, Float speed, Integer voiceType, Float volume, String codec, String callbackUrl) { try { - // 先检查是否存在生成中的任务(相同文本与参数) + // 将文章内容去除HTML并计算哈希,用于缓存唯一标识 + String normalized = stripHtml(text); + String textHash = String.valueOf(normalized.hashCode()); + + // 先检查是否存在生成中的任务(相同文本哈希与音色) LambdaQueryWrapper generatingWrapper = new LambdaQueryWrapper<>(); - generatingWrapper.eq(AppletTtsCache::getText, text) + generatingWrapper.eq(AppletTtsCache::getText, textHash) .eq(AppletTtsCache::getVoiceType, voiceType) - .eq(volume != null, AppletTtsCache::getVolume, volume != null ? volume.doubleValue() : null) - .eq(speed != null, AppletTtsCache::getSpeed, speed != null ? speed.doubleValue() : null) .eq(AppletTtsCache::getState, 0); AppletTtsCache generating = appletTtsCacheService.getOne(generatingWrapper); @@ -249,10 +328,8 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 再检查是否已有成功缓存 LambdaQueryWrapper successWrapper = new LambdaQueryWrapper<>(); - successWrapper.eq(AppletTtsCache::getText, text) + successWrapper.eq(AppletTtsCache::getText, textHash) .eq(AppletTtsCache::getVoiceType, voiceType) - .eq(volume != null, AppletTtsCache::getVolume, volume != null ? volume.doubleValue() : null) - .eq(speed != null, AppletTtsCache::getSpeed, speed != null ? speed.doubleValue() : null) .eq(AppletTtsCache::getSuccess, "Y") .eq(AppletTtsCache::getState, 1); AppletTtsCache successCache = appletTtsCacheService.getOne(successWrapper); @@ -285,7 +362,8 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { // 启用时间戳字幕 try { req.getClass().getMethod("setEnableSubtitle", Boolean.class).invoke(req, Boolean.TRUE); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } if (callbackUrl != null && !callbackUrl.isEmpty()) { req.setCallbackUrl(callbackUrl); } @@ -296,10 +374,10 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { throw new JeecgBootException("创建长文本TTS任务失败,未返回任务ID"); } - // 写入缓存,标记生成中 + // 写入缓存,标记生成中(text字段保存哈希) AppletTtsCache cache = new AppletTtsCache(); cache.setTaskId(taskId); - cache.setText(text); + cache.setText(textHash); cache.setVoiceType(voiceType); cache.setVolume(volume != null ? volume.doubleValue() : null); cache.setSpeed(speed != null ? speed.doubleValue() : null); @@ -308,7 +386,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { cache.setCreateTime(new java.util.Date()); appletTtsCacheService.save(cache); - log.info("长文本TTS任务创建成功,taskId: {}", taskId); + log.info("长文本TTS任务创建成功,taskId: {},textHash: {}", taskId, textHash); return taskId; } catch (Exception e) { log.error("创建长文本TTS任务失败: {}", e.getMessage(), e); @@ -352,32 +430,21 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { appletTtsCacheService.updateById(cache); return cache; } else if (status == 2) { - // 成功,处理音频 - byte[] audioData = null; - if (audioUrl != null && !audioUrl.isEmpty()) { - audioData = downloadBytes(audioUrl); - } - - if (audioData != null) { - String fileName = IdUtils.generateNo("TTS_") + System.currentTimeMillis() + ".wav"; - ByteArrayInputStream inputStream = new ByteArrayInputStream(audioData); - String uploadedUrl = OssBootUtil.upload(inputStream, "tts/" + fileName); - cache.setAudioId(uploadedUrl != null ? uploadedUrl : audioUrl); + // 成功,使用统一的音频处理方法 + AudioProcessResult audioResult = processAndUploadAudio(audioUrl, taskId); - Double realDuration = AudioDurationUtil.calculateDuration(audioData); - if (realDuration != null) { - cache.setDuration(realDuration); - } - } else { - cache.setAudioId(audioUrl); + cache.setAudioId(audioResult.getAudioUrl()); + if (audioResult.getDuration() != null) { + cache.setDuration(audioResult.getDuration()); } // 解析时间戳 String timestampsJson = null; try { timestampsJson = JSON.toJSONString(resp.getData().getSubtitles()); - cache.setTimestamps(timestampsJson); - } catch (Exception ignore) {} + cache.setSubtitlesJson(timestampsJson); + } catch (Exception ignore) { + } cache.setSuccess("Y"); cache.setState(1); @@ -538,7 +605,8 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { String text = obj.getString("content"); return text != null ? text : ""; } - } catch (Exception ignore) {} + } catch (Exception ignore) { + } // 作为纯文本返回 return content; } @@ -607,6 +675,31 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { } } + @Override + public Map selectMapByHtml(String content) { + HashMap map = new HashMap<>(); + + int i = stripHtml(content).hashCode(); + String textHash = String.valueOf(i); + + List list = appletTtsTimbreService + .lambdaQuery() + .select(AppletTtsTimbre::getVoiceType) + .list(); + + for (AppletTtsTimbre timbre : list) { + AppletTtsCache one = appletTtsCacheService.lambdaQuery() + .eq(AppletTtsCache::getText, textHash) + .eq(AppletTtsCache::getVoiceType, timbre.getVoiceType()) + .select(AppletTtsCache::getAudioId) + .one(); + + map.put(timbre.getVoiceType(), one != null ? one.getAudioId() : null); + } + + return map; + } + private byte[] downloadBytes(String url) { try (InputStream in = new URL(url).openStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buf = new byte[8192]; @@ -620,16 +713,14 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { return null; } } - + /** * 保存TTS播放日志 */ - private void savePlayLog(String userId, String text, Integer voiceType, Double volume, - Double speed, Double elapsedTime, boolean success, String cacheId) { + private void savePlayLog(String userId, String text, Integer voiceType, Double volume, + Double speed, Double elapsedTime, boolean success, String cacheId) { try { - org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog playLog = - new org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog(); - + AppletTtsPlayLog playLog = new AppletTtsPlayLog(); playLog.setUserId(userId); playLog.setVoicetype(voiceType); playLog.setVolume(volume); @@ -640,7 +731,7 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { playLog.setCreateTime(new java.util.Date()); appletTtsPlayLogService.save(playLog); - + log.debug("TTS播放日志保存成功,用户: {}, 结果: {}, 缓存ID: {}", userId, success ? "成功" : "失败", cacheId); } catch (Exception e) { log.error("保存TTS播放日志失败: {}", e.getMessage(), e); @@ -653,4 +744,88 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { return appletTtsTimbreService.list(); } -} + @Override + public void generateLongTextForArticleContentAllTimbres(String content) { + try { + List timbres = appletTtsTimbreService + .lambdaQuery() + .eq(AppletTtsTimbre::getStatus, "Y") + .select(AppletTtsTimbre::getVoiceType) + .list(); + String callbackUrl = TtscallbackUrl; + for (AppletTtsTimbre timbre : timbres) { + try { + createLongTextTtsTask(content, null, timbre.getVoiceType(), null, "wav", callbackUrl); + } catch (Exception ex) { + log.warn("创建文章内容长文本TTS任务失败 voiceType {}: {}", timbre.getVoiceType(), ex.getMessage()); + } + } + } catch (Exception e) { + log.error("遍历音色创建文章长文本TTS任务失败: {}", e.getMessage(), e); + } + } + + /** + * 统一的音频处理方法:下载音频、上传到OSS、计算时长 + * + * @param audioUrl 音频URL + * @param taskId 任务ID(用于日志) + * @return AudioProcessResult 包含上传后的URL和音频时长 + */ + private AudioProcessResult processAndUploadAudio(String audioUrl, String taskId) { + if (audioUrl == null || audioUrl.isEmpty()) { + log.warn("音频URL为空,taskId: {}", taskId); + return new AudioProcessResult(null, null); + } + + try { + // 下载音频数据 + byte[] audioData = downloadBytes(audioUrl); + if (audioData == null || audioData.length == 0) { + log.error("音频下载失败或数据为空,taskId: {}, audioUrl: {}", taskId, audioUrl); + return new AudioProcessResult(audioUrl, null); // 返回原始URL作为兜底 + } + + // 生成文件名并上传到OSS + String fileName = IdUtils.generateNo("TTS_") + System.currentTimeMillis() + ".wav"; + ByteArrayInputStream inputStream = new ByteArrayInputStream(audioData); + String uploadedUrl = OssBootUtil.upload(inputStream, "tts/" + fileName); + + if (uploadedUrl == null) { + log.error("音频上传到OSS失败,taskId: {}", taskId); + return new AudioProcessResult(audioUrl, null); // 返回原始URL作为兜底 + } + + // 计算音频时长 +// Double audioDuration = AudioDurationUtil.calculateDuration(audioData); + + log.info("音频处理成功,taskId: {}, uploadedUrl: {}, duration: {}", taskId, uploadedUrl, null); + return new AudioProcessResult(uploadedUrl, null); + + } catch (Exception e) { + log.error("音频处理失败,taskId: {}, audioUrl: {}, error: {}", taskId, audioUrl, e.getMessage()); + return new AudioProcessResult(audioUrl, null); // 返回原始URL作为兜底 + } + } + + /** + * 音频处理结果封装类 + */ + private static class AudioProcessResult { + private final String audioUrl; + private final Double duration; + + public AudioProcessResult(String audioUrl, Double duration) { + this.audioUrl = audioUrl; + this.duration = duration; + } + + public String getAudioUrl() { + return audioUrl; + } + + public Double getDuration() { + return duration; + } + } +} \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/controller/AppletArticleController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/controller/AppletArticleController.java new file mode 100644 index 0000000..b9288aa --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/controller/AppletArticleController.java @@ -0,0 +1,207 @@ +package org.jeecg.modules.demo.appletArticle.controller; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.query.QueryRuleEnum; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; +import org.jeecg.modules.demo.appletArticle.service.IAppletArticleService; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; + +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.def.NormalExcelConstants; +import org.jeecgframework.poi.excel.entity.ExportParams; +import org.jeecgframework.poi.excel.entity.ImportParams; +import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; +import org.jeecg.common.system.base.controller.JeecgController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.ModelAndView; +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.jeecg.modules.applet.service.AppletApiTTService; + /** + * @Description: 小程序文章 + * @Author: jeecg-boot + * @Date: 2025-10-23 + * @Version: V1.0 + */ +@Tag(name="小程序文章") +@RestController +@RequestMapping("/appletArticle/appletArticle") +@Slf4j +public class AppletArticleController extends JeecgController { + @Autowired + private IAppletArticleService appletArticleService; + + @Autowired + private AppletApiTTService appletApiTTService; + + /** + * 分页列表查询 + * + * @param appletArticle + * @param pageNo + * @param pageSize + * @param req + * @return + */ + //@AutoLog(value = "小程序文章-分页列表查询") + @Operation(summary="小程序文章-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(AppletArticle appletArticle, + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, + HttpServletRequest req) { + + + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(appletArticle, req.getParameterMap()); + Page page = new Page(pageNo, pageSize); + IPage pageList = appletArticleService.page(page, queryWrapper); + return Result.OK(pageList); + } + + /** + * 添加 + * + * @param appletArticle + * @return + */ + @AutoLog(value = "小程序文章-添加") + @Operation(summary="小程序文章-添加") + @RequiresPermissions("appletArticle:applet_article:add") + @PostMapping(value = "/add") + public Result add(@RequestBody AppletArticle appletArticle) { + appletArticleService.save(appletArticle); + + // 触发长文本TTS生成:遍历所有启用音色,对content进行转换 + try { + String content = appletArticle.getContent(); + if (content != null && !content.trim().isEmpty()) { + appletApiTTService.generateLongTextForArticleContentAllTimbres(content); + } + } catch (Exception e) { + log.warn("文章添加后触发长文本TTS失败: {}", e.getMessage()); + } + + return Result.OK("添加成功!"); + } + + /** + * 编辑 + * + * @param appletArticle + * @return + */ + @AutoLog(value = "小程序文章-编辑") + @Operation(summary="小程序文章-编辑") + @RequiresPermissions("appletArticle:applet_article:edit") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) + public Result edit(@RequestBody AppletArticle appletArticle) { + appletArticleService.updateById(appletArticle); + + // 触发长文本TTS生成:遍历所有启用音色,对content进行转换 + try { + String content = appletArticle.getContent(); + if (content != null && !content.trim().isEmpty()) { + appletApiTTService.generateLongTextForArticleContentAllTimbres(content); + } + } catch (Exception e) { + log.warn("文章编辑后触发长文本TTS失败: {}", e.getMessage()); + } + + return Result.OK("编辑成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "小程序文章-通过id删除") + @Operation(summary="小程序文章-通过id删除") + @RequiresPermissions("appletArticle:applet_article:delete") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name="id",required=true) String id) { + appletArticleService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + @AutoLog(value = "小程序文章-批量删除") + @Operation(summary="小程序文章-批量删除") + @RequiresPermissions("appletArticle:applet_article:deleteBatch") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { + this.appletArticleService.removeByIds(Arrays.asList(ids.split(","))); + return Result.OK("批量删除成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "小程序文章-通过id查询") + @Operation(summary="小程序文章-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name="id",required=true) String id) { + AppletArticle appletArticle = appletArticleService.getById(id); + if(appletArticle==null) { + return Result.error("未找到对应数据"); + } + return Result.OK(appletArticle); + } + + /** + * 导出excel + * + * @param request + * @param appletArticle + */ + @RequiresPermissions("appletArticle:applet_article:exportXls") + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, AppletArticle appletArticle) { + return super.exportXls(request, appletArticle, AppletArticle.class, "小程序文章"); + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequiresPermissions("appletArticle:applet_article:importExcel") + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) { + return super.importExcel(request, response, AppletArticle.class); + } + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/entity/AppletArticle.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/entity/AppletArticle.java new file mode 100644 index 0000000..777c378 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/entity/AppletArticle.java @@ -0,0 +1,77 @@ +package org.jeecg.modules.demo.appletArticle.entity; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.math.BigDecimal; +import java.util.Map; + +import com.baomidou.mybatisplus.annotation.*; +import org.jeecg.common.constant.ProvinceCityArea; +import org.jeecg.common.util.SpringContextUtils; +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.jeecg.common.aspect.annotation.Dict; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @Description: 小程序文章 + * @Author: jeecg-boot + * @Date: 2025-10-23 + * @Version: V1.0 + */ +@Data +@TableName("applet_article") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Schema(description="小程序文章") +public class AppletArticle implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键") + private java.lang.String id; + /**创建人*/ + @Schema(description = "创建人") + private java.lang.String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(description = "创建日期") + private java.util.Date createTime; + /**更新人*/ + @Schema(description = "更新人") + private java.lang.String updateBy; + /**更新日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(description = "更新日期") + private java.util.Date updateTime; + /**所属部门*/ + @Schema(description = "所属部门") + private java.lang.String sysOrgCode; + /**标题*/ + @Excel(name = "标题", width = 15) + @Schema(description = "标题") + private java.lang.String title; + /**排序*/ + @Excel(name = "排序", width = 15) + @Schema(description = "排序") + private java.lang.Integer sort; + /**上架*/ + @Excel(name = "上架", width = 15,replace = {"是_Y","否_N"} ) + @Schema(description = "上架") + private java.lang.String status; + /**内容*/ + @Excel(name = "内容", width = 15) + @Schema(description = "内容") + private java.lang.String content; + + @TableField(exist = false) + private Map audios; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/AppletArticleMapper.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/AppletArticleMapper.java new file mode 100644 index 0000000..371a77d --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/AppletArticleMapper.java @@ -0,0 +1,17 @@ +package org.jeecg.modules.demo.appletArticle.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @Description: 小程序文章 + * @Author: jeecg-boot + * @Date: 2025-10-23 + * @Version: V1.0 + */ +public interface AppletArticleMapper extends BaseMapper { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/xml/AppletArticleMapper.xml b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/xml/AppletArticleMapper.xml new file mode 100644 index 0000000..47d0077 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/mapper/xml/AppletArticleMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/IAppletArticleService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/IAppletArticleService.java new file mode 100644 index 0000000..9df3a75 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/IAppletArticleService.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.demo.appletArticle.service; + +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @Description: 小程序文章 + * @Author: jeecg-boot + * @Date: 2025-10-23 + * @Version: V1.0 + */ +public interface IAppletArticleService extends IService { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/impl/AppletArticleServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/impl/AppletArticleServiceImpl.java new file mode 100644 index 0000000..190f0d9 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/service/impl/AppletArticleServiceImpl.java @@ -0,0 +1,19 @@ +package org.jeecg.modules.demo.appletArticle.service.impl; + +import org.jeecg.modules.demo.appletArticle.entity.AppletArticle; +import org.jeecg.modules.demo.appletArticle.mapper.AppletArticleMapper; +import org.jeecg.modules.demo.appletArticle.service.IAppletArticleService; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +/** + * @Description: 小程序文章 + * @Author: jeecg-boot + * @Date: 2025-10-23 + * @Version: V1.0 + */ +@Service +public class AppletArticleServiceImpl extends ServiceImpl implements IAppletArticleService { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleForm.vue new file mode 100644 index 0000000..3bda3db --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleForm.vue @@ -0,0 +1,101 @@ + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleList.vue new file mode 100644 index 0000000..8f5234f --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp/AppletArticleList.vue @@ -0,0 +1,44 @@ + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleData.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleData.ts new file mode 100644 index 0000000..6209b9a --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleData.ts @@ -0,0 +1,22 @@ +import { render } from '@/common/renderUtils'; +//列表数据 +export const columns = [ + { + title: '标题', + align:"center", + dataIndex: 'title' + }, + { + title: '排序', + align:"center", + dataIndex: 'sort' + }, + { + title: '上架', + align:"center", + dataIndex: 'status', + customRender:({text}) => { + return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}]) + }, + }, +]; \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleForm.vue new file mode 100644 index 0000000..d54fad8 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleForm.vue @@ -0,0 +1,253 @@ + +{ +layout: 'default', +style: { +navigationStyle: 'custom', +navigationBarTitleText: '小程序文章', +}, +} + + + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleList.vue new file mode 100644 index 0000000..3b287fa --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/uniapp3/AppletArticleList.vue @@ -0,0 +1,148 @@ + +{ +layout: 'default', +style: { +navigationBarTitleText: '小程序文章', +navigationStyle: 'custom', +}, +} + + + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.api.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.api.ts new file mode 100644 index 0000000..433950b --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.api.ts @@ -0,0 +1,64 @@ +import {defHttp} from '/@/utils/http/axios'; +import { useMessage } from "/@/hooks/web/useMessage"; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/appletArticle/appletArticle/list', + save='/appletArticle/appletArticle/add', + edit='/appletArticle/appletArticle/edit', + deleteOne = '/appletArticle/appletArticle/delete', + deleteBatch = '/appletArticle/appletArticle/deleteBatch', + importExcel = '/appletArticle/appletArticle/importExcel', + exportXls = '/appletArticle/appletArticle/exportXls', +} +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; +/** + * 导入api + */ +export const getImportUrl = Api.importExcel; +/** + * 列表接口 + * @param params + */ +export const list = (params) => + defHttp.get({url: Api.list, params}); + +/** + * 删除单个 + */ +export const deleteOne = (params,handleSuccess) => { + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); +} +/** + * 批量删除 + * @param params + */ +export const batchDelete = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); + } + }); +} +/** + * 保存或者更新 + * @param params + */ +export const saveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({url: url, params}); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.data.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.data.ts new file mode 100644 index 0000000..ea351e5 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticle.data.ts @@ -0,0 +1,77 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +import { getWeekMonthQuarterYear } from '/@/utils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '标题', + align:"center", + dataIndex: 'title' + }, + { + title: '排序', + align:"center", + dataIndex: 'sort' + }, + { + title: '上架', + align:"center", + dataIndex: 'status', + customRender:({text}) => { + return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}]) + }, + }, +]; +//查询数据 +export const searchFormSchema: FormSchema[] = [ +]; +//表单数据 +export const formSchema: FormSchema[] = [ + { + label: '标题', + field: 'title', + component: 'Input', + }, + { + label: '排序', + field: 'sort', + component: 'InputNumber', + }, + { + label: '上架', + field: 'status', + component: 'JSwitch', + componentProps:{ + }, + }, + { + label: '内容', + field: 'content', + component: 'JEditor', + }, + // TODO 主键隐藏字段,目前写死为ID + { + label: '', + field: 'id', + component: 'Input', + show: false + }, +]; + +// 高级查询数据 +export const superQuerySchema = { + title: {title: '标题',order: 0,view: 'text', type: 'string',}, + sort: {title: '排序',order: 1,view: 'number', type: 'number',}, + status: {title: '上架',order: 2,view: 'switch', type: 'string',}, +}; + +/** +* 流程表单调用这个方法获取formSchema +* @param param +*/ +export function getBpmFormSchema(_formData): FormSchema[]{ + // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema + return formSchema; +} \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticleList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticleList.vue new file mode 100644 index 0000000..cd0231d --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/AppletArticleList.vue @@ -0,0 +1,210 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/V20251023_1__menu_insert_AppletArticle.sql b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/V20251023_1__menu_insert_AppletArticle.sql new file mode 100644 index 0000000..2a33648 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/V20251023_1__menu_insert_AppletArticle.sql @@ -0,0 +1,26 @@ +-- 注意:该页面对应的前台目录为views/appletArticle文件夹下 +-- 如果你想更改到其他目录,请修改sql中component字段对应的值 + + +INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) +VALUES ('2025102304348630020', NULL, '小程序文章', '/appletArticle/appletArticleList', 'appletArticle/AppletArticleList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0); + +-- 权限控制sql +-- 新增 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630021', '2025102304348630020', '添加小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); +-- 编辑 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630022', '2025102304348630020', '编辑小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); +-- 删除 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630023', '2025102304348630020', '删除小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); +-- 批量删除 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630024', '2025102304348630020', '批量删除小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); +-- 导出excel +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630025', '2025102304348630020', '导出excel_小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); +-- 导入excel +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025102304348630026', '2025102304348630020', '导入excel_小程序文章', NULL, NULL, 0, NULL, NULL, 2, 'appletArticle:applet_article:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-23 16:34:02', NULL, NULL, 0, 0, '1', 0); \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleForm.vue new file mode 100644 index 0000000..4e98b11 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleForm.vue @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleModal.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleModal.vue new file mode 100644 index 0000000..34c8f5e --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletArticle/vue3/components/AppletArticleModal.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsCache/entity/AppletTtsCache.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsCache/entity/AppletTtsCache.java index cb54901..2a9a5ee 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsCache/entity/AppletTtsCache.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsCache/entity/AppletTtsCache.java @@ -99,5 +99,5 @@ public class AppletTtsCache implements Serializable { private java.lang.Integer state; @Schema(description = "时间戳") - private java.lang.String timestamps; + private java.lang.String subtitlesJson; } diff --git a/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts b/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts index ea351e5..97dc4a1 100644 --- a/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts +++ b/jeecgboot-vue3/src/views/applet/article/AppletArticle.data.ts @@ -1,8 +1,8 @@ -import {BasicColumn} from '/@/components/Table'; -import {FormSchema} from '/@/components/Table'; -import { rules} from '/@/utils/helper/validator'; -import { render } from '/@/utils/common/renderUtils'; -import { getWeekMonthQuarterYear } from '/@/utils'; +import {BasicColumn} from '/src/components/Table'; +import {FormSchema} from '/src/components/Table'; +import { rules} from '/src/utils/helper/validator'; +import { render } from '/src/utils/common/renderUtils'; +import { getWeekMonthQuarterYear } from '/src/utils'; //列表数据 export const columns: BasicColumn[] = [ {