完善 任务列表页面
This commit is contained in:
parent
963448382d
commit
24502a526d
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SwaggerFileUploadFilter>();
|
||||
//按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<ApiExplorerSettingsAttribute>().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<string>()
|
||||
}
|
||||
});
|
||||
|
||||
//添加一个必须的全局安全信息,和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<string, OpenApiSchema> schema = new Dictionary<string, OpenApiSchema>();
|
||||
schema["fileName"] = new OpenApiSchema { Description = "选择上传文件", Type = "string", Format = "binary" };
|
||||
Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>();
|
||||
content["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = schema } };
|
||||
operation.RequestBody = new OpenApiRequestBody() { Content = content };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<BasicAuthMiddleware>("Swagger");
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
|
||||
_ = app.Services.GetRequiredService<RedisInit>();
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
||||
# # 接口地址
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type { Res } from "@/utils/http/types";
|
|||
* @return {object}
|
||||
*/
|
||||
export function getenum(type) {
|
||||
return http.request<ComboModel[]>("get", `Public/enum/${type}`);
|
||||
return http.request<ComboModel[]>("get", `api/Public/enum/${type}`);
|
||||
}
|
||||
/**
|
||||
* @description 获取枚举对象
|
||||
|
|
@ -16,5 +16,5 @@ export function getenum(type) {
|
|||
* @return {object}
|
||||
*/
|
||||
export function getenumDic(type) {
|
||||
return http.request<any>("get", `Public/enum/${type}/Dic`);
|
||||
return http.request<any>("get", `api/Public/enum/${type}/Dic`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,23 +9,27 @@ export class hTableAPI {
|
|||
this.url = url;
|
||||
}
|
||||
PageList(data = {}) {
|
||||
return http.request<Res<any>>("post", `${this.url}/PageList`, { data });
|
||||
return http.request<Res<any>>("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<Res<any>>("get", getUrl);
|
||||
}
|
||||
edit(data) {
|
||||
return http.request<Res<any>>("post", `${this.url}/Edit`, { data });
|
||||
return http.request<Res<any>>("post", `api/${this.url}/Edit`, { data });
|
||||
}
|
||||
delete(data) {
|
||||
return http.request<Res<any>>("post", `${this.url}/Del`, { data });
|
||||
return http.request<Res<any>>("post", `api/${this.url}/Del`, { data });
|
||||
}
|
||||
querycombo(data = {}) {
|
||||
return http.request<Res<ComboModel[]>>("post", `${this.url}/QueryCombo`, {
|
||||
data
|
||||
});
|
||||
return http.request<Res<ComboModel[]>>(
|
||||
"post",
|
||||
`api/${this.url}/QueryCombo`,
|
||||
{
|
||||
data
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export type RefreshTokenResult = {
|
|||
|
||||
/** 登录 */
|
||||
export const getLogin = (data?: object) => {
|
||||
return http.request<UserResult>("post", "/api/Public/Login", { data });
|
||||
return http.request<UserResult>("post", "/api/Admin/Login", { data });
|
||||
};
|
||||
|
||||
/** 刷新`token` */
|
||||
|
|
|
|||
|
|
@ -37,22 +37,22 @@ export interface RowRloadResult {
|
|||
|
||||
/** 刷新任务实时数据 */
|
||||
export const RowRload = (id: any) => {
|
||||
return http.request<RowRloadResult>("post", "/api/VideoTask/RowRload", {
|
||||
params: id
|
||||
return http.request<RowRloadResult>("get", "/api/VideoTask/RowRload", {
|
||||
params: { id }
|
||||
});
|
||||
};
|
||||
|
||||
/** 重试任务 */
|
||||
export const ReStart = (id: any, selectEnum: number) => {
|
||||
return http.request<any>("post", "/api/VideoTask/ReStart", {
|
||||
return http.request<any>("get", "/api/VideoTask/ReStart", {
|
||||
params: { id, selectEnum }
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** 重试任务 */
|
||||
/** 展示数据 */
|
||||
export const ShowTaskInfo = (id: any) => {
|
||||
return http.request<ShowTaskInfoRes>("post", "/api/VideoTask/ShowTaskInfo", {
|
||||
return http.request<ShowTaskInfoRes>("get", "/api/VideoTask/ShowTaskInfo", {
|
||||
params: { id }
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
<script setup lang="ts">
|
||||
import Motion from "./utils/motion";
|
||||
import { useRouter } from "vue-router";
|
||||
import { message } from "@/utils/message";
|
||||
import { loginRules } from "./utils/rule";
|
||||
import { ref, reactive, toRaw } from "vue";
|
||||
import { debounce } from "@pureadmin/utils";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import { useEventListener } from "@vueuse/core";
|
||||
import type { FormInstance } from "element-plus";
|
||||
import { useLayout } from "@/layout/hooks/useLayout";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { initRouter, getTopMenu } from "@/router/utils";
|
||||
import { bg, avatar, illustration } from "./utils/static";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
||||
|
||||
import dayIcon from "@/assets/svg/day.svg?component";
|
||||
import darkIcon from "@/assets/svg/dark.svg?component";
|
||||
import Lock from "~icons/ri/lock-fill";
|
||||
import User from "~icons/ri/user-3-fill";
|
||||
|
||||
defineOptions({
|
||||
name: "Login",
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const disabled = ref(false);
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
|
||||
const { initStorage } = useLayout();
|
||||
initStorage();
|
||||
|
||||
const { dataTheme, overallStyle, dataThemeChange } = useDataThemeChange();
|
||||
dataThemeChange(overallStyle.value);
|
||||
const { title } = useNav();
|
||||
|
||||
const ruleForm = reactive({
|
||||
account: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const onLogin = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
useUserStoreHook()
|
||||
.loginByUsername({
|
||||
account: ruleForm.account,
|
||||
password: ruleForm.password,
|
||||
})
|
||||
.then((res) => {
|
||||
// 获取后端路由
|
||||
return initRouter().then(() => {
|
||||
disabled.value = true;
|
||||
router
|
||||
.push(getTopMenu(true).path)
|
||||
.then(() => {
|
||||
message("登录成功", { type: "success" });
|
||||
})
|
||||
.finally(() => (disabled.value = false));
|
||||
});
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const immediateDebounce: any = debounce((formRef) => onLogin(formRef), 1000, true);
|
||||
|
||||
useEventListener(document, "keydown", ({ code }) => {
|
||||
if (["Enter", "NumpadEnter"].includes(code) && !disabled.value && !loading.value)
|
||||
immediateDebounce(ruleFormRef.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="select-none">
|
||||
<img :src="bg" class="wave" />
|
||||
<div class="flex-c absolute right-5 top-3">
|
||||
<!-- 主题 -->
|
||||
<el-switch
|
||||
v-model="dataTheme"
|
||||
inline-prompt
|
||||
:active-icon="dayIcon"
|
||||
:inactive-icon="darkIcon"
|
||||
@change="dataThemeChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="login-container">
|
||||
<div class="img">
|
||||
<component :is="toRaw(illustration)" />
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<div class="login-form">
|
||||
<avatar class="avatar" />
|
||||
<Motion>
|
||||
<h2 class="outline-hidden">{{ title }}</h2>
|
||||
</Motion>
|
||||
|
||||
<!-- <el-form
|
||||
ref="ruleFormRef"
|
||||
:model="ruleForm"
|
||||
:rules="loginRules"
|
||||
size="large"
|
||||
> -->
|
||||
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" size="large">
|
||||
<Motion :delay="100">
|
||||
<el-form-item
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入账号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
prop="account"
|
||||
>
|
||||
<el-input
|
||||
v-model="ruleForm.account"
|
||||
clearable
|
||||
placeholder="账号"
|
||||
:prefix-icon="useRenderIcon(User)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</Motion>
|
||||
|
||||
<Motion :delay="150">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="ruleForm.password"
|
||||
clearable
|
||||
show-password
|
||||
placeholder="密码"
|
||||
:prefix-icon="useRenderIcon(Lock)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</Motion>
|
||||
|
||||
<Motion :delay="250">
|
||||
<el-button
|
||||
class="w-full mt-4!"
|
||||
size="default"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@click="onLogin(ruleFormRef)"
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
</Motion>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@import url("@/style/login.css");
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -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<FormRules>({
|
||||
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 };
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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<ComboModel[]>([]);
|
||||
let redisChannelEnum = ref<ComboModel[]>([]);
|
||||
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<StepData[]>([
|
|||
<template #expandSlot="{ props }">
|
||||
<!-- 拓展内容 -->
|
||||
<div class="expanded-content expandSlot">
|
||||
<el-descriptions title="任务详情" :column="2" border>
|
||||
<el-descriptions-item label="操作">
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Refresh"
|
||||
@click="RloadTaskInfo(props.row.id)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger">重试</el-button>
|
||||
<el-button type="primary">预览</el-button>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="props.row.lastEnum">
|
||||
{{ props.row.progress }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="步骤" :rowspan="2">
|
||||
<h3>任务详情</h3>
|
||||
<div class="InfoEx">
|
||||
<div>
|
||||
<span>进度</span>
|
||||
<div class="content">{{ props.row.lastEnum }} {{ props.row.progress }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>操作</span>
|
||||
<div class="content">
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="Refresh"
|
||||
@click="RloadTaskInfo(props.row)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" @click="showDialog(props.row.id)"
|
||||
>重试</el-button
|
||||
>
|
||||
<el-button type="primary">预览</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_item_full_width" v-if="props.row.TaskInfo != null">
|
||||
<span>步骤</span>
|
||||
<el-steps
|
||||
style="max-width: 1000px"
|
||||
:active="stepData.findIndex((s) => s.value == props.row.lastEnum)"
|
||||
class="content"
|
||||
style="max-width: 100%"
|
||||
:active="props.row.TaskInfo?.active"
|
||||
align-center
|
||||
>
|
||||
<el-step
|
||||
v-for="s in stepData"
|
||||
v-for="s in props.row.TaskInfo.stepData"
|
||||
:title="s.title"
|
||||
:description="s.time"
|
||||
:status="s.status"
|
||||
/>
|
||||
</el-steps>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item
|
||||
v-if="props.row.TaskInfo?.errorMessage != ''"
|
||||
label="错误信息"
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
props.row.TaskInfo?.errorMessage != null &&
|
||||
props.row.TaskInfo?.errorMessage.length > 0
|
||||
"
|
||||
>
|
||||
{{ props.row.TaskInfo?.errorMessage }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<h2>任务详情</h2>
|
||||
<div>1</div>
|
||||
<span>错误信息</span>
|
||||
<div class="content">{{ props.row.TaskInfo?.errorMessage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ahTable>
|
||||
|
||||
<el-dialog v-model="dialogRef.visible" title="重试任务" width="800">
|
||||
<h3>重试任务</h3>
|
||||
<el-dialog v-model="dialogRef.visible" title="重试任务" width="430">
|
||||
<h3>ID : {{ dialogRef.data.id }}</h3>
|
||||
<p></p>
|
||||
<p>将从哪个步骤重试?</p>
|
||||
|
|
@ -197,16 +243,22 @@ const stepData = ref<StepData[]>([
|
|||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in subjectEnum"
|
||||
v-for="item in redisChannelEnum"
|
||||
:key="item.value"
|
||||
:label="item.text"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogRef.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitRowRload()"> 提交 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
<style scoped>
|
||||
.expandSlot {
|
||||
padding: 10px 20px;
|
||||
background: #f5f7fa;
|
||||
|
|
@ -220,4 +272,24 @@ const stepData = ref<StepData[]>([
|
|||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.InfoEx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.InfoEx span {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.InfoEx .content {
|
||||
padding: 5px;
|
||||
}
|
||||
/* 关键:让某个网格项占满一行(跨所有列) */
|
||||
.grid_item_full_width {
|
||||
grid-column: 1 / -1; /* 从第1列跨到最后一列 */
|
||||
}
|
||||
/* :deep(.el-step__description) {
|
||||
font-size: 16px !important;
|
||||
} */
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 通用接口
|
||||
/// </summary>
|
||||
[Authorize(AuthenticationSchemes = Authentication.vdAdmin)]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class AdminController : ControllerBase
|
||||
{
|
||||
public AdminController()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// 登录
|
||||
/// </summary>
|
||||
/// <param name="req">请求体</param>
|
||||
/// <returns></returns>
|
||||
[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"),
|
||||
])
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<VideoTask> videoTaskDB;
|
||||
private readonly RedisManager redisManager;
|
||||
private readonly Repository<VideoKonwPoint> videoKonwDB;
|
||||
private readonly Repository<CourseInfo> courseInfoDB;
|
||||
public readonly SenseVoice senseVoice;
|
||||
public ApiController(Repository<VideoTask> videoTaskDB,
|
||||
IMapper mp, Repository<VideoKonwPoint> videoKonwDB, Repository<CourseInfo> courseInfoDB, RedisManager redisManager, SenseVoice senseVoice)
|
||||
{
|
||||
this.videoTaskDB = videoTaskDB;
|
||||
this.mp = mp;
|
||||
this.videoKonwDB = videoKonwDB;
|
||||
this.courseInfoDB = courseInfoDB;
|
||||
this.redisManager = redisManager;
|
||||
this.senseVoice = senseVoice;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -75,33 +75,8 @@ namespace VideoAnalysisCore.Controllers
|
|||
AppCommon.Config
|
||||
});
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 视频处理
|
||||
/// </summary>
|
||||
/// <param name="req">请求体</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, AllowAnonymous]
|
||||
public async Task<ActionResult<object>> 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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,8 +177,8 @@ namespace VideoAnalysisCore.Controllers
|
|||
/// <param name="tagId">自定义id</param>
|
||||
/// <param name="subject">切换任务所属学科 null忽略</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet(Name = "ReStart")]
|
||||
public async Task<IActionResult> ReStart(long taskId, string? tagId, SubjectEnum? subject)
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> 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<dynamic> 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
|
|||
/// <param name="selectEnum">任务类型</param>
|
||||
/// <returns></returns>
|
||||
[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
|
|||
/// <param name="id">任务id</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<object> RowRload(long id)
|
||||
public async Task<IActionResult> RowRload(long id)
|
||||
{
|
||||
if (id == 0)
|
||||
return BadRequest("无效id");
|
||||
var d = await redisManager.Redis.HMGetAsync<string>(RedisExpandKey.Task(id),
|
||||
"Progress", "LastEnum", "StartTime", "ErrorMessage");
|
||||
return Ok(new
|
||||
return Ok(new
|
||||
{
|
||||
Progress = d[0],
|
||||
LastEnum = d[1]?.ToEnum<RedisChannelEnum>().ToString(),
|
||||
StartTime = JsonSerializer.Deserialize<Dictionary<RedisChannelEnum, DateTime>>( d[2]),
|
||||
StartTime = d[2] != null
|
||||
? JsonSerializer.Deserialize<Dictionary<RedisChannelEnum, DateTime>>(d[2])
|
||||
: null,
|
||||
ErrorMessage = d[3],
|
||||
}) ;
|
||||
});
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
|
|
@ -301,7 +305,7 @@ namespace VideoAnalysisCore.Controllers
|
|||
/// <param name="id">任务id</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<object> ShowTaskInfo(long id)
|
||||
public async Task<object> 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,
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.205" />
|
||||
<PackageReference Include="UserCenter.Model" Version="1.3.5" />
|
||||
<PackageReference Include="UserCenter.Model" Version="1.4.9" />
|
||||
<PackageReference Include="Whisper.net" Version="1.5.0" />
|
||||
<PackageReference Include="Whisper.net.Runtime" Version="1.5.0" />
|
||||
<PackageReference Include="xFFmpeg.NET" Version="6.0.0" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue