diff --git a/VideoAnalysis/Expand/AuthorizeExpand.cs b/VideoAnalysis/Expand/AuthorizeExpand.cs index 40cde2b..ddcfe21 100644 --- a/VideoAnalysis/Expand/AuthorizeExpand.cs +++ b/VideoAnalysis/Expand/AuthorizeExpand.cs @@ -6,63 +6,63 @@ using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using VideoAnalysisCore.Common; -namespace Learn.Archives.API.Expand +namespace Learn.VideoAnalysis.Expand { public static class AuthorizeExpand { public static IServiceCollection AddPermissionAuthentication(this IServiceCollection services) { - services.AddAuthentication() - .AddJwtBearer(Authentication.vdAdmin, options => - { - options.RequireHttpsMetadata = false; - options.UseSecurityTokenValidators = true; - options.MapInboundClaims = false; // .NET 5+ - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - options.TokenValidationParameters = new TokenValidationParameters - { - SaveSigninToken = false,//保存token,后台验证token是否生效(重要) - RequireExpirationTime = true, // 设置请求需要携带accesstoken的过期时间 - ValidateIssuer = false,//必须验证签发人 - ValidateAudience = false,//验证受众 - ValidateLifetime = true,//是否验证Token有效期 - ValidateIssuerSigningKey = true,//是否验证签名,不验证 会被篡改数据,不安全 - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppCommon.Config.AuthKey.Secret)),//解密的密钥 - }; - options.Events = new JwtBearerEvents - { - OnMessageReceived = context => - { - var token = context.Request.Headers["Authorization"].FirstOrDefault(); - // 3. 安全提取令牌 - if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - { - // 移除"Bearer "前缀并清除两端空格 - token = token.Substring("Bearer ".Length).Trim(); - context.Token = token; - } + services.AddAuthentication() + .AddJwtBearer(Authentication.vdAdmin, options => + { + options.RequireHttpsMetadata = false; + options.UseSecurityTokenValidators = true; + options.MapInboundClaims = false; // .NET 5+ + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + options.TokenValidationParameters = new TokenValidationParameters + { + SaveSigninToken = false,//保存token,后台验证token是否生效(重要) + RequireExpirationTime = true, // 设置请求需要携带accesstoken的过期时间 + ValidateIssuer = false,//必须验证签发人 + ValidateAudience = false,//验证受众 + ValidateLifetime = true,//是否验证Token有效期 + ValidateIssuerSigningKey = true,//是否验证签名,不验证 会被篡改数据,不安全 + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppCommon.Config.AuthKey.Secret)),//解密的密钥 + }; + options.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + var token = context.Request.Headers["Authorization"].FirstOrDefault(); + // 3. 安全提取令牌 + if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + { + // 移除"Bearer "前缀并清除两端空格 + token = token.Substring("Bearer ".Length).Trim(); + context.Token = token; + } - return Task.CompletedTask; - }, - OnAuthenticationFailed = context => - { - context.Response.StatusCode = 403; - return Task.CompletedTask; - }, - OnChallenge = context => - { - context.HandleResponse(); - if (context.Response.StatusCode == 403) - return Task.CompletedTask; - context.Response.Clear(); - context.Response.ContentType = "application/json"; - context.Response.StatusCode = 401; - var data = new { Code = 401, Message = context.Error + context.AuthenticateFailure?.Message }; - context.Response.WriteAsync(data.ToJson()); - return Task.CompletedTask; - } - }; - }); + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + context.Response.StatusCode = 403; + return Task.CompletedTask; + }, + OnChallenge = context => + { + context.HandleResponse(); + if (context.Response.StatusCode == 403) + return Task.CompletedTask; + context.Response.Clear(); + context.Response.ContentType = "application/json"; + context.Response.StatusCode = 401; + var data = new { Code = 401, Message = context.Error + context.AuthenticateFailure?.Message }; + context.Response.WriteAsync(data.ToJson()); + return Task.CompletedTask; + } + }; + }); return services; } diff --git a/VideoAnalysis/Expand/SwaggerExpand.cs b/VideoAnalysis/Expand/SwaggerExpand.cs new file mode 100644 index 0000000..e6d2d69 --- /dev/null +++ b/VideoAnalysis/Expand/SwaggerExpand.cs @@ -0,0 +1,106 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Learn.VideoAnalysis.Expand +{ + public static class SwaggerExpand + { + public static void AddSwaggerExpand(this IServiceCollection s, string name = "") + { + s.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Description = name + }); + c.OperationFilter(); + //按Http类型排序 + c.OrderActionsBy(o => o.GroupName); + + c.DocInclusionPredicate((docName, apiDesc) => + { + try + { + if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false; + var versions = methodInfo.DeclaringType.GetCustomAttributes(true) + .OfType().Select(attr => attr.GroupName); + if (docName.ToLower() == "v1" && versions.FirstOrDefault() == null) + return true; + return versions.Any(v => v.ToString() == docName); + } + catch (Exception ex) + { + + throw; + } + + }); + + + //添加全局安全性需求 + c.AddSecurityRequirement(new OpenApiSecurityRequirement{ + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "bearerAuth" + } + }, Array.Empty() + } + }); + + //添加一个必须的全局安全信息,和AddSecurityDefinition方法指定的方案名称要一致,这里是Bearer。 + c.AddSecurityDefinition("bearerAuth", + new OpenApiSecurityScheme + { + Description = "使用JWT授权头。示例:\"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + //内容为以 bearer开头 + Scheme = "bearer", + BearerFormat = "JWT" + }); + + DirectoryInfo dirs = new DirectoryInfo(AppContext.BaseDirectory); + FileInfo[] files = dirs.GetFiles("*.xml"); + foreach (var path in files) + { + c.IncludeXmlComments(path.FullName); + } + }); + //s.AddSwaggerGenNewtonsoftSupport(); + } + } + + + class SwaggerFileUploadFilter : IOperationFilter + { + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (context.ApiDescription.ActionDescriptor.Parameters.Any(w => w.ParameterType == typeof(IFormCollection))) + { + Dictionary schema = new Dictionary(); + schema["fileName"] = new OpenApiSchema { Description = "选择上传文件", Type = "string", Format = "binary" }; + Dictionary content = new Dictionary(); + content["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = schema } }; + operation.RequestBody = new OpenApiRequestBody() { Content = content }; + } + } + } + + +} diff --git a/VideoAnalysis/Program.cs b/VideoAnalysis/Program.cs index bd43a68..12bcdac 100644 --- a/VideoAnalysis/Program.cs +++ b/VideoAnalysis/Program.cs @@ -15,6 +15,7 @@ using System.Diagnostics; using VideoAnalysisCore.AICore.FFMPGE; using System.Text.Encodings.Web; using System.Text.Unicode; +using System.Text.Json; @@ -26,11 +27,6 @@ namespace Learn.VideoAnalysis { var builder = WebApplication.CreateBuilder(args); - // Add services to the container. - builder.Services.AddRazorComponents() - .AddInteractiveServerComponents(); - //.AddInteractiveWebAssemblyComponents(); - builder.Services.AddLogging(loggingBuilder => { @@ -39,33 +35,12 @@ namespace Learn.VideoAnalysis loggingBuilder.SetMinimumLevel(LogLevel.Warning); // С־Ϊ Warning }); - builder.Services.AddControllers() - .AddJsonOptions(options => - { - options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); - }); - - builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo - { - Title = "Learn.VideoAnalysis", - Version = "v1", - Description = "ѧƵƽ̨v1" - }); - var file = Path.Combine(AppContext.BaseDirectory, "Learn.VideoAnalysis.xml"); // xmlĵ· - var file1 = Path.Combine(AppContext.BaseDirectory, "VideoAnalysisCore.xml"); // xmlĵ· - c.IncludeXmlComments(file, true); // true : ʾע - c.IncludeXmlComments(file1, true); // true : ʾע - c.OrderActionsBy(o => o.RelativePath); // actionƽжͿԿЧˡ - }); - - // appsetting - //ʼ + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerExpand("AIƵ"); + // appsetting builder.Configuration.AddAppConfig(args); - + builder.Services.AddPermissionAuthentication(); builder.Services.AddSqlSugarExpand(); builder.Services.AddRedisExpand(); @@ -84,6 +59,13 @@ namespace Learn.VideoAnalysis { options.Filters.Add(typeof(ExceptionFilter)); }); + builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//תʱʹUnicode + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// ĬСշ null շ + options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; + }); builder.Services.AddScoped(sp => @@ -115,11 +97,8 @@ namespace Learn.VideoAnalysis var app = builder.Build(); AppCommon.Services = app.Services; - app.UseMiddleware("Swagger"); - // Configure the HTTP request pipeline. - _ = app.Services.GetRequiredService(); app.UseSwagger(); app.UseSwaggerUI(); diff --git a/VideoAnalysis/WebUI/.env.development b/VideoAnalysis/WebUI/.env.development index 54402ee..9e64136 100644 --- a/VideoAnalysis/WebUI/.env.development +++ b/VideoAnalysis/WebUI/.env.development @@ -8,7 +8,7 @@ VITE_PUBLIC_PATH = / VITE_ROUTER_HISTORY = "hash" # 接口地址 -VITE_API_BASEURL = "http://192.168.2.33:5238/api" +VITE_API_BASEURL = "http://192.168.2.33:5238" # # 接口地址 diff --git a/VideoAnalysis/WebUI/src/api/enum.ts b/VideoAnalysis/WebUI/src/api/enum.ts index 655d62f..544e220 100644 --- a/VideoAnalysis/WebUI/src/api/enum.ts +++ b/VideoAnalysis/WebUI/src/api/enum.ts @@ -8,7 +8,7 @@ import type { Res } from "@/utils/http/types"; * @return {object} */ export function getenum(type) { - return http.request("get", `Public/enum/${type}`); + return http.request("get", `api/Public/enum/${type}`); } /** * @description 获取枚举对象 @@ -16,5 +16,5 @@ export function getenum(type) { * @return {object} */ export function getenumDic(type) { - return http.request("get", `Public/enum/${type}/Dic`); + return http.request("get", `api/Public/enum/${type}/Dic`); } diff --git a/VideoAnalysis/WebUI/src/api/hTable.ts b/VideoAnalysis/WebUI/src/api/hTable.ts index 1ea2b59..2ccdb58 100644 --- a/VideoAnalysis/WebUI/src/api/hTable.ts +++ b/VideoAnalysis/WebUI/src/api/hTable.ts @@ -9,23 +9,27 @@ export class hTableAPI { this.url = url; } PageList(data = {}) { - return http.request>("post", `${this.url}/PageList`, { data }); + return http.request>("post", `api/${this.url}/PageList`, { data }); } Info(tag) { - const pUrl = `${this.url}/${tag}`; + const pUrl = `api/${this.url}/${tag}`; let getUrl = pUrl; return http.request>("get", getUrl); } edit(data) { - return http.request>("post", `${this.url}/Edit`, { data }); + return http.request>("post", `api/${this.url}/Edit`, { data }); } delete(data) { - return http.request>("post", `${this.url}/Del`, { data }); + return http.request>("post", `api/${this.url}/Del`, { data }); } querycombo(data = {}) { - return http.request>("post", `${this.url}/QueryCombo`, { - data - }); + return http.request>( + "post", + `api/${this.url}/QueryCombo`, + { + data + } + ); } } diff --git a/VideoAnalysis/WebUI/src/api/user.ts b/VideoAnalysis/WebUI/src/api/user.ts index db1b9aa..792f7d8 100644 --- a/VideoAnalysis/WebUI/src/api/user.ts +++ b/VideoAnalysis/WebUI/src/api/user.ts @@ -34,7 +34,7 @@ export type RefreshTokenResult = { /** 登录 */ export const getLogin = (data?: object) => { - return http.request("post", "/api/Public/Login", { data }); + return http.request("post", "/api/Admin/Login", { data }); }; /** 刷新`token` */ diff --git a/VideoAnalysis/WebUI/src/api/videoTask.ts b/VideoAnalysis/WebUI/src/api/videoTask.ts index 7ca1f43..02a1083 100644 --- a/VideoAnalysis/WebUI/src/api/videoTask.ts +++ b/VideoAnalysis/WebUI/src/api/videoTask.ts @@ -37,22 +37,22 @@ export interface RowRloadResult { /** 刷新任务实时数据 */ export const RowRload = (id: any) => { - return http.request("post", "/api/VideoTask/RowRload", { - params: id + return http.request("get", "/api/VideoTask/RowRload", { + params: { id } }); }; /** 重试任务 */ export const ReStart = (id: any, selectEnum: number) => { - return http.request("post", "/api/VideoTask/ReStart", { + return http.request("get", "/api/VideoTask/ReStart", { params: { id, selectEnum } }); }; -/** 重试任务 */ +/** 展示数据 */ export const ShowTaskInfo = (id: any) => { - return http.request("post", "/api/VideoTask/ShowTaskInfo", { + return http.request("get", "/api/VideoTask/ShowTaskInfo", { params: { id } }); }; diff --git a/VideoAnalysis/WebUI/src/components/hTable/index.vue b/VideoAnalysis/WebUI/src/components/hTable/index.vue index 6afc551..f32897b 100644 --- a/VideoAnalysis/WebUI/src/components/hTable/index.vue +++ b/VideoAnalysis/WebUI/src/components/hTable/index.vue @@ -210,7 +210,7 @@ function handleDelete(obj, row) { ElMessageBox.confirm("此操作将永久删除勾选记录, 是否继续?") .then(() => { Api.delete(ids).then((res) => { - if (res.code === 200) { + if (true) { handleReloadPaged(); ElMessage.success("删除成功"); } else { @@ -371,8 +371,8 @@ function fetchPagedData() { } } Api.PageList(table.value.search).then((res) => { - if (res.code === 200) { - table.value.data = res.data.data.map((s, i) => { + if (true) { + table.value.data = res.data.map((s, i) => { return { ...s, customId: i }; }); table.value.pageData = res.data; diff --git a/VideoAnalysis/WebUI/src/utils/http/index.ts b/VideoAnalysis/WebUI/src/utils/http/index.ts index f27807b..6d6fa5f 100644 --- a/VideoAnalysis/WebUI/src/utils/http/index.ts +++ b/VideoAnalysis/WebUI/src/utils/http/index.ts @@ -208,6 +208,9 @@ class PureHttp { router.push({ path: "/error/403" }); + } else if (error.response?.status !== 200) { + ElMessage.warning("请求失败" + $error.message + " "); + console.log("请求失败" ,$error); } // 所有的响应异常 区分来源为取消请求/非取消请求 return Promise.reject($error); @@ -234,11 +237,7 @@ class PureHttp { PureHttp.axiosInstance .request(config) .then((response: any) => { - debugger - if (response.code != null && response.code !== 200) { - - message(response.message, { type: "error" }); - } resolve(response); + resolve(response); }) .catch(error => { if (error.status != 200) { diff --git a/VideoAnalysis/WebUI/src/views/login/index.vue b/VideoAnalysis/WebUI/src/views/login/index.vue new file mode 100644 index 0000000..fc05b9f --- /dev/null +++ b/VideoAnalysis/WebUI/src/views/login/index.vue @@ -0,0 +1,170 @@ + + + + + + + diff --git a/VideoAnalysis/WebUI/src/views/login/utils/motion.ts b/VideoAnalysis/WebUI/src/views/login/utils/motion.ts new file mode 100644 index 0000000..2b1182c --- /dev/null +++ b/VideoAnalysis/WebUI/src/views/login/utils/motion.ts @@ -0,0 +1,40 @@ +import { h, defineComponent, withDirectives, resolveDirective } from "vue"; + +/** 封装@vueuse/motion动画库中的自定义指令v-motion */ +export default defineComponent({ + name: "Motion", + props: { + delay: { + type: Number, + default: 50 + } + }, + render() { + const { delay } = this; + const motion = resolveDirective("motion"); + return withDirectives( + h( + "div", + {}, + { + default: () => [this.$slots.default()] + } + ), + [ + [ + motion, + { + initial: { opacity: 0, y: 100 }, + enter: { + opacity: 1, + y: 0, + transition: { + delay + } + } + } + ] + ] + ); + } +}); diff --git a/VideoAnalysis/WebUI/src/views/login/utils/rule.ts b/VideoAnalysis/WebUI/src/views/login/utils/rule.ts new file mode 100644 index 0000000..b32982c --- /dev/null +++ b/VideoAnalysis/WebUI/src/views/login/utils/rule.ts @@ -0,0 +1,28 @@ +import { reactive } from "vue"; +import type { FormRules } from "element-plus"; + +/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */ +export const REGEXP_PWD = + /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/; + +/** 登录校验 */ +const loginRules = reactive({ + password: [ + { + validator: (rule, value, callback) => { + if (value === "") { + callback(new Error("请输入密码")); + } else if (!REGEXP_PWD.test(value)) { + callback( + new Error("密码格式应为8-18位数字、字母、符号的任意两种组合") + ); + } else { + callback(); + } + }, + trigger: "blur" + } + ] +}); + +export { loginRules }; diff --git a/VideoAnalysis/WebUI/src/views/login/utils/static.ts b/VideoAnalysis/WebUI/src/views/login/utils/static.ts new file mode 100644 index 0000000..18268d8 --- /dev/null +++ b/VideoAnalysis/WebUI/src/views/login/utils/static.ts @@ -0,0 +1,5 @@ +import bg from "@/assets/login/bg.png"; +import avatar from "@/assets/login/avatar.svg?component"; +import illustration from "@/assets/login/illustration.svg?component"; + +export { bg, avatar, illustration }; diff --git a/VideoAnalysis/WebUI/src/views/welcome/index.vue b/VideoAnalysis/WebUI/src/views/welcome/index.vue index 15f5bf4..db21203 100644 --- a/VideoAnalysis/WebUI/src/views/welcome/index.vue +++ b/VideoAnalysis/WebUI/src/views/welcome/index.vue @@ -8,17 +8,19 @@ import { TableConfig, } from "@/components/hTable/hTable"; import { onMounted, ref } from "vue"; -import { fa } from "element-plus/es/locales.mjs"; +import { de, fa } from "element-plus/es/locales.mjs"; import { hTableAPI } from "@/api/hTable"; import { getenum } from "@/api/enum"; import { ruleRequired, ruleRequiredNumber } from "@/utils/rules"; import { ElMessage } from "element-plus"; -import { RowRload } from "@/api/videoTask"; +import { ReStart, RowRload } from "@/api/videoTask"; import { Refresh } from "@element-plus/icons-vue"; -const ControllerName = "ExamClassInfo"; +import { message } from "@/utils/message"; +import { json } from "stream/consumers"; +const ControllerName = "VideoTask"; defineOptions({ - name: "ClassExam", + name: ControllerName, }); const props = defineProps<{ @@ -37,48 +39,50 @@ const tableData: TableConfig = intTableData({ search: { // 查询条件 show: true, - PageSize: 999, + PageSize: 10, }, operationColumn: true, // 显示操作按钮 - operationColumnData: [ - { - topBtn: false, // 头部按钮 - show: true, - label: "详情", - btnType: "custom", - btnStyle: "primary", - custom: { - title: "考试班级详情", // 弹出框title - src: "exam/classExamRecord", // 组件路径 - width: "1600px", // 弹框宽度 - height: "800px", // 弹框高度 - }, - }, - ], + operationColumnData: [], column: { // 行数据 - schoolName: { - label: "学校", - width: "180px", - search: new TableColumnSearch(true, ConditionalType.Like), - }, - grade: { - label: "年级", - width: "120px", - custom: (row) => row.gradeLevel + row.gradeYear + "届", + id: { + label: "ID", + width: "140px", search: new TableColumnSearch(true), }, - className: { - label: "班级", - width: "80px", - search: new TableColumnSearch(true, ConditionalType.Like), + tagId: { + label: "标签ID", + width: "140px", + search: new TableColumnSearch(true), }, - examName: { - label: "最近考试", + videoType: { + label: "任务类型", + width: "100px", + type: "dropdown", + search: new TableColumnSearch(true), + }, + lastEnum: { + label: "最后状态", width: "200px", + type: "dropdown", + search: new TableColumnSearch(true), }, - peopleCount: { - label: "参考人数", + subject: { + label: "学科", + width: "100px", + type: "dropdown", + search: new TableColumnSearch(true), + }, + comeFrom: { + label: "IP", + width: "120px", + }, + mediaUrl: { + label: "媒体地址", + width: "300px", + }, + createTime: { + label: "创建时间", }, }, data: [], @@ -92,35 +96,67 @@ const dialogRef = ref({ value: null as any, data: Object as any, }); -let subjectEnum = ref([]); +let redisChannelEnum = ref([]); const showTable = ref(false); onMounted(async () => { //初始化数据 - subjectEnum.value = await getenum("SubjectEnum"); + tableData.column.videoType.setting.datasource = await getenum("AttachmentsInfoType"); + tableData.column.lastEnum.setting.datasource = await getenum("RedisChannelEnum"); + tableData.column.subject.setting.datasource = await getenum("SubjectEnum"); + redisChannelEnum.value = tableData.column.lastEnum.setting.datasource; showTable.value = true; }); +async function showDialog(row) { + dialogRef.value.data = row; + dialogRef.value.value = row.lastEnum; + dialogRef.value.visible = true; +} +async function submitRowRload() { + await ReStart(dialogRef.value.data.id, dialogRef.value.value); + dialogRef.value.visible = false; + message("重试任务", { type: "success" }); +} async function expandChange(row: any, expandedRows: any[]) { - let res = await RowRload(row.id); - row.TaskInfo = res; + if (expandedRows.find((s) => s == row)) RloadTaskInfo(row); } async function RloadTaskInfo(row: any) { let res = await RowRload(row.id); row.TaskInfo = res; - row.TaskInfo.active = stepData.value.findIndex((s) => s.value == row.TaskInfo.lastEnum); - for (const element of stepData.value) { - element.time = row.TaskInfo.startTime[element.title]; - if (element.value < row.TaskInfo.lastEnum) { - element.status = "finish"; - } else if (element.value == 60) { - element.status = "success"; - } else if (element.value == row.TaskInfo.lastEnum) { - element.status = "process"; - } else { - element.status = "wait"; + row.TaskInfo.stepData = JSON.parse(JSON.stringify(stepData.value)); + row.TaskInfo.active = row.TaskInfo.stepData.findIndex( + (s) => s.title == row.TaskInfo.lastEnum + ); + if (row.TaskInfo.startTime != null) { + for (const element of row.TaskInfo.stepData) { + element.time = formatDateToChinese( + row.TaskInfo.startTime[element.title.toLowerCase()] + ); + if (element.value < row.TaskInfo.lastEnum) { + element.status = "finish"; + } else if (element.value == 60) { + element.status = "success"; + } else if (element.value == row.TaskInfo.lastEnum) { + element.status = "process"; + } else { + element.status = "wait"; + } } } } +function formatDateToChinese(dateString) { + const date = new Date(dateString); + if (isNaN(date.getTime())) return ""; + + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + const hours = String(date.getHours()).padStart(2, "0"); + const minutes = String(date.getMinutes()).padStart(2, "0"); + const seconds = String(date.getSeconds()).padStart(2, "0"); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} interface StepData { status: "" | "wait" | "process" | "finish" | "error" | "success"; time: string | null; @@ -144,49 +180,59 @@ const stepData = ref([ - -

重试任务

+

ID : {{ dialogRef.data.id }}

将从哪个步骤重试?

@@ -197,16 +243,22 @@ const stepData = ref([ style="width: 240px" > +
- diff --git a/VideoAnalysisCore/Common/AppCommon.cs b/VideoAnalysisCore/Common/AppCommon.cs index a59063d..485ce4a 100644 --- a/VideoAnalysisCore/Common/AppCommon.cs +++ b/VideoAnalysisCore/Common/AppCommon.cs @@ -1,4 +1,5 @@ -using FreeRedis; +using FFmpeg.NET.Services; +using FreeRedis; using Microsoft.Extensions.DependencyModel; using Microsoft.IdentityModel.Tokens; using SqlSugar; @@ -13,7 +14,7 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; -using UserCenter.Model.Interface; +using UserCenter.Model; using VideoAnalysisCore.AICore.SherpaOnnx; using VideoAnalysisCore.Model; using VideoAnalysisCore.Model.Dto; @@ -203,16 +204,11 @@ namespace VideoAnalysisCore.Common .Where(u => !u.Name.StartsWith(nameof(Microsoft)) && !u.Name.StartsWith(nameof(System)) && !u.Name.StartsWith("netstandard") - && (u.Type == "project")); + && u.Type == "project"); var assemblies = assembliesStr.Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList(); - var assemblies1 = Assembly.GetEntryAssembly().GetReferencedAssemblies().Where(x => x.Name.StartsWith("App.") || x.Name.StartsWith("UserCenter.")) - .Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList(); - foreach (var item in assemblies1) - { - if (!assemblies.Contains(item)) - assemblies.Add(item); - } + var assembly = Assembly.Load("UserCenter.Model"); + assemblies.Add(assembly); return assemblies; } /// diff --git a/VideoAnalysisCore/Controllers/AdminController.cs b/VideoAnalysisCore/Controllers/AdminController.cs new file mode 100644 index 0000000..d06f354 --- /dev/null +++ b/VideoAnalysisCore/Controllers/AdminController.cs @@ -0,0 +1,51 @@ + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using System.Diagnostics; +using System.Security.Claims; +using UserCenter.Model; +using VideoAnalysisCore.Common; +using VideoAnalysisCore.Controllers.Dto; + +namespace VideoAnalysisCore.Controllers +{ + /// + /// 通用接口 + /// + [Authorize(AuthenticationSchemes = Authentication.vdAdmin)] + [Route("api/[controller]/[action]")] + public class AdminController : ControllerBase + { + public AdminController() + { + } + /// + /// 登录 + /// + /// 请求体 + /// + [HttpPost, AllowAnonymous] + public IActionResult Login([FromBody] AdminLoginReq req) + { + if (!ModelState.IsValid) return BadRequest(ModelState); + if (string.IsNullOrWhiteSpace(req.Account) + || string.IsNullOrWhiteSpace(req.Password)) + return BadRequest("无效的登录信息"); + + return Ok(new + { + //按钮权限 + Permissions = "*", + //用户名 + UserName = "管理员", + NickName = "管理员", + AccessToken = JwtHelper.GetToken(AppCommon.Config.AuthKey, + [ + new Claim("Id","999"), + ]) + }); + } + + } +} diff --git a/VideoAnalysisCore/Controllers/ApiController.cs b/VideoAnalysisCore/Controllers/ApiController.cs deleted file mode 100644 index 8b92698..0000000 --- a/VideoAnalysisCore/Controllers/ApiController.cs +++ /dev/null @@ -1,56 +0,0 @@ - -using VideoAnalysisCore.Common; -using Microsoft.AspNetCore.Mvc; -using System.Reflection; -using MapsterMapper; -using Mapster; -using VideoAnalysisCore.AICore.SherpaOnnx; -using UserCenter.Model.Enum; -using VideoAnalysisCore.AICore.GPT.ChatGPT; -using VideoAnalysisCore.AICore.GPT; -using System.Text.Json; -using VideoAnalysisCore.Model.Enum; -using Yitter.IdGenerator; -using VideoAnalysisCore.Model; -using Microsoft.AspNetCore.Http; -using VideoAnalysisCore.Model.Dto; -using VideoAnalysisCore.Controllers.Dto; -using VideoAnalysisCore.Common.Expand; -using Microsoft.Extensions.DependencyInjection; -using System.Threading.Tasks; -using VideoAnalysisCore.Model.ǿ; -using VideoAnalysisCore.Model.Interface; -using System.Security.Claims; -using UserCenter.Model; -using Microsoft.AspNetCore.Authorization; -using SqlSugar; - -namespace VideoAnalysisCore.Controllers -{ - [ApiController] - [Route("[controller]/[action]")] - [Authorize(AuthenticationSchemes = Authentication.vdAdmin)] - public class ApiController : ControllerBase - { - private readonly IMapper mp; - private readonly Repository videoTaskDB; - private readonly RedisManager redisManager; - private readonly Repository videoKonwDB; - private readonly Repository courseInfoDB; - public readonly SenseVoice senseVoice; - public ApiController(Repository videoTaskDB, - IMapper mp, Repository videoKonwDB, Repository courseInfoDB, RedisManager redisManager, SenseVoice senseVoice) - { - this.videoTaskDB = videoTaskDB; - this.mp = mp; - this.videoKonwDB = videoKonwDB; - this.courseInfoDB = courseInfoDB; - this.redisManager = redisManager; - this.senseVoice = senseVoice; - } - - - - - } -} diff --git a/VideoAnalysisCore/Controllers/PublicController.cs b/VideoAnalysisCore/Controllers/PublicController.cs index 5a20e18..eb660e4 100644 --- a/VideoAnalysisCore/Controllers/PublicController.cs +++ b/VideoAnalysisCore/Controllers/PublicController.cs @@ -75,33 +75,8 @@ namespace VideoAnalysisCore.Controllers AppCommon.Config }); } -#endif - /// - /// 视频处理 - /// - /// 请求体 - /// - [HttpPost, AllowAnonymous] - public async Task> Login(AdminLoginReq req) - { - if (!ModelState.IsValid) return BadRequest(ModelState); - if (string.IsNullOrWhiteSpace(req.Account) - || string.IsNullOrWhiteSpace(req.Password)) - return BadRequest("无效的登录信息"); - return Ok(new - { - //按钮权限 - Permissions = "*", - //用户名 - UserName = "管理员", - NickName = "管理员", - AccessToken = JwtHelper.GetToken(AppCommon.Config.AuthKey, - [ - new Claim("Id","999"), - ]) - }); - } +#endif } } diff --git a/VideoAnalysisCore/Controllers/VideoTaskController.cs b/VideoAnalysisCore/Controllers/VideoTaskController.cs index 4a6c4c7..475f118 100644 --- a/VideoAnalysisCore/Controllers/VideoTaskController.cs +++ b/VideoAnalysisCore/Controllers/VideoTaskController.cs @@ -177,8 +177,8 @@ namespace VideoAnalysisCore.Controllers /// 自定义id /// 切换任务所属学科 null忽略 /// - [HttpGet(Name = "ReStart")] - public async Task ReStart(long taskId, string? tagId, SubjectEnum? subject) + [HttpGet] + public async Task ReStartTask(long taskId, string? tagId, SubjectEnum? subject) { var task = await baseService.AsQueryable() .WhereIF(taskId != 0, s => s.Id == taskId) @@ -192,7 +192,7 @@ namespace VideoAnalysisCore.Controllers await baseService.UpdateAsync(task); } //todo重新开始执行GPT分析 - + return BadRequest("任务为实现"); return Ok(); } @@ -241,9 +241,9 @@ namespace VideoAnalysisCore.Controllers public override async Task PageList([FromBody] QueryRequestBase model) { var sqlquery = base.BaseQuery(model) - .Select(s =>new VideoTask + .Select(s => new VideoTask { - Id = s.Id, + Id = s.Id, TagId = s.TagId, VideoType = s.VideoType, LastEnum = s.LastEnum, @@ -267,7 +267,7 @@ namespace VideoAnalysisCore.Controllers /// 任务类型 /// [HttpGet] - public async Task ReStart(long id, RedisChannelEnum selectEnum) + public async Task ReStart(long id, RedisChannelEnum selectEnum) { await redisManager.ClearTaskError(id); await Task.Run(async () => @@ -282,17 +282,21 @@ namespace VideoAnalysisCore.Controllers /// 任务id /// [HttpGet] - public async Task RowRload(long id) + public async Task RowRload(long id) { + if (id == 0) + return BadRequest("无效id"); var d = await redisManager.Redis.HMGetAsync(RedisExpandKey.Task(id), "Progress", "LastEnum", "StartTime", "ErrorMessage"); - return Ok(new + return Ok(new { Progress = d[0], LastEnum = d[1]?.ToEnum().ToString(), - StartTime = JsonSerializer.Deserialize>( d[2]), + StartTime = d[2] != null + ? JsonSerializer.Deserialize>(d[2]) + : null, ErrorMessage = d[3], - }) ; + }); } /// @@ -301,7 +305,7 @@ namespace VideoAnalysisCore.Controllers /// 任务id /// [HttpGet] - public async Task ShowTaskInfo(long id) + public async Task ShowTaskInfo(long id) { var nowTask = await baseService.GetFirstAsync(s => s.Id == id); if (nowTask is null) @@ -342,7 +346,8 @@ namespace VideoAnalysisCore.Controllers .Where(s => s.StageId == item.StageId).ToArray(); } - return Ok(new { + return Ok(new + { Captions = captionsArr, Captions1 = captionsArr1, VideoKnows = videoKnows, diff --git a/VideoAnalysisCore/VideoAnalysisCore.csproj b/VideoAnalysisCore/VideoAnalysisCore.csproj index 1a77a00..7af44fc 100644 --- a/VideoAnalysisCore/VideoAnalysisCore.csproj +++ b/VideoAnalysisCore/VideoAnalysisCore.csproj @@ -75,7 +75,7 @@ - +