refactor,feat: 批量代码重构与新增业务模块
1. 重命名枚举项与实体字段,修正类型引用 2. 新增IsNull扩展方法与多项字符串处理扩展 3. 新增大量业务DTO、服务接口与枚举定义 4. 重构RabbitMQ服务实现,替换旧版消息队列组件 5. 优化签到服务的宠物喂养事务逻辑 6. 移除冗余的项目引用与旧版消息队列代码 7. 新增Excel导出、导入模板相关工具方法
This commit is contained in:
441
QYZH.InteractiveMagazine.Common/Extensions/Extension.Convert.cs
Normal file
441
QYZH.InteractiveMagazine.Common/Extensions/Extension.Convert.cs
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Common.Extensions
|
||||||
|
{
|
||||||
|
public static partial class Extensions
|
||||||
|
{
|
||||||
|
#region 转换为long
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为long,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static long ParseToLong(this object obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return long.Parse(obj.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为long,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <param name="defaultValue"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static long ParseToLong(this string str, long defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return long.Parse(str);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为int
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为int,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int ParseToInt(this object str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(str);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为int,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// null返回默认值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <param name="defaultValue"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int ParseToInt(this object str, int defaultValue)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(str);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为short
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为short,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static short ParseToShort(this object obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return short.Parse(obj.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为short,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static short ParseToShort(this object str, short defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return short.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为demical
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为demical,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static decimal ParseToDecimal(this object str, decimal defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return decimal.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为demical,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static decimal ParseToDecimal(this object str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return decimal.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转化为bool
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为bool,若转换失败,则返回false。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool ParseToBool(this object str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return bool.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为bool,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool ParseToBool(this object str, bool result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return bool.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为float
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为float,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static float ParseToFloat(this object str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return float.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为float,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static float ParseToFloat(this object str, float result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return float.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为Guid
|
||||||
|
/// <summary>
|
||||||
|
/// 将string转换为Guid,若转换失败,则返回Guid.Empty。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Guid ParseToGuid(this string str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new Guid(str);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Guid.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为DateTime
|
||||||
|
/// <summary>
|
||||||
|
/// 将string转换为DateTime,若转换失败,则返回日期最小值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DateTime ParseToDateTime(this string str)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(str))
|
||||||
|
{
|
||||||
|
return DateTime.MinValue;
|
||||||
|
}
|
||||||
|
if (str.Contains("-") || str.Contains("/"))
|
||||||
|
{
|
||||||
|
return DateTime.Parse(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int length = str.Length;
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 6:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 8:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 10:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 12:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 14:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
default:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DateTime.MinValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将string转换为DateTime,若转换失败,则返回默认值。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <param name="defaultValue"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DateTime ParseToDateTime(this string str, DateTime? defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(str))
|
||||||
|
{
|
||||||
|
return defaultValue.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
if (str.Contains("-") || str.Contains("/"))
|
||||||
|
{
|
||||||
|
return DateTime.Parse(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int length = str.Length;
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 6:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 8:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 10:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 12:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
case 14:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
default:
|
||||||
|
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为string
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为string,若转换失败,则返回""。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ParseToString(this object obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return obj.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string ParseToStrings<T>(this object obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var list = obj as IEnumerable<T>;
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
return string.Join(",", list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return obj.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 转换为double
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为double,若转换失败,则返回0。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static double ParseToDouble(this object obj)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return double.Parse(obj.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将object转换为double,若转换失败,则返回指定值。不抛出异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <param name="defaultValue"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static double ParseToDouble(this object str, double defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return double.Parse(str.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 强制转换类型
|
||||||
|
/// <summary>
|
||||||
|
/// 强制转换类型
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResult"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
|
||||||
|
{
|
||||||
|
foreach (object item in source)
|
||||||
|
{
|
||||||
|
yield return (TResult)Convert.ChangeType(item, typeof(TResult));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
//using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Common.Extensions
|
||||||
|
{
|
||||||
|
public static partial class Extensions
|
||||||
|
{
|
||||||
|
public static bool IsEmpty(this object value)
|
||||||
|
{
|
||||||
|
if (value != null && !string.IsNullOrEmpty(value.ParseToString()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static bool IsNotEmpty(this object value)
|
||||||
|
{
|
||||||
|
return !IsEmpty(value);
|
||||||
|
}
|
||||||
|
public static bool IsNullOrZero(this object value)
|
||||||
|
{
|
||||||
|
if (value == null || value.ParseToString().Trim() == "0")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public static bool IsAjaxRequest(this HttpRequest request)
|
||||||
|
//{
|
||||||
|
// if (request == null)
|
||||||
|
// throw new ArgumentNullException("request");
|
||||||
|
|
||||||
|
// if (request.Headers != null)
|
||||||
|
// return request.Headers["X-Requested-With"] == "XMLHttpRequest";
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -95,5 +95,10 @@ namespace QYZH.InteractiveMagazine.Common.Extensions
|
|||||||
{
|
{
|
||||||
return s == null || s?.Count() < 1;
|
return s == null || s?.Count() < 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsNull(this object? s)
|
||||||
|
{
|
||||||
|
return s == null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,5 +163,55 @@ namespace QYZH.InteractiveMagazine.Common.Extensions
|
|||||||
// 匹配域名后的路径部分(包括第一个/)
|
// 匹配域名后的路径部分(包括第一个/)
|
||||||
return Regex.Replace(str, @"https?://[^/]+/([^""\s<>]*)", "$1");
|
return Regex.Replace(str, @"https?://[^/]+/([^""\s<>]*)", "$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AddDomain(this string str, string domainUrl)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(domainUrl)) return str;
|
||||||
|
|
||||||
|
string pattern = @"src=""([^""]+)""";
|
||||||
|
return Regex.Replace(str, pattern, match =>
|
||||||
|
{
|
||||||
|
string originalUrl = match.Groups[1].Value;
|
||||||
|
// 忽略base64图片
|
||||||
|
if (originalUrl.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) return match.Value; // 返回原样
|
||||||
|
// 如果已经是完整URL则不做处理
|
||||||
|
if (Uri.IsWellFormedUriString(originalUrl, UriKind.Absolute)) return match.Value; // 返回原样
|
||||||
|
|
||||||
|
|
||||||
|
// 处理URL开头可能存在的斜杠
|
||||||
|
var trimmedUrl = originalUrl.StartsWith("/") ? originalUrl.Substring(1) : originalUrl;
|
||||||
|
var trimmedDomain = domainUrl.EndsWith("/") ? domainUrl : domainUrl + "/";
|
||||||
|
|
||||||
|
string newUrl = $"{trimmedDomain}{trimmedUrl}";
|
||||||
|
return $"src=\"{newUrl}\""; // 返回替换后的属性
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToExtension(this string str)
|
||||||
|
{
|
||||||
|
return Regex.Match(str, @"(?<=\.)[a-zA-Z0-9]+$").Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NotNull(this string s)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> AllUrl(this string str, string url = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str)) return new List<string>();
|
||||||
|
|
||||||
|
string pattern = @"src=""([^""]+)""";
|
||||||
|
|
||||||
|
var matches = Regex.Matches(str, pattern, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
return matches
|
||||||
|
.Cast<Match>()
|
||||||
|
.Select(m => m.Groups[1].Value)
|
||||||
|
.Where(u => !string.IsNullOrEmpty(u) && !u.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) // 排除base64
|
||||||
|
.Where(url => !string.IsNullOrEmpty(url) && url.StartsWith(url, StringComparison.OrdinalIgnoreCase))
|
||||||
|
//.Where(url => url.StartsWith("https://example.com")) // 二次验证
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\QYZH.InteractiveMagazine.Models\QYZH.InteractiveMagazine.Models.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
|||||||
45
QYZH.InteractiveMagazine.IService/IJournalCatalogService.cs
Normal file
45
QYZH.InteractiveMagazine.IService/IJournalCatalogService.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Base;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.IService;
|
||||||
|
|
||||||
|
public interface IJournalCatalogService : IBaseService<JournalCatalog>
|
||||||
|
{
|
||||||
|
Task<List<JournalCatalog>> DetailAsync(long JournalId);
|
||||||
|
|
||||||
|
Task<long> InsertAsync(JournalCatalogInput input);
|
||||||
|
|
||||||
|
Task<long> ImportAsync(JournalImportDto input);
|
||||||
|
|
||||||
|
Task<bool> UpdateAsync(JournalCatalogUpdateInput input);
|
||||||
|
|
||||||
|
Task<bool> DeleteAsync(long id);
|
||||||
|
|
||||||
|
Task<bool> MoveAsync(MoveInput input);
|
||||||
|
|
||||||
|
//Task<bool> CopyAsync(CopyInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取书分类及页码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<JournalCatalogTreeListDto>> GetJournalCatalogListAsync(long JournalId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取书分类及页码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<IcrJournalCatalogTreeDto>> GetJournalCataloTreeAsync(long JournalId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入书籍目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> ImportCatalogAsync(long JournalId, List<JournalCatalogTreeListDto> dtos);
|
||||||
|
}
|
||||||
32
QYZH.InteractiveMagazine.IService/IJournalPageService.cs
Normal file
32
QYZH.InteractiveMagazine.IService/IJournalPageService.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.IService;
|
||||||
|
|
||||||
|
public interface IJournalPageService: IBaseService<JournalPage>
|
||||||
|
{
|
||||||
|
Task<long> InsertAsync(JournalAddV2Input input);
|
||||||
|
|
||||||
|
Task<bool> UpdateAsync(PageLayoutInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修改书页的点阵码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> UpdatePageNoAsync(long JournalId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打印书页(全部)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> PrintJournalPageAsync(long JournalId);
|
||||||
|
|
||||||
|
Task<JournalPageV2Output> DetailAsync(long id);
|
||||||
|
Task<bool> DeleteAsync(long id);
|
||||||
|
|
||||||
|
//Task<List<JournalPageNoArticleOutput>> PageNoArticleAsync(long id);
|
||||||
|
}
|
||||||
17
QYZH.InteractiveMagazine.IService/IJournalPageTaskService.cs
Normal file
17
QYZH.InteractiveMagazine.IService/IJournalPageTaskService.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.IService;
|
||||||
|
|
||||||
|
public interface IJournalPageTaskService : IBaseService<JournalPageTask>
|
||||||
|
{
|
||||||
|
Task<long?> InsertAsync(JournalPageTaskAddInput input);
|
||||||
|
Task<bool> UpdateAsync(JournalPageTaskUpdateInput input);
|
||||||
|
Task<bool> KeywordAnalysisAsync(JournalPageTaskKeywordAnalysis input);
|
||||||
|
Task<JournalPageTaskOutput> DetailAsync(long id);
|
||||||
|
|
||||||
|
Task<bool> DeleteAsync(long id);
|
||||||
|
Task<bool> ComplementAsync(JournalPageTaskComplementInput input);
|
||||||
|
}
|
||||||
48
QYZH.InteractiveMagazine.IService/IJournalService.cs
Normal file
48
QYZH.InteractiveMagazine.IService/IJournalService.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.IService;
|
||||||
|
|
||||||
|
public interface IJournalService : IBaseService<Journal>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 查询List
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<JournalDto>> GetListAsync(JournalQueryDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页查询
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="search"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<PageListModel<JournalDto>> GetPageListAsync(PageQueryModel<JournalQueryDto> search);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<BaseResponse<bool>> EditAsync(JournalEditDto input);
|
||||||
|
|
||||||
|
Task<List<JournalTaskOutput>> Tasks(long id);
|
||||||
|
|
||||||
|
Task<JournalDto> DetailAsync(long id);
|
||||||
|
|
||||||
|
Task<bool> DeleteAsync(List<long> id);
|
||||||
|
|
||||||
|
|
||||||
|
Task<bool> StartPageAsync(long Id, int Index);
|
||||||
|
|
||||||
|
Task<bool> StatusAsync(long id, JournalStatusEnum status);
|
||||||
|
|
||||||
|
Task<DotMatrixOutput> PrintCodeAsync(long id);
|
||||||
|
|
||||||
|
Task<bool> ResultReportAsync(DotMatrixNoteJournalReportInput input);
|
||||||
|
}
|
||||||
@ -37,6 +37,14 @@ public interface IPetService : IBaseService<UserPet>
|
|||||||
/// <returns>喂养结果</returns>
|
/// <returns>喂养结果</returns>
|
||||||
Task<FeedPetOutput> FeedPetAsync(long userId, FeedPetInput input);
|
Task<FeedPetOutput> FeedPetAsync(long userId, FeedPetInput input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 喂养宠物(无事务,需在外部事务中调用)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">用户Id</param>
|
||||||
|
/// <param name="input">喂养输入</param>
|
||||||
|
/// <returns>喂养结果</returns>
|
||||||
|
Task<FeedPetOutput> FeedPetInTranAsync(long userId, FeedPetInput input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取宠物喂养记录列表
|
/// 获取宠物喂养记录列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
using Autofac.Extensions.DependencyInjection;
|
using Autofac.Extensions.DependencyInjection;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using QYZH.InteractiveMagazine.Infrastructure.MessageQueue;
|
|
||||||
using QYZH.InteractiveMagazine.Models.Settings;
|
using QYZH.InteractiveMagazine.Models.Settings;
|
||||||
using RabbitMQ.Client;
|
using RabbitMQ.Client;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
@ -25,31 +24,7 @@ namespace QYZH.InteractiveMagazine.Infrastructure.Autofacs
|
|||||||
var source = friendlyName.Split('.');
|
var source = friendlyName.Split('.');
|
||||||
var assemblyNames = string.Join(".", source.Take(source.Length - 1));
|
var assemblyNames = string.Join(".", source.Take(source.Length - 1));
|
||||||
containerBuilder.RegisterModule(new AutofacModuleRegister(assemblyNames));
|
containerBuilder.RegisterModule(new AutofacModuleRegister(assemblyNames));
|
||||||
|
|
||||||
InitializeRabbitMQ(c.Configuration, containerBuilder);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void InitializeRabbitMQ(IConfiguration configuration, ContainerBuilder containerBuilder)
|
|
||||||
{
|
|
||||||
var rabbitMQSettings = configuration.GetSection("RabbitMQSettings").Get<RabbitMQSettings>();
|
|
||||||
if (rabbitMQSettings != null && !string.IsNullOrWhiteSpace(rabbitMQSettings.HostName))
|
|
||||||
{
|
|
||||||
var factory = new ConnectionFactory
|
|
||||||
{
|
|
||||||
HostName = rabbitMQSettings.HostName,
|
|
||||||
Port = rabbitMQSettings.Port,
|
|
||||||
UserName = rabbitMQSettings.UserName ?? string.Empty,
|
|
||||||
Password = rabbitMQSettings.Password ?? string.Empty,
|
|
||||||
VirtualHost = rabbitMQSettings.VirtualHost ?? string.Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
var connection = factory.CreateConnectionAsync().GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
containerBuilder.RegisterInstance(connection).As<IConnection>().SingleInstance();
|
|
||||||
containerBuilder.RegisterType<RabbitMQPublisher>().InstancePerDependency();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using RabbitMQ.Client;
|
|
||||||
using RabbitMQ.Client.Events;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace QYZH.InteractiveMagazine.Infrastructure.MessageQueue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RabbitMQ消息消费者基类
|
|
||||||
/// </summary>
|
|
||||||
public abstract class RabbitMQConsumer : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IConnection _connection;
|
|
||||||
private readonly ILogger<RabbitMQConsumer> _logger;
|
|
||||||
private IChannel? _channel;
|
|
||||||
private AsyncEventingBasicConsumer? _consumer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造函数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connection">RabbitMQ连接</param>
|
|
||||||
/// <param name="logger">日志记录器</param>
|
|
||||||
protected RabbitMQConsumer(IConnection connection, ILogger<RabbitMQConsumer> logger)
|
|
||||||
{
|
|
||||||
_connection = connection;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动消费
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="queueName">队列名称</param>
|
|
||||||
/// <param name="handleMessage">消息处理委托</param>
|
|
||||||
public async Task StartConsume(string queueName, Func<string, Task> handleMessage)
|
|
||||||
{
|
|
||||||
_channel = await _connection.CreateChannelAsync();
|
|
||||||
|
|
||||||
_consumer = new AsyncEventingBasicConsumer(_channel);
|
|
||||||
_consumer.ReceivedAsync += async (model, ea) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var body = ea.Body.ToArray();
|
|
||||||
var message = Encoding.UTF8.GetString(body);
|
|
||||||
|
|
||||||
await handleMessage(message);
|
|
||||||
|
|
||||||
await _channel.BasicAckAsync(ea.DeliveryTag, false);
|
|
||||||
|
|
||||||
_logger.LogInformation("消息消费成功 | 队列: {QueueName} | 消息: {Message}", queueName, message);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "消息消费失败 | 队列: {QueueName}", queueName);
|
|
||||||
await _channel.BasicNackAsync(ea.DeliveryTag, false, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await _channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumer: _consumer);
|
|
||||||
|
|
||||||
_logger.LogInformation("开始消费消息 | 队列: {QueueName}", queueName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 释放资源
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_channel?.DisposeAsync().GetAwaiter().GetResult();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using RabbitMQ.Client;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace QYZH.InteractiveMagazine.Infrastructure.MessageQueue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RabbitMQ消息发布器
|
|
||||||
/// </summary>
|
|
||||||
public class RabbitMQPublisher
|
|
||||||
{
|
|
||||||
private readonly IConnection _connection;
|
|
||||||
private readonly ILogger<RabbitMQPublisher> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造函数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connection">RabbitMQ连接</param>
|
|
||||||
/// <param name="logger">日志记录器</param>
|
|
||||||
public RabbitMQPublisher(IConnection connection, ILogger<RabbitMQPublisher> logger)
|
|
||||||
{
|
|
||||||
_connection = connection;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发布消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="exchange">交换机名称</param>
|
|
||||||
/// <param name="routingKey">路由键</param>
|
|
||||||
/// <param name="message">消息内容</param>
|
|
||||||
public async Task PublishMessage(string exchange, string routingKey, string message)
|
|
||||||
{
|
|
||||||
await using var channel = await _connection.CreateChannelAsync();
|
|
||||||
|
|
||||||
var body = Encoding.UTF8.GetBytes(message);
|
|
||||||
|
|
||||||
await channel.BasicPublishAsync(
|
|
||||||
exchange: exchange,
|
|
||||||
routingKey: routingKey,
|
|
||||||
body: body
|
|
||||||
);
|
|
||||||
|
|
||||||
_logger.LogInformation("消息已发布 | 交换机: {Exchange} | 路由键: {RoutingKey} | 消息: {Message}",
|
|
||||||
exchange, routingKey, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -217,7 +217,7 @@ namespace QYZH.InteractiveMagazine.Infrastructure.OSS
|
|||||||
|
|
||||||
|
|
||||||
//var del = _ossResourceRepository.Delete(w => keys.Contains(w.Path));
|
//var del = _ossResourceRepository.Delete(w => keys.Contains(w.Path));
|
||||||
//AppException.ThrowIf(!del, "删除资源失败");
|
//BusinessException.ThrowIf(!del, "删除资源失败");
|
||||||
|
|
||||||
|
|
||||||
return result.Keys.Count() == keys.Count;
|
return result.Keys.Count() == keys.Count;
|
||||||
@ -286,7 +286,7 @@ namespace QYZH.InteractiveMagazine.Infrastructure.OSS
|
|||||||
// Path = targetObject,
|
// Path = targetObject,
|
||||||
|
|
||||||
//});
|
//});
|
||||||
//AppException.ThrowIf(data.IsNull(), "资源添加失败");
|
//BusinessException.ThrowIf(data.IsNull(), "资源添加失败");
|
||||||
|
|
||||||
var req = new CopyObjectRequest(_ossOption.BucketName, sourceObject, _ossOption.BucketName, targetObject)
|
var req = new CopyObjectRequest(_ossOption.BucketName, sourceObject, _ossOption.BucketName, targetObject)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public interface IRabbitMQService
|
||||||
|
{
|
||||||
|
Task<bool> SendAsync(RabbitMQSendParam param, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<bool> SendBatchAsync(IEnumerable<RabbitMQSendParam> @params, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task ReceiveAsync(string queueName, Func<IChannel, BasicDeliverEventArgs, Task> callback, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public interface IRabbitMQConnection : IDisposable
|
||||||
|
{
|
||||||
|
Task<IChannel> CreateChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RabbitMQConnection : IRabbitMQConnection
|
||||||
|
{
|
||||||
|
private readonly ConnectionFactory _factory;
|
||||||
|
private readonly IConnection _connection;
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
public RabbitMQConnection(ConnectionFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
|
||||||
|
_connection = factory.CreateConnectionAsync().Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IChannel> CreateChannel()
|
||||||
|
{
|
||||||
|
EnsureNotDisposed();
|
||||||
|
return await _connection.CreateChannelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_isDisposed) return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Free any other managed objects here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free any unmanaged objects here.
|
||||||
|
_connection.Dispose();
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~RabbitMQConnection()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureNotDisposed()
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(RabbitMQConnection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public class RabbitMQOptions
|
||||||
|
{
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string HostName { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string ExchangeName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string VirtualHost { get; set; } = "/";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public class RabbitMQSendParam
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 交换机 默认空
|
||||||
|
/// </summary>
|
||||||
|
public string Exchange { get; set; } = "";
|
||||||
|
public string Queue { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 路由键
|
||||||
|
/// </summary>
|
||||||
|
public string RoutingKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息数据
|
||||||
|
/// </summary>
|
||||||
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否清空队列
|
||||||
|
/// </summary>
|
||||||
|
public bool Purge { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public class RabbitMQService : IRabbitMQService
|
||||||
|
{
|
||||||
|
private readonly IRabbitMQConnection _connection;
|
||||||
|
private readonly JsonSerializerOptions options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
public RabbitMQService(IRabbitMQConnection connection)
|
||||||
|
{
|
||||||
|
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> SendAsync(RabbitMQSendParam param, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var channel = await _connection.CreateChannel();
|
||||||
|
|
||||||
|
// 启用发布者确认
|
||||||
|
//var channelOpts = new CreateChannelOptions
|
||||||
|
//(
|
||||||
|
// publisherConfirmationsEnabled: true,
|
||||||
|
// publisherConfirmationTrackingEnabled: true,
|
||||||
|
// outstandingPublisherConfirmationsRateLimiter: new ThrottlingRateLimiter(MAX_OUTSTANDING_CONFIRMS)
|
||||||
|
//);
|
||||||
|
|
||||||
|
// 声明队列(持久化)
|
||||||
|
await channel.QueueDeclareAsync(queue: param.RoutingKey, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
// 清空队列
|
||||||
|
if (param.Purge) await channel.QueuePurgeAsync(param.RoutingKey);
|
||||||
|
// 消息序列化
|
||||||
|
var mesjson = JsonSerializer.Serialize(param.Data, options);
|
||||||
|
|
||||||
|
var body = Encoding.UTF8.GetBytes(mesjson);
|
||||||
|
var properties = new BasicProperties
|
||||||
|
{
|
||||||
|
Persistent = true // 设置消息持久化
|
||||||
|
};
|
||||||
|
await channel.BasicPublishAsync(param.Exchange, param.RoutingKey, false, properties, body, cancellationToken);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Operation was canceled: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> SendBatchAsync(IEnumerable<RabbitMQSendParam> @params, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
IChannel channel = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
channel = await _connection.CreateChannel();
|
||||||
|
|
||||||
|
// 开启事务
|
||||||
|
await channel.TxSelectAsync();
|
||||||
|
|
||||||
|
var properties = new BasicProperties
|
||||||
|
{
|
||||||
|
Persistent = true // 设置消息持久化
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量发送消息到不同的 routingKey
|
||||||
|
var declaredExchanges = new HashSet<string>();
|
||||||
|
foreach (var param in @params)
|
||||||
|
{
|
||||||
|
// 声明 Exchange(持久化)
|
||||||
|
if (declaredExchanges.Add(param.Exchange))
|
||||||
|
{
|
||||||
|
await channel.ExchangeDeclareAsync(exchange: param.Exchange, type: "direct", durable: true, autoDelete: false, arguments: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 声明队列(持久化)
|
||||||
|
await channel.QueueDeclareAsync(queue: param.Queue, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
|
// 绑定队列到 Exchange
|
||||||
|
await channel.QueueBindAsync(queue: param.Queue, exchange: param.Exchange, routingKey: param.RoutingKey, arguments: null);
|
||||||
|
|
||||||
|
// 清空队列
|
||||||
|
if (param.Purge) await channel.QueuePurgeAsync(param.Queue);
|
||||||
|
|
||||||
|
// 消息序列化
|
||||||
|
var mesjson = JsonSerializer.Serialize(param.Data, options);
|
||||||
|
var body = Encoding.UTF8.GetBytes(mesjson);
|
||||||
|
|
||||||
|
// 发布消息
|
||||||
|
await channel.BasicPublishAsync(param.Exchange, param.RoutingKey, false, properties, body, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交事务 - 确保所有消息都发送成功
|
||||||
|
await channel.TxCommitAsync();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||||
|
// 回滚事务
|
||||||
|
try { await channel?.TxRollbackAsync(); } catch { /* 忽略回滚异常 */ }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (channel != null && !channel.IsClosed)
|
||||||
|
{
|
||||||
|
await channel.CloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task ReceiveAsync(string queueName, Func<IChannel, BasicDeliverEventArgs, Task> callback, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var channel = await _connection.CreateChannel();
|
||||||
|
//await channel.BasicQosAsync(0, 10, false); // 一次最多接收10条未确认的消息
|
||||||
|
await channel.QueueDeclareAsync(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
|
var consumer = new AsyncEventingBasicConsumer(channel);
|
||||||
|
consumer.ReceivedAsync += async (model, ea) =>
|
||||||
|
{
|
||||||
|
//var body = ea.Body.ToArray();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 直接传递 model 和 body 给 callback,不需要转换
|
||||||
|
await callback(channel, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//await channel.BasicAckAsync(ea.DeliveryTag, false, cancellationToken);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumer: consumer, cancellationToken: cancellationToken);
|
||||||
|
// Prevent the method from returning immediately
|
||||||
|
await Task.Delay(-1, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Infrastructure.RabbitMQ
|
||||||
|
{
|
||||||
|
public static class RabbiteMQExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化消息队列,并添加Publisher到IoC容器
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>从Configuration读取"RabbbitMQOptions配置项"</remarks>
|
||||||
|
public static IServiceCollection AddRabbitMQ(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var rabbitMqSection = configuration.GetSection("RabbitMq");
|
||||||
|
|
||||||
|
if (rabbitMqSection.Exists())
|
||||||
|
{
|
||||||
|
// 绑定RabbitMQ配置
|
||||||
|
services.Configure<RabbitMQOptions>(rabbitMqSection);
|
||||||
|
// 注册RabbitMQ连接工厂
|
||||||
|
services.AddSingleton<IRabbitMQConnection, RabbitMQConnection>(sp =>
|
||||||
|
{
|
||||||
|
var options = sp.GetRequiredService<IOptions<RabbitMQOptions>>().Value;
|
||||||
|
var factory = new ConnectionFactory()
|
||||||
|
{
|
||||||
|
HostName = options.HostName,
|
||||||
|
Port = options.Port,
|
||||||
|
UserName = options.UserName,
|
||||||
|
Password = options.Password,
|
||||||
|
VirtualHost = options.VirtualHost,
|
||||||
|
|
||||||
|
// 自动恢复配置
|
||||||
|
AutomaticRecoveryEnabled = true, // 启用自动恢复
|
||||||
|
NetworkRecoveryInterval = TimeSpan.FromSeconds(10), // 每10秒尝试重连
|
||||||
|
// 心跳检测
|
||||||
|
RequestedHeartbeat = TimeSpan.FromSeconds(10), // 60秒心跳
|
||||||
|
// 其他重要配置
|
||||||
|
TopologyRecoveryEnabled = true, // 恢复交换机、队列等拓扑结构
|
||||||
|
RequestedConnectionTimeout = TimeSpan.FromSeconds(30), // 连接超时
|
||||||
|
SocketReadTimeout = TimeSpan.FromSeconds(30), // 读取超时
|
||||||
|
SocketWriteTimeout = TimeSpan.FromSeconds(30) // 写入超时
|
||||||
|
};
|
||||||
|
return new RabbitMQConnection(factory);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加RabbitMQService的服务注册
|
||||||
|
services.AddSingleton<IRabbitMQService, RabbitMQService>();
|
||||||
|
//services.AddHostedService<TerminalReportService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,4 +43,23 @@ public class BusinessException : Exception
|
|||||||
public BusinessException(string message, Exception innerException) : base(message, innerException)
|
public BusinessException(string message, Exception innerException) : base(message, innerException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ThrowIf(bool isTrue, string message)
|
||||||
|
{
|
||||||
|
if (isTrue) throw new BusinessException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ThrowIf(bool isTrue, Func<string> action)
|
||||||
|
{
|
||||||
|
if (isTrue) throw new BusinessException(action.Invoke());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ThrowIf(bool isTrue, string message, Action action)
|
||||||
|
{
|
||||||
|
if (isTrue)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
throw new BusinessException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.DotMatrix
|
||||||
|
{
|
||||||
|
public class DotMatrixNoteJournalReportInput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
public string FileKey { get; set; }
|
||||||
|
|
||||||
|
public string FileUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.DotMatrix
|
||||||
|
{
|
||||||
|
public class DotMatrixOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 方法
|
||||||
|
/// </summary>
|
||||||
|
public string Method { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// id
|
||||||
|
/// </summary>
|
||||||
|
public long NoteId { get; set; }
|
||||||
|
|
||||||
|
private string _pdfUrl;
|
||||||
|
/// <summary>
|
||||||
|
/// pdf地址
|
||||||
|
/// </summary>
|
||||||
|
public string PdfUrl
|
||||||
|
{
|
||||||
|
get => DomainHelper.OssFullUrl(_pdfUrl);
|
||||||
|
set => _pdfUrl = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DotMatrixMqOutput> DotMatrixs { get; set; } = new List<DotMatrixMqOutput>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DotMatrixMqOutput
|
||||||
|
{
|
||||||
|
public long DotId { get; set; }
|
||||||
|
|
||||||
|
private string _fileAddress;
|
||||||
|
/// <summary>
|
||||||
|
/// 点阵xml地址
|
||||||
|
/// </summary>
|
||||||
|
public string FileAddress
|
||||||
|
{
|
||||||
|
get => DomainHelper.OssFullUrl(_fileAddress);
|
||||||
|
set => _fileAddress = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页
|
||||||
|
/// </summary>
|
||||||
|
public List<DotMatrixPageDto> Pages { get; set; } = new List<DotMatrixPageDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DotMatrixPageDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 页码
|
||||||
|
/// </summary>
|
||||||
|
public string PageNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连续数量
|
||||||
|
/// </summary>
|
||||||
|
public int PageNum { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定期刊输入DTO
|
||||||
|
/// </summary>
|
||||||
|
public class BindJournalInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 期刊模板Id(扫码解析的期刊定义Id)
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实例化期刊Id(扫码解析的具体期刊实例Id,可选)
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联类型: Read(已读), Favorite(收藏), Subscribe(订阅),默认 Subscribe
|
||||||
|
/// </summary>
|
||||||
|
public string Type { get; set; } = UserJournalTypeEnum.Subscribe.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定期刊输出DTO
|
||||||
|
/// </summary>
|
||||||
|
public class BindJournalOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定记录Id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户Id
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 期刊模板Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联类型
|
||||||
|
/// </summary>
|
||||||
|
public string Type { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态
|
||||||
|
/// </summary>
|
||||||
|
public string Status { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户期刊关联查询输入DTO
|
||||||
|
/// </summary>
|
||||||
|
public class UserJournalQueryInput : PageQueryModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 期刊模板Id
|
||||||
|
/// </summary>
|
||||||
|
public long? JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实例化期刊Id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联类型: Read, Favorite, Subscribe
|
||||||
|
/// </summary>
|
||||||
|
public string? Type { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
|
||||||
|
public class JournalImportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// pdf地址
|
||||||
|
/// </summary>
|
||||||
|
public string PdfUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从多少页开始
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public List<JournalCatalogAddInput> JournalCatalogs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目录导入
|
||||||
|
/// </summary>
|
||||||
|
public class JournalCatalogImportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 一级目录
|
||||||
|
/// </summary>
|
||||||
|
public string ParentName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 二级目录
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页码
|
||||||
|
/// </summary>
|
||||||
|
public int PageNum { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public long PagePageId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalCatalogAddInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 目录名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 级别
|
||||||
|
/// </summary>
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型0-目录 1-页
|
||||||
|
/// </summary>
|
||||||
|
public int Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// url
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalCatalogInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目录名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 父Id;无限级别
|
||||||
|
/// </summary>
|
||||||
|
public long ParentId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等级
|
||||||
|
/// </summary>
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指定插入位置的索引(从0开始)不传或传null时默认追加到末尾
|
||||||
|
/// </summary>
|
||||||
|
public int? Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型0-目录 1-页
|
||||||
|
/// </summary>
|
||||||
|
public int Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// url
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
using MiniExcelLibs.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public partial class JournalCatalogTreeListDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:主键id
|
||||||
|
/// </summary>
|
||||||
|
[ExcelIgnore]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:书id
|
||||||
|
/// </summary>
|
||||||
|
[ExcelIgnore]
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:一级目录名称
|
||||||
|
/// </summary>
|
||||||
|
[DisplayName("目录名称")]
|
||||||
|
[Required]
|
||||||
|
public string? ParentName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:目录名称
|
||||||
|
/// </summary>
|
||||||
|
[DisplayName("二级目录")]
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[ExcelIgnore]
|
||||||
|
public long PageId { get; set; }
|
||||||
|
|
||||||
|
[DisplayName("页码")]
|
||||||
|
[Required]
|
||||||
|
public int PageNum { get; set; }
|
||||||
|
|
||||||
|
[ExcelIgnore]
|
||||||
|
public long? ParentId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public partial class IcrJournalCatalogTreeDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:主键id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层级
|
||||||
|
/// </summary>
|
||||||
|
public int Level { get; set; }
|
||||||
|
|
||||||
|
public List<IcrJournalCatalogTreeDto> Child { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalCatalogUpdateInput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目录名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,81 +1,145 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
||||||
using QYZH.InteractiveMagazine.Models.Enum;
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QYZH.InteractiveMagazine.Models.Dto;
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绑定期刊输入DTO
|
|
||||||
/// </summary>
|
|
||||||
public class BindJournalInput
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class JournalDto
|
||||||
/// 期刊模板Id(扫码解析的期刊定义Id)
|
{
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public long JournalId { get; set; }
|
/// Desc:主键id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 实例化期刊Id(扫码解析的具体期刊实例Id,可选)
|
/// Desc:书籍名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long Id { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关联类型: Read(已读), Favorite(收藏), Subscribe(订阅),默认 Subscribe
|
/// Desc:总页数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Type { get; set; } = UserJournalTypeEnum.Subscribe.ToString();
|
public int TotalPage { get; set; }
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Desc:校验页数
|
||||||
/// 绑定期刊输出DTO
|
/// </summary>
|
||||||
/// </summary>
|
public int VerifyPage { get; set; }
|
||||||
public class BindJournalOutput
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 绑定记录Id
|
/// <summary>
|
||||||
/// </summary>
|
/// Desc:状态
|
||||||
public long Id { get; set; }
|
/// </summary>
|
||||||
|
public JournalStatusEnum Status { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// 用户Id
|
/// <summary>
|
||||||
/// </summary>
|
/// 状态
|
||||||
public long UserId { get; set; }
|
/// </summary>
|
||||||
|
public string ShowStatus => Status.GetDescription();
|
||||||
/// <summary>
|
|
||||||
/// 期刊模板Id
|
private string _pdfUrl;
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public long JournalId { get; set; }
|
/// pdf预览地址
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public string PdfUrl
|
||||||
/// 关联类型
|
{
|
||||||
/// </summary>
|
get => DomainHelper.OssFullUrl(_pdfUrl);
|
||||||
public string Type { get; set; } = string.Empty;
|
set => _pdfUrl = value;
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// 状态
|
/// <summary>
|
||||||
/// </summary>
|
/// Desc:宽度
|
||||||
public string Status { get; set; } = string.Empty;
|
/// </summary>
|
||||||
|
public float Width { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// 绑定时间
|
/// <summary>
|
||||||
/// </summary>
|
/// Desc:高度
|
||||||
public DateTime CreatedAt { get; set; }
|
/// </summary>
|
||||||
}
|
public float Height { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户期刊关联查询输入DTO
|
/// Desc:乐观锁
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserJournalQueryInput : PageQueryModel
|
public int Revision { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 期刊模板Id
|
/// Desc:审查结果(默认0 1通过 -1驳回)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long? JournalId { get; set; }
|
public int Result { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
private string _cover;
|
||||||
/// 实例化期刊Id
|
/// <summary>
|
||||||
/// </summary>
|
/// 封面
|
||||||
public long Id { get; set; }
|
/// </summary>
|
||||||
|
public string Cover
|
||||||
/// <summary>
|
{
|
||||||
/// 关联类型: Read, Favorite, Subscribe
|
get => DomainHelper.OssFullUrl(_cover);
|
||||||
/// </summary>
|
set => _cover = value;
|
||||||
public string? Type { get; set; }
|
}
|
||||||
|
|
||||||
|
private string _backCover;
|
||||||
|
/// <summary>
|
||||||
|
/// 封底
|
||||||
|
/// </summary>
|
||||||
|
public string BackCover
|
||||||
|
{
|
||||||
|
get => DomainHelper.OssFullUrl(_backCover);
|
||||||
|
set => _backCover = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string _pdfPreviewUrl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// pdf预览地址
|
||||||
|
/// </summary>
|
||||||
|
public string PdfPreviewUrl
|
||||||
|
{
|
||||||
|
get => DomainHelper.OssFullUrl(_pdfPreviewUrl);
|
||||||
|
set => _pdfPreviewUrl = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:结论json
|
||||||
|
/// </summary>
|
||||||
|
public object? Conclusion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:思路导读
|
||||||
|
/// </summary>
|
||||||
|
public string? Guide { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:家长指导
|
||||||
|
/// </summary>
|
||||||
|
public string? Tutelage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:所属机构列表
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<long, string>? Organization { get; set; }
|
||||||
|
|
||||||
|
//public List<Dictionary<long, string>> Organizations => Organization.Select(c => new Dictionary<long, string>() { { c.Key, c.Value } }).ToList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:副标题
|
||||||
|
/// </summary>
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 年级
|
||||||
|
/// </summary>
|
||||||
|
public int Grade { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:下载书籍页码点阵码PDF文件名称
|
||||||
|
/// </summary>
|
||||||
|
public string? DownloadJournalPagePdfName { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
116
QYZH.InteractiveMagazine.Models/Dto/Journal/JournalEditDto.cs
Normal file
116
QYZH.InteractiveMagazine.Models/Dto/Journal/JournalEditDto.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public partial class JournalEditDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:主键id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:书籍名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:总页数
|
||||||
|
/// </summary>
|
||||||
|
public int TotalPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:校验页数
|
||||||
|
/// </summary>
|
||||||
|
public int VerifyPage { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:状态
|
||||||
|
/// </summary>
|
||||||
|
public JournalStatusEnum Status { get; set; } = JournalStatusEnum.Created;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:pdf地址
|
||||||
|
/// </summary>
|
||||||
|
public string PdfUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:宽度
|
||||||
|
/// </summary>
|
||||||
|
public float Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:高度
|
||||||
|
/// </summary>
|
||||||
|
public float Height { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:乐观锁
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public int Revision { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:审查结果(默认0 1通过 -1驳回)
|
||||||
|
/// </summary>
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:封面
|
||||||
|
/// </summary>
|
||||||
|
public string Cover { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:封底
|
||||||
|
/// </summary>
|
||||||
|
public string BackCover { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:pdf预览地址
|
||||||
|
/// </summary>
|
||||||
|
public string PdfPreviewUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:结论json
|
||||||
|
/// </summary>
|
||||||
|
public object Conclusion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:思路导读
|
||||||
|
/// </summary>
|
||||||
|
public string Guide { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:家长指导
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public string Tutelage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:所属机构
|
||||||
|
/// </summary>
|
||||||
|
public long[] OrganizationIds { get; set; }
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Desc:所属机构
|
||||||
|
///// </summary>
|
||||||
|
//public string OrganizationName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:副标题
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 年级
|
||||||
|
/// </summary>
|
||||||
|
public int Grade { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalAddV2Input
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书目录id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalCatalogId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页码
|
||||||
|
/// </summary>
|
||||||
|
public int PageNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页图片
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalPageTaskAddInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalPageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题号
|
||||||
|
/// </summary>
|
||||||
|
public string No { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学科Id
|
||||||
|
/// </summary>
|
||||||
|
public long SubjectId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分数
|
||||||
|
/// </summary>
|
||||||
|
public float Score { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回答时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int AnswerTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联的知识点ids
|
||||||
|
/// </summary>
|
||||||
|
public string KnowledgePointIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关键解析
|
||||||
|
/// </summary>
|
||||||
|
public string KeywordAnalysis { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题内容
|
||||||
|
/// </summary>
|
||||||
|
public string Task { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalPageTaskKeywordAnalysis
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 问题Id
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单项解析
|
||||||
|
/// </summary>
|
||||||
|
public string KeywordAnalysis { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalPageTaskOutput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalPageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学科Id
|
||||||
|
/// </summary>
|
||||||
|
public long SubjectId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题号
|
||||||
|
/// </summary>
|
||||||
|
public string No { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum Type { get; set; }
|
||||||
|
|
||||||
|
public string ShowType => Type.GetDescription();
|
||||||
|
|
||||||
|
private string _task;
|
||||||
|
/// <summary>
|
||||||
|
/// 问题
|
||||||
|
/// </summary>
|
||||||
|
public string Task
|
||||||
|
{
|
||||||
|
get { return _task.AddDomain(DomainHelper.OssDomain); }
|
||||||
|
set { _task = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 选项
|
||||||
|
/// </summary>
|
||||||
|
public string Options { get; set; }
|
||||||
|
|
||||||
|
private string _answer;
|
||||||
|
/// <summary>
|
||||||
|
/// 答案
|
||||||
|
/// </summary>
|
||||||
|
public string Answer
|
||||||
|
{
|
||||||
|
get { return _answer.AddDomain(DomainHelper.OssDomain); }
|
||||||
|
set { _answer = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _analysis;
|
||||||
|
/// <summary>
|
||||||
|
/// 解析
|
||||||
|
/// </summary>
|
||||||
|
public string Analysis
|
||||||
|
{
|
||||||
|
get { return _analysis.AddDomain(DomainHelper.OssDomain); }
|
||||||
|
set { _analysis = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AnalysisUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题分数
|
||||||
|
/// </summary>
|
||||||
|
public float TaskScore { get; set; }
|
||||||
|
|
||||||
|
private string _audioUrl;
|
||||||
|
/// <summary>
|
||||||
|
/// 音频地址
|
||||||
|
/// </summary>
|
||||||
|
public string AudioUrl
|
||||||
|
{
|
||||||
|
get { return DomainHelper.OssFullUrl(_audioUrl); }
|
||||||
|
set { _audioUrl = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _videoUrl;
|
||||||
|
/// <summary>
|
||||||
|
/// 视频地址
|
||||||
|
/// </summary>
|
||||||
|
public string VideoUrl
|
||||||
|
{
|
||||||
|
get { return DomainHelper.OssFullUrl(_videoUrl); }
|
||||||
|
set { _videoUrl = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _pointsUrl;
|
||||||
|
/// <summary>
|
||||||
|
/// 点位数据地址
|
||||||
|
/// </summary>
|
||||||
|
public string PointsUrl
|
||||||
|
{
|
||||||
|
get { return DomainHelper.OssFullUrl(_pointsUrl); }
|
||||||
|
set { _pointsUrl = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _imageUrl;
|
||||||
|
/// <summary>
|
||||||
|
/// 图片地址
|
||||||
|
/// </summary>
|
||||||
|
public string ImageUrl
|
||||||
|
{
|
||||||
|
get { return DomainHelper.OssFullUrl(_imageUrl); }
|
||||||
|
set { _imageUrl = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> ImageUrls { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题分数
|
||||||
|
/// </summary>
|
||||||
|
public float Score { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回答时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int AnswerTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频开始时间
|
||||||
|
/// </summary>
|
||||||
|
public long AudioStartTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频结束时间
|
||||||
|
/// </summary>
|
||||||
|
public long AudioEndTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 知识点
|
||||||
|
/// </summary>
|
||||||
|
public string KnowledgePointIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回答项解析
|
||||||
|
/// </summary>
|
||||||
|
public string KeywordAnalysis { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 年级
|
||||||
|
/// </summary>
|
||||||
|
public int Grade { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalPageTaskUpdateInput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书Id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页id
|
||||||
|
/// </summary>
|
||||||
|
public long JournalPageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题号
|
||||||
|
/// </summary>
|
||||||
|
public string No { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题
|
||||||
|
/// </summary>
|
||||||
|
public string Task { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 选项
|
||||||
|
/// </summary>
|
||||||
|
public string Options { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 答案
|
||||||
|
/// </summary>
|
||||||
|
public string Answer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 答案图片
|
||||||
|
/// </summary>
|
||||||
|
public string AnswerUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析
|
||||||
|
/// </summary>
|
||||||
|
public string Analysis { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析图片地址
|
||||||
|
/// </summary>
|
||||||
|
public string AnalysisUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题分数
|
||||||
|
/// </summary>
|
||||||
|
public float Score { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回答时间秒
|
||||||
|
/// </summary>
|
||||||
|
public int AnswerTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图片
|
||||||
|
/// </summary>
|
||||||
|
public string ImageUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 视频
|
||||||
|
/// </summary>
|
||||||
|
public string VideoUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学科Id
|
||||||
|
/// </summary>
|
||||||
|
public long SubjectId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关联的知识点ids
|
||||||
|
/// </summary>
|
||||||
|
public string KnowledgepointIds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalPageTaskComplementInput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务id
|
||||||
|
/// </summary>
|
||||||
|
public long AssignTaskId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频地址
|
||||||
|
/// </summary>
|
||||||
|
public string AudioUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频开始时间
|
||||||
|
/// </summary>
|
||||||
|
public long AudioStartTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频结束时间
|
||||||
|
/// </summary>
|
||||||
|
public long AudioEndTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 点位数据地址
|
||||||
|
/// </summary>
|
||||||
|
public string PointsUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalPageV2Output
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public long JournalId { get; set; }
|
||||||
|
|
||||||
|
public long JournalCatalogId { get; set; }
|
||||||
|
|
||||||
|
public long JournalPageId { get; set; }
|
||||||
|
|
||||||
|
public string Layout { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页码
|
||||||
|
/// </summary>
|
||||||
|
public int PageNum { get; set; }
|
||||||
|
|
||||||
|
private string _url;
|
||||||
|
/// <summary>
|
||||||
|
/// 图片
|
||||||
|
/// </summary>
|
||||||
|
public string Url
|
||||||
|
{
|
||||||
|
get { return DomainHelper.OssFullUrl(_url); }
|
||||||
|
set { _url = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 问题
|
||||||
|
/// </summary>
|
||||||
|
public List<JournalPageTaskV2Output>? Tasks { get; set; }
|
||||||
|
|
||||||
|
public List<JournalPageOtherOutput>? Areas { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalPageTaskV2Output
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public long GroupId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题号
|
||||||
|
/// </summary>
|
||||||
|
public string No { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
//public long test => long.Parse(No.Replace("-", string.Empty));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum Type { get; set; }
|
||||||
|
|
||||||
|
public string ShowType => Type.GetDescription();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 选项
|
||||||
|
/// </summary>
|
||||||
|
public string Options { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 答案
|
||||||
|
/// </summary>
|
||||||
|
public string Answers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析
|
||||||
|
/// </summary>
|
||||||
|
public string Analysis { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否分配
|
||||||
|
/// </summary>
|
||||||
|
public bool Assign { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalPageOtherOutput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题号
|
||||||
|
/// </summary>
|
||||||
|
public string No { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum Type { get; set; }
|
||||||
|
|
||||||
|
public string ShowType => Type.GetDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalQueryDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍名称
|
||||||
|
/// </summary>
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态
|
||||||
|
/// </summary>
|
||||||
|
public JournalStatusEnum? Status { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class JournalTaskOutput
|
||||||
|
{
|
||||||
|
public long TaskId { get; set; }
|
||||||
|
public string TaskNo { get; set; }
|
||||||
|
|
||||||
|
public TaskTypeEnum TaskType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var type = new TaskBankType();
|
||||||
|
if (type.ObjectiveTypes.Contains(TaskSubType))
|
||||||
|
return TaskTypeEnum.Objective;
|
||||||
|
else if (type.SubjectiveTypes.Contains(TaskSubType))
|
||||||
|
return TaskTypeEnum.Subjective;
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException($"Unknown task sub type: {TaskSubType}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 详细类型
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum TaskSubType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Dto.Journal
|
||||||
|
{
|
||||||
|
public class PageLayoutInput
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public string Layout { get; set; }
|
||||||
|
|
||||||
|
public List<TasksImages> TasksImages { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
public class TasksImages
|
||||||
|
{
|
||||||
|
public long TaskId { get; set; }
|
||||||
|
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
29
QYZH.InteractiveMagazine.Models/Dto/MoveInput.cs
Normal file
29
QYZH.InteractiveMagazine.Models/Dto/MoveInput.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Base
|
||||||
|
{
|
||||||
|
public class MoveInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 要移动节点ID (必须)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public long SourceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目标父节点ID (0表示移动到根节点)
|
||||||
|
/// </summary>
|
||||||
|
public long TargetParentId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在目标父节点下的插入位置 (可选,从0开始,null表示追加到末尾)
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, int.MaxValue)]
|
||||||
|
public int? Position { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,73 +14,107 @@ namespace QYZH.InteractiveMagazine.Models.Entity
|
|||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:期刊号
|
/// Desc:书籍名称
|
||||||
|
/// Default:
|
||||||
|
/// Nullable:True
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:总页数
|
||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:False
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int IssueNumber {get;set;}
|
public int TotalPage { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:期刊标题
|
/// Desc:校验页数
|
||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:False
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Title {get;set;}
|
public int VerifyPage { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Desc:封面图地址
|
|
||||||
/// Default:
|
|
||||||
/// Nullable:True
|
|
||||||
/// </summary>
|
|
||||||
public string CoverImageUrl {get;set;}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:摘要
|
/// Desc:pdf地址
|
||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:True
|
/// Nullable:True
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Summary {get;set;}
|
public string PdfUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:发布时间
|
/// Desc:宽度
|
||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:True
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? PublishDate {get;set;}
|
public float Width { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:是否启用
|
/// Desc:高度
|
||||||
/// Default:b'1'
|
/// Default:
|
||||||
/// Nullable:False
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsActive {get;set;}
|
public float Height { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:排序权重
|
/// Desc:乐观锁
|
||||||
/// Default:0
|
/// Default:
|
||||||
/// Nullable:False
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SortOrder {get;set;}
|
public int Revision { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:编者寄语
|
/// Desc:审查结果(默认0 1通过 -1驳回)
|
||||||
/// Default:
|
/// Default:0
|
||||||
/// Nullable:True
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string EditorNote {get;set;}
|
public int Result { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:主题色
|
/// Desc:封面
|
||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:True
|
/// Nullable:True
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ThemeColor {get;set;}
|
public string Cover { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:封底
|
||||||
|
/// Default:
|
||||||
|
/// Nullable:True
|
||||||
|
/// </summary>
|
||||||
|
public string BackCover { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:pdf预览地址
|
||||||
|
/// Default:
|
||||||
|
/// Nullable:True
|
||||||
|
/// </summary>
|
||||||
|
public string PdfPreviewUrl { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:下载书籍页码点阵码PDF文件名称
|
||||||
|
/// Default:
|
||||||
|
/// Nullable:True
|
||||||
|
/// </summary>
|
||||||
|
public string DownloadJournalPagePdfName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:书籍描述/简介
|
||||||
|
/// Default:
|
||||||
|
/// Nullable:True
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desc:期刊类型: Normal, Special
|
||||||
|
/// Default:Normal
|
||||||
|
/// Nullable:False
|
||||||
|
/// </summary>
|
||||||
|
public JournalTypeEnum Type {get;set;}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Desc:期刊类型: Normal, Special
|
|
||||||
/// Default:Normal
|
|
||||||
/// Nullable:False
|
|
||||||
/// </summary>
|
|
||||||
public JournalTypeEnum Type {get;set;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ namespace QYZH.InteractiveMagazine.Models.Entity
|
|||||||
/// Default:
|
/// Default:
|
||||||
/// Nullable:False
|
/// Nullable:False
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JournalPageTaskTypeEnum Type {get;set;}
|
public TaskBankTypeEnum Type {get;set;}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Desc:任务
|
/// Desc:任务
|
||||||
|
|||||||
@ -26,5 +26,5 @@ public enum JournalPageTaskTypeEnum
|
|||||||
/// 问答题
|
/// 问答题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("问答题")]
|
[Description("问答题")]
|
||||||
QuestionAnswer = 4
|
TaskAnswer = 4
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,20 +8,58 @@ namespace QYZH.InteractiveMagazine.Models.Enum;
|
|||||||
public enum JournalStatusEnum
|
public enum JournalStatusEnum
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 草稿
|
/// 缺失目录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("草稿")]
|
[Description("缺失目录")]
|
||||||
Draft = 0,
|
MissCatalog = -1,
|
||||||
|
/// <summary>
|
||||||
|
/// 已创建
|
||||||
|
/// </summary>
|
||||||
|
[Description("已创建")]
|
||||||
|
Created = 0,
|
||||||
|
|
||||||
|
[Description("编辑")]
|
||||||
|
Editor = 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已发布
|
/// 已校验
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("已发布")]
|
[Description("已校验")]
|
||||||
Published = 1,
|
Verify = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 铺码中
|
||||||
|
/// </summary>
|
||||||
|
[Description("铺码中")]
|
||||||
|
Codeing = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 铺码成功
|
||||||
|
/// </summary>
|
||||||
|
[Description("铺码成功")]
|
||||||
|
CodeSuccess = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 铺码失败
|
||||||
|
/// </summary>
|
||||||
|
[Description("铺码失败")]
|
||||||
|
CodeFail = -5,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已归档
|
/// 已归档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("已归档")]
|
[Description("已归档")]
|
||||||
Archived = 2
|
Archive = 9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已废弃
|
||||||
|
/// </summary>
|
||||||
|
[Description("已废弃")]
|
||||||
|
Abandoned = -9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已发布
|
||||||
|
/// </summary>
|
||||||
|
[Description("已发布")]
|
||||||
|
Published = 999
|
||||||
}
|
}
|
||||||
|
|||||||
77
QYZH.InteractiveMagazine.Models/Enum/TaskBankTypeEnum.cs
Normal file
77
QYZH.InteractiveMagazine.Models/Enum/TaskBankTypeEnum.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Enum
|
||||||
|
{
|
||||||
|
public enum TaskBankTypeEnum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无效题目
|
||||||
|
/// </summary>
|
||||||
|
[Description("无效")]
|
||||||
|
Default = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 单选题
|
||||||
|
/// </summary>
|
||||||
|
[Description("单选题")]
|
||||||
|
Single = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 多选题
|
||||||
|
/// </summary>
|
||||||
|
[Description("多选题")]
|
||||||
|
Multiple = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断题
|
||||||
|
/// </summary>
|
||||||
|
[Description("判断题")]
|
||||||
|
Whether = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 填空题
|
||||||
|
/// </summary>
|
||||||
|
[Description("填空题")]
|
||||||
|
FillIn = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用题
|
||||||
|
/// </summary>
|
||||||
|
[Description("应用题")]
|
||||||
|
Problem = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 简答题
|
||||||
|
/// </summary>
|
||||||
|
[Description("简答题")]
|
||||||
|
ShortAnswer = 6,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskBankType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 客观题
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum[] ObjectiveTypes => new[] { TaskBankTypeEnum.Single, TaskBankTypeEnum.Multiple, TaskBankTypeEnum.Whether };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取客观题类型的整数值
|
||||||
|
/// </summary>
|
||||||
|
public List<int> ObjectiveTypeValues => ObjectiveTypes.Select(t => (int)t).ToList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主观题
|
||||||
|
/// </summary>
|
||||||
|
public TaskBankTypeEnum[] SubjectiveTypes => new[] { TaskBankTypeEnum.FillIn, TaskBankTypeEnum.Problem, TaskBankTypeEnum.ShortAnswer };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取主观题类型的整数值
|
||||||
|
/// </summary>
|
||||||
|
public List<int> SubjectiveTypeValues => SubjectiveTypes.Select(t => (int)t).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
QYZH.InteractiveMagazine.Models/Enum/TaskTypeEnum.cs
Normal file
24
QYZH.InteractiveMagazine.Models/Enum/TaskTypeEnum.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Models.Enum
|
||||||
|
{
|
||||||
|
public enum TaskTypeEnum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 客观题
|
||||||
|
/// </summary>
|
||||||
|
[Description("客观题")]
|
||||||
|
Objective = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主观题
|
||||||
|
/// </summary>
|
||||||
|
[Description("主观题")]
|
||||||
|
Subjective = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,9 +9,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
<PackageReference Include="MiniExcel" Version="1.41.4" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\QYZH.InteractiveMagazine.Common\QYZH.InteractiveMagazine.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -68,7 +68,7 @@ public class CheckInService(
|
|||||||
.Where(p => p.UserId == userId && !p.IsDeleted)
|
.Where(p => p.UserId == userId && !p.IsDeleted)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
// 6. 事务执行签到相关写操作
|
// 6. 事务执行签到相关写操作(含宠物喂养,统一事务)
|
||||||
var result = new CheckInOutput();
|
var result = new CheckInOutput();
|
||||||
|
|
||||||
await checkInRecordRepository.UseTranAsync(async () =>
|
await checkInRecordRepository.UseTranAsync(async () =>
|
||||||
@ -107,6 +107,17 @@ public class CheckInService(
|
|||||||
Description = $"签到奖励(连续{consecutiveDays}天)"
|
Description = $"签到奖励(连续{consecutiveDays}天)"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 6d. 如果有活跃宠物,喂养宠物(同一事务内)
|
||||||
|
FeedPetOutput? feedResult = null;
|
||||||
|
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||||
|
{
|
||||||
|
feedResult = await petService.FeedPetInTranAsync(userId, new FeedPetInput
|
||||||
|
{
|
||||||
|
PetId = pet.Id,
|
||||||
|
GrowthPoints = growthReward
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
result.RecordId = (long)recordId;
|
result.RecordId = (long)recordId;
|
||||||
result.CheckInDate = today;
|
result.CheckInDate = today;
|
||||||
@ -116,41 +127,17 @@ public class CheckInService(
|
|||||||
result.PointsBalance = pointsResult.NewBalance;
|
result.PointsBalance = pointsResult.NewBalance;
|
||||||
result.GrowthPointsBalance = newGrowthBalance;
|
result.GrowthPointsBalance = newGrowthBalance;
|
||||||
result.HasPet = pet != null;
|
result.HasPet = pet != null;
|
||||||
});
|
if (feedResult != null)
|
||||||
|
|
||||||
// 7. 如果用户有活跃宠物,调用 PetService 喂养(含进化检查),独立事务
|
|
||||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var feedResult = await petService.FeedPetAsync(userId, new FeedPetInput
|
|
||||||
{
|
|
||||||
PetId = pet.Id,
|
|
||||||
GrowthPoints = growthReward
|
|
||||||
});
|
|
||||||
|
|
||||||
result.HasEvolved = feedResult.HasEvolved;
|
result.HasEvolved = feedResult.HasEvolved;
|
||||||
result.EvolvedStageName = feedResult.EvolvedStageName;
|
result.EvolvedStageName = feedResult.EvolvedStageName;
|
||||||
|
|
||||||
logger.LogInformation("签到成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
|
||||||
pet.Id, feedResult.HasEvolved);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
});
|
||||||
{
|
|
||||||
logger.LogWarning(ex, "签到后喂养宠物失败,PetId: {PetId},将创建补偿任务", pet.Id);
|
|
||||||
|
|
||||||
await compensationTaskService.CreateTaskAsync(new CreateCompensationTaskInput
|
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||||
{
|
{
|
||||||
TaskType = CompensationTaskTypeEnum.PetFeeding,
|
logger.LogInformation("签到成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
||||||
BusinessSource = "CheckIn",
|
pet.Id, result.HasEvolved);
|
||||||
BusinessId = result.RecordId.ToString(),
|
|
||||||
UserId = userId,
|
|
||||||
Payload = new { PetId = pet.Id, GrowthPoints = growthReward },
|
|
||||||
ErrorMessage = ex.Message,
|
|
||||||
ErrorSource = "CheckInService.CheckInAsync → PetService.FeedPetAsync",
|
|
||||||
MaxRetries = 3
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("用户签到成功,UserId: {UserId}, 连续{Days}天, 积分+{Points}, 成长值+{Growth}",
|
logger.LogInformation("用户签到成功,UserId: {UserId}, 连续{Days}天, 积分+{Points}, 成长值+{Growth}",
|
||||||
@ -339,6 +326,17 @@ public class CheckInService(
|
|||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有活跃宠物,喂养宠物(同一事务内)
|
||||||
|
FeedPetOutput? feedResult = null;
|
||||||
|
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||||
|
{
|
||||||
|
feedResult = await petService.FeedPetInTranAsync(userId, new Models.Dto.Pet.FeedPetInput
|
||||||
|
{
|
||||||
|
PetId = pet.Id,
|
||||||
|
GrowthPoints = growthReward
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
result.RecordId = (long)recordId;
|
result.RecordId = (long)recordId;
|
||||||
result.CheckInDate = targetDate;
|
result.CheckInDate = targetDate;
|
||||||
result.ConsecutiveDays = 0;
|
result.ConsecutiveDays = 0;
|
||||||
@ -347,27 +345,17 @@ public class CheckInService(
|
|||||||
result.PointsBalance = pointsResult.NewBalance;
|
result.PointsBalance = pointsResult.NewBalance;
|
||||||
result.GrowthPointsBalance = newGrowthBalance;
|
result.GrowthPointsBalance = newGrowthBalance;
|
||||||
result.HasPet = pet != null;
|
result.HasPet = pet != null;
|
||||||
});
|
if (feedResult != null)
|
||||||
|
|
||||||
// 如果有活跃宠物,喂养成长值
|
|
||||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var feedResult = await petService.FeedPetAsync(userId, new Models.Dto.Pet.FeedPetInput
|
|
||||||
{
|
|
||||||
PetId = pet.Id,
|
|
||||||
GrowthPoints = growthReward
|
|
||||||
});
|
|
||||||
|
|
||||||
result.HasEvolved = feedResult.HasEvolved;
|
result.HasEvolved = feedResult.HasEvolved;
|
||||||
result.EvolvedStageName = feedResult.EvolvedStageName;
|
result.EvolvedStageName = feedResult.EvolvedStageName;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
});
|
||||||
{
|
|
||||||
logger.LogWarning(ex, "补签后喂养宠物失败,PetId: {PetId}", pet.Id);
|
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||||
// 补偿机制:如需可在此创建补偿任务
|
{
|
||||||
}
|
logger.LogInformation("补签成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
||||||
|
pet.Id, result.HasEvolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("补签成功,UserId: {UserId}, Date: {Date}, 积分+{Points}, 成长值+{Growth}",
|
logger.LogInformation("补签成功,UserId: {UserId}, Date: {Date}, 积分+{Points}, 成长值+{Growth}",
|
||||||
|
|||||||
526
QYZH.InteractiveMagazine.Service/JournalCatalogService.cs
Normal file
526
QYZH.InteractiveMagazine.Service/JournalCatalogService.cs
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
|
||||||
|
using Mapster;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||||
|
using QYZH.InteractiveMagazine.IService;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Base;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Repository;
|
||||||
|
using SqlSugar;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Service;
|
||||||
|
|
||||||
|
public class JournalCatalogService : BaseRepository<JournalCatalog>, IJournalCatalogService
|
||||||
|
{
|
||||||
|
private readonly BaseRepository<JournalPage> _JournalPageRepository;
|
||||||
|
private readonly BaseRepository<JournalPageTask> _JournalPageTaskRepository;
|
||||||
|
private readonly OssService _ossService;
|
||||||
|
|
||||||
|
public JournalCatalogService(BaseRepository<JournalPage> JournalPageRepository, BaseRepository<JournalPageTask> JournalPageTaskRepository, OssService ossService)
|
||||||
|
{
|
||||||
|
//_JournalRepository = JournalRepository;
|
||||||
|
_JournalPageRepository = JournalPageRepository;
|
||||||
|
_ossService = ossService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<JournalCatalog>> DetailAsync(long JournalId)
|
||||||
|
{
|
||||||
|
return await base.Queryable().Where(w => w.ParentId == 0 && w.JournalId == JournalId).OrderBy(o => o.Sort).ToChildListAsync(it => it.ParentId, 0) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> ImportAsync(JournalImportDto input)
|
||||||
|
{
|
||||||
|
var Journals = await Queryable<Journal>().Where(w => w.Id == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journals.IsNull(), "未找到书本");
|
||||||
|
|
||||||
|
var pageNum = await _JournalPageRepository.Queryable().Where(w => w.JournalId == input.JournalId).MaxAsync(m => m.PageNum);
|
||||||
|
BusinessException.ThrowIf(pageNum != 0, "已经存在书页");
|
||||||
|
|
||||||
|
|
||||||
|
var JournalCatalog = new JournalCatalog()
|
||||||
|
{
|
||||||
|
Id = YitIdHelper.NextId(),
|
||||||
|
JournalId = input.JournalId,
|
||||||
|
Name = "目录",
|
||||||
|
ParentId = 0,
|
||||||
|
Level = 0,
|
||||||
|
Sort = 1,
|
||||||
|
Type = JournalCatalogTypeEnum.Page,
|
||||||
|
};
|
||||||
|
|
||||||
|
int sort = 1;
|
||||||
|
var result = input.JournalCatalogs.Where(w => w.Type == 1).Select(s =>
|
||||||
|
{
|
||||||
|
var JournalPage = new JournalPage
|
||||||
|
{
|
||||||
|
Id = YitIdHelper.NextId(),
|
||||||
|
JournalId = input.JournalId,
|
||||||
|
JournalCatalogId = JournalCatalog.Id,
|
||||||
|
Sort = sort,
|
||||||
|
PageNum = sort < input.Index ? 0 : sort - (input.Index - 1)
|
||||||
|
};
|
||||||
|
var key = $"Journal/{input.JournalId}/{JournalPage.Id}/page.{s.Url.ToExtension()}";
|
||||||
|
_ossService.CopyObject(s.Url.RemoveDomain(), key);
|
||||||
|
|
||||||
|
JournalPage.Url = key;
|
||||||
|
//dotMatrixPage.PreviewUrl = key;
|
||||||
|
|
||||||
|
sort = sort + 1;
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
JournalPage = JournalPage
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
|
||||||
|
var data = await base.InsertAsync(JournalCatalog);
|
||||||
|
|
||||||
|
var JournalPages = result.Select(x => x.JournalPage).ToList();
|
||||||
|
await _JournalPageRepository.InsertRangeAsync(JournalPages);
|
||||||
|
|
||||||
|
var key = $"Journal/{input.JournalId}/Journal.pdf";
|
||||||
|
_ossService.CopyObject(input.PdfUrl.RemoveDomain(), key);
|
||||||
|
|
||||||
|
await Updateable<Journal>().SetColumns(s => s.PdfUrl, key)
|
||||||
|
.SetColumns(s => s.Status, JournalStatusEnum.Created)
|
||||||
|
.SetColumns(s => s.TotalPage, JournalPages.Count).Where(w => w.Id == input.JournalId)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取书分类及页码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<List<JournalCatalogTreeListDto>> GetJournalCatalogListAsync(long JournalId)
|
||||||
|
{
|
||||||
|
var Journal = await Queryable<Journal>().Where(w => w.Id == JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||||
|
|
||||||
|
var pageNumList = await _JournalPageRepository.Queryable()
|
||||||
|
.InnerJoin<JournalCatalog>((a, b) => a.JournalCatalogId == b.Id)
|
||||||
|
.Where((a, b) => a.JournalId == JournalId)
|
||||||
|
.OrderBy((a, b) => a.PageNum)
|
||||||
|
.Select((a, b) => new JournalCatalogTreeListDto
|
||||||
|
{
|
||||||
|
JournalId = a.JournalId,
|
||||||
|
Id = b.Id,
|
||||||
|
Name = b.Name,
|
||||||
|
PageNum = a.PageNum,
|
||||||
|
ParentName = SqlFunc.Subqueryable<JournalCatalog>().Where(c => c.Id == b.ParentId).Select(c => c.Name),
|
||||||
|
ParentId = b.ParentId,
|
||||||
|
PageId = a.Id,
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return pageNumList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取书分类及页码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<List<IcrJournalCatalogTreeDto>> GetJournalCataloTreeAsync(long JournalId)
|
||||||
|
{
|
||||||
|
var Journal = await Queryable<Journal>().Where(w => w.Id == JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||||
|
|
||||||
|
// 先查询所有目录
|
||||||
|
var allCatalogs = await base.Queryable()
|
||||||
|
.Where(w => w.JournalId == JournalId)
|
||||||
|
.OrderBy(o => o.Sort)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!allCatalogs.Any())
|
||||||
|
return new List<IcrJournalCatalogTreeDto>();
|
||||||
|
|
||||||
|
// 再查询有页面对应的目录及页面信息
|
||||||
|
var pageNumList = await _JournalPageRepository.Queryable()
|
||||||
|
.InnerJoin<JournalCatalog>((a, b) => a.JournalCatalogId == b.Id)
|
||||||
|
.Where((a, b) => a.JournalId == JournalId)
|
||||||
|
.OrderBy((a, b) => a.PageNum)
|
||||||
|
.Select((a, b) => new JournalCatalogTreeListDto
|
||||||
|
{
|
||||||
|
JournalId = a.JournalId,
|
||||||
|
Id = b.Id,
|
||||||
|
Name = b.Name,
|
||||||
|
PageNum = a.PageNum,
|
||||||
|
ParentName = SqlFunc.Subqueryable<JournalCatalog>().Where(c => c.Id == b.ParentId).Select(c => c.Name),
|
||||||
|
ParentId = b.ParentId,
|
||||||
|
PageId = a.Id,
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var tree = BuildCatalogTree(allCatalogs, pageNumList);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建目录树形结构(支持任意层级)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="allCatalogs">所有目录</param>
|
||||||
|
/// <param name="pageNumList">有页面对应的目录信息</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private List<IcrJournalCatalogTreeDto> BuildCatalogTree(List<JournalCatalog> allCatalogs, List<JournalCatalogTreeListDto> pageNumList)
|
||||||
|
{
|
||||||
|
// 构建有页面对应的目录到页面列表的映射
|
||||||
|
var catalogPagesMap = pageNumList
|
||||||
|
.GroupBy(x => x.Id)
|
||||||
|
.ToDictionary(
|
||||||
|
g => g.Key,
|
||||||
|
g => g.Select(p => new IcrJournalCatalogTreeDto
|
||||||
|
{
|
||||||
|
Id = p.PageId,
|
||||||
|
Name = $"第{p.PageNum}页",
|
||||||
|
Level = 0,
|
||||||
|
Child = new List<IcrJournalCatalogTreeDto>()
|
||||||
|
}).ToList()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 构建所有目录节点字典
|
||||||
|
var catalogDict = new Dictionary<long, IcrJournalCatalogTreeDto>();
|
||||||
|
var rootCatalogIds = new HashSet<long>();
|
||||||
|
|
||||||
|
foreach (var catalog in allCatalogs)
|
||||||
|
{
|
||||||
|
var catalogId = catalog.Id;
|
||||||
|
var parentId = catalog.ParentId;
|
||||||
|
|
||||||
|
if (catalogDict.ContainsKey(catalogId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var node = new IcrJournalCatalogTreeDto
|
||||||
|
{
|
||||||
|
Id = catalogId,
|
||||||
|
Name = catalog.Name,
|
||||||
|
Level = 0,
|
||||||
|
Child = catalogPagesMap.GetValueOrDefault(catalogId, new List<IcrJournalCatalogTreeDto>())
|
||||||
|
};
|
||||||
|
|
||||||
|
catalogDict[catalogId] = node;
|
||||||
|
|
||||||
|
if (parentId == 0)
|
||||||
|
{
|
||||||
|
rootCatalogIds.Add(catalogId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建树形结构:将子节点挂载到父节点
|
||||||
|
foreach (var catalog in allCatalogs)
|
||||||
|
{
|
||||||
|
var catalogId = catalog.Id;
|
||||||
|
var parentId = catalog.ParentId;
|
||||||
|
|
||||||
|
if (parentId > 0 && catalogDict.ContainsKey(parentId) && catalogDict.ContainsKey(catalogId))
|
||||||
|
{
|
||||||
|
var parentNode = catalogDict[parentId];
|
||||||
|
var childNode = catalogDict[catalogId];
|
||||||
|
|
||||||
|
if (!parentNode.Child.Any(c => c.Id == childNode.Id))
|
||||||
|
{
|
||||||
|
parentNode.Child.Add(childNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算每个节点的层级并返回根节点列表
|
||||||
|
var result = rootCatalogIds
|
||||||
|
.Where(id => catalogDict.ContainsKey(id))
|
||||||
|
.Select(id =>
|
||||||
|
{
|
||||||
|
var node = catalogDict[id];
|
||||||
|
node.Level = 1;
|
||||||
|
CalculateChildLevels(node.Child, 2);
|
||||||
|
return node;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 递归计算子节点层级
|
||||||
|
/// </summary>
|
||||||
|
private void CalculateChildLevels(List<IcrJournalCatalogTreeDto> children, int level)
|
||||||
|
{
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
child.Level = level;
|
||||||
|
if (child.Child != null && child.Child.Count > 0)
|
||||||
|
{
|
||||||
|
CalculateChildLevels(child.Child, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入书籍目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <param name="dtos"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ImportCatalogAsync(long JournalId, List<JournalCatalogTreeListDto> dtos)
|
||||||
|
{
|
||||||
|
dtos = dtos.Where(c => !string.IsNullOrWhiteSpace(c.Name)).ToList();
|
||||||
|
BusinessException.ThrowIf(dtos.Select(c => c.PageNum).Distinct().Count() != dtos.Count(), "存在重复的页码");
|
||||||
|
|
||||||
|
var Journal = await Queryable<Journal>().Where(w => w.Id == JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||||
|
var pageNums = dtos.Select(c => c.PageNum).ToList();
|
||||||
|
var pageNumNotExists = await _JournalPageRepository.Queryable().AnyAsync(w => w.JournalId == JournalId && !pageNums.Contains(w.PageNum));
|
||||||
|
BusinessException.ThrowIf(pageNumNotExists, "不存在的书页");
|
||||||
|
|
||||||
|
var firstCatelogGroup = dtos.Select(c => c.ParentName).Distinct().Select(c => new JournalCatalog
|
||||||
|
{
|
||||||
|
JournalId = JournalId,
|
||||||
|
Id = YitIdHelper.NextId(),
|
||||||
|
Level = 1,
|
||||||
|
Name = c,
|
||||||
|
}).ToList();
|
||||||
|
var subCatelogGroup = dtos.Select(c => new { c.Name, c.ParentName }).Distinct().Select(c => new
|
||||||
|
{
|
||||||
|
JournalId = JournalId,
|
||||||
|
Id = YitIdHelper.NextId(),
|
||||||
|
Level = 2,
|
||||||
|
Name = c.Name,
|
||||||
|
ParentId = firstCatelogGroup.FirstOrDefault(m => m.Name == c.ParentName)?.Id,
|
||||||
|
ParentName = c.ParentName
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var result = await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
await base.DeleteAsync(c => c.JournalId == JournalId);
|
||||||
|
await base.InsertRangeAsync(firstCatelogGroup);
|
||||||
|
await base.InsertRangeAsync(subCatelogGroup.Adapt<List<JournalCatalog>>());
|
||||||
|
var JournalPages = await Queryable<JournalPage>().Where(c => c.JournalId == JournalId).ToListAsync();
|
||||||
|
JournalPages.ForEach(c =>
|
||||||
|
{
|
||||||
|
var cate = dtos.FirstOrDefault(m => m.PageNum == c.PageNum);
|
||||||
|
c.JournalCatalogId = subCatelogGroup?.FirstOrDefault(m => m.Name == cate?.Name && m.ParentName == cate?.ParentName)?.Id ?? 0;
|
||||||
|
});
|
||||||
|
await base.Context.Updateable<JournalPage>(JournalPages).UpdateColumns(c => c.JournalCatalogId).ExecuteCommandAsync();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> InsertAsync(JournalCatalogInput input)
|
||||||
|
{
|
||||||
|
var map = input.Adapt<JournalCatalog>();
|
||||||
|
map.Id = YitIdHelper.NextId();
|
||||||
|
await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
// 获取同级所有节点
|
||||||
|
var siblings = await base.Queryable().Where(w => w.JournalId == input.JournalId).Where(w => w.ParentId == input.ParentId).OrderBy(o => o.Sort).ToListAsync();
|
||||||
|
|
||||||
|
if (input.Position.HasValue && input.Position > 0 && input.Position <= siblings.Count)
|
||||||
|
{
|
||||||
|
// 调换位置,前面不变,后续加1即可
|
||||||
|
int validPosition = Math.Min(input.Position.Value, siblings.Count);
|
||||||
|
map.Sort = validPosition;
|
||||||
|
|
||||||
|
// 调整后续节点的SortOrder (+1)
|
||||||
|
await base.Updateable()
|
||||||
|
.SetColumns(x => x.Sort == x.Sort + 1)
|
||||||
|
.Where(x => x.ParentId == input.ParentId && x.Sort >= validPosition)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 默认追加到末尾
|
||||||
|
map.Sort = siblings.Count > 0 ? siblings.Max(x => x.Sort) + 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var data = await base.InsertAsync(map);
|
||||||
|
|
||||||
|
if (input.Type == 1)
|
||||||
|
{
|
||||||
|
var Journal = await Queryable<Journal>().Where(w => w.Id == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||||
|
|
||||||
|
//var dotMatrixPage = new DotMatrixPage()
|
||||||
|
//{
|
||||||
|
// Id = YitIdHelper.NextId(),
|
||||||
|
// DotMatrixNoteJournalId = input.JournalId,
|
||||||
|
// WidthMilliMeter = Journal.Width,
|
||||||
|
// HightMilliMeter = Journal.Height,
|
||||||
|
//};
|
||||||
|
var JournalPage = new JournalPage
|
||||||
|
{
|
||||||
|
JournalId = input.JournalId,
|
||||||
|
JournalCatalogId = map.Id,
|
||||||
|
//DotMatrixPageId = dotMatrixPage.Id,
|
||||||
|
PageNum = map.Sort,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map.Id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateAsync(JournalCatalogUpdateInput input)
|
||||||
|
{
|
||||||
|
var result = await base.Updateable(input.Adapt<JournalCatalog>()).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteAsync(long id)
|
||||||
|
{
|
||||||
|
var catas = await base.Queryable().ToChildListAsync(it => it.ParentId, id);
|
||||||
|
var ids = catas.Select(s => s.Id).ToList();
|
||||||
|
|
||||||
|
await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
// 删除目录
|
||||||
|
var cata = await base.Deleteable().Where(d => ids.Contains(d.Id)).ExecuteCommandAsync() > 0;
|
||||||
|
BusinessException.ThrowIf(!cata, $"删除目录失败");
|
||||||
|
// 删除所有页/问题
|
||||||
|
var pages = await _JournalPageRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId));
|
||||||
|
BusinessException.ThrowIf(pages.IsNull(), $"删除数据失败");
|
||||||
|
|
||||||
|
|
||||||
|
//var x = await _JournalPageRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId));
|
||||||
|
//var y = await _JournalPageTaskRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId));
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> MoveAsync(MoveInput input)
|
||||||
|
{
|
||||||
|
// 1. 获取源节点并校验
|
||||||
|
var sourceNode = await base.Queryable().Where(x => x.Id == input.SourceId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(sourceNode.IsNull(), $"要移动的节点{input.SourceId}不存在");
|
||||||
|
|
||||||
|
|
||||||
|
// 2. 校验目标父节点(如果指定)
|
||||||
|
if (input.TargetParentId > 0)
|
||||||
|
{
|
||||||
|
var targetParentExists = await base.Queryable().Where(x => x.Id == input.TargetParentId).AnyAsync();
|
||||||
|
BusinessException.ThrowIf(!targetParentExists, $"目标父节点{input.TargetParentId}不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
//var tree = await base.Queryable().Where(x => x.Id == input.SourceId).AsTreeCte().OrderBy(x => x.Level).ToTreeListAsync();
|
||||||
|
|
||||||
|
if (input.Position.HasValue)
|
||||||
|
{
|
||||||
|
await base.Updateable()
|
||||||
|
.SetColumns(x => x.Sort == x.Sort + 1)
|
||||||
|
.Where(x => x.ParentId == input.TargetParentId && x.Sort >= input.Position.Value)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新源节点的父节点
|
||||||
|
var updateCount = await base.Updateable()
|
||||||
|
.SetColumns(x => x.ParentId, input.TargetParentId)
|
||||||
|
.SetColumns(x => x.Sort, input.Position ?? 0)
|
||||||
|
.Where(x => x.Id == input.SourceId)
|
||||||
|
.ExecuteCommandAsync() > 0;
|
||||||
|
|
||||||
|
BusinessException.ThrowIf(!updateCount, $"节点移动失败");
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public async Task<bool> CopyAsync(CopyInput input)
|
||||||
|
//{
|
||||||
|
|
||||||
|
// // 1. 获取源节点并校验
|
||||||
|
// var sourceNode = await base.Queryable().Where(x => x.Id == input.SourceId).FirstAsync();
|
||||||
|
// BusinessException.ThrowIf(sourceNode.IsNull(), $"要移动的节点{input.SourceId}不存在");
|
||||||
|
|
||||||
|
|
||||||
|
// // 2. 校验目标父节点(如果指定)
|
||||||
|
// if (input.TargetParentId > 0)
|
||||||
|
// {
|
||||||
|
// var targetParentExists = await base.Queryable().Where(x => x.Id == input.TargetParentId).AnyAsync();
|
||||||
|
// BusinessException.ThrowIf(!targetParentExists, $"目标父节点{input.TargetParentId}不存在");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// using var uow = _unitOfWorkManager.Begin();
|
||||||
|
// var tree = await base.Queryable().IncludeMany(c => c.Pages, then => then.IncludeMany(x => x.PageTasks))
|
||||||
|
// .Where(x => x.Id == input.SourceId)
|
||||||
|
// .AsTreeCte()
|
||||||
|
// .OrderBy(x => x.Level)
|
||||||
|
// .ToTreeListAsync();
|
||||||
|
|
||||||
|
// if (input.Position.HasValue)
|
||||||
|
// {
|
||||||
|
// await base.Updateable()
|
||||||
|
// .Set(x => x.Sort + 1)
|
||||||
|
// .Where(x => x.ParentId == input.TargetParentId && x.Sort >= input.Position.Value)
|
||||||
|
// .ExecuteCommandAsync();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var copy = await DeepCopyAsync(tree.First(), input.TargetParentId);
|
||||||
|
// BusinessException.ThrowIf(copy.IsNull(), $"复制节点失败");
|
||||||
|
|
||||||
|
// var data = await base.InsertAsync(copy);
|
||||||
|
// BusinessException.ThrowIf(data.IsNull(), $"复制节点失败");
|
||||||
|
|
||||||
|
// var pages = copy.Where(s => s.Pages.NotNull()).Queryable()Many(s => s.Pages);
|
||||||
|
// var tempPage = await _JournalPageRepository.InsertAsync(pages);
|
||||||
|
// BusinessException.ThrowIf(tempPage.IsNull(), $"复制页面数据失败");
|
||||||
|
|
||||||
|
// //var task = pages.Where(w => w.PageTasks.NotNull()).Queryable()Many(s => s.PageTasks);
|
||||||
|
// //var tempPageTask = await _JournalPageTaskRepository.InsertAsync(task);
|
||||||
|
// //BusinessException.ThrowIf(tempPageTask.IsNull(), $"复制页面问题数据失败");
|
||||||
|
|
||||||
|
// uow.Commit();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
//private async Task<List<IcrJournalCatalog>> DeepCopyAsync(IcrJournalCatalog source, long targetParentId, List<IcrJournalCatalog>? list = null)
|
||||||
|
//{
|
||||||
|
// list ??= new List<IcrJournalCatalog>();
|
||||||
|
|
||||||
|
// var copy = new IcrJournalCatalog
|
||||||
|
// {
|
||||||
|
// Id = YitIdHelper.NextId(),// 设置新ID
|
||||||
|
// Name = !list.Any() ? $"{source.Name} 副本" : source.Name,
|
||||||
|
// ParentId = targetParentId,
|
||||||
|
// Level = source.Level,
|
||||||
|
// Sort = source.Sort,
|
||||||
|
// };
|
||||||
|
// copy.Pages?.ForEach(page =>
|
||||||
|
// {
|
||||||
|
// page.Id = YitIdHelper.NextId(); // 设置新页面ID
|
||||||
|
// page.JournalCatalogId = copy.Id; // 设置为新目录的ID
|
||||||
|
// page.PageTasks?.ForEach(task =>
|
||||||
|
// {
|
||||||
|
// task.Id = YitIdHelper.NextId(); // 设置新问题ID
|
||||||
|
// task.JournalPageId = page.Id; // 设置为新页面的ID
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// list.Add(copy);
|
||||||
|
|
||||||
|
// // 递归复制子节点
|
||||||
|
// if (source.Childs != null && source.Childs.Any())
|
||||||
|
// {
|
||||||
|
// foreach (var child in source.Childs)
|
||||||
|
// {
|
||||||
|
// await DeepCopyAsync(child, copy.Id, list);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return list;
|
||||||
|
//}
|
||||||
|
}
|
||||||
211
QYZH.InteractiveMagazine.Service/JournalPageService.cs
Normal file
211
QYZH.InteractiveMagazine.Service/JournalPageService.cs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||||
|
using QYZH.InteractiveMagazine.IService;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Repository;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Service;
|
||||||
|
|
||||||
|
public class JournalPageService(BaseRepository<Journal> JournalRepository,
|
||||||
|
OssService ossService,
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IRabbitMQService rabbitMqService,
|
||||||
|
BaseRepository<JournalPageTask> JournalPageTaskRepository) : BaseRepository<JournalPage>, IJournalPageService
|
||||||
|
{
|
||||||
|
|
||||||
|
public async Task<long> InsertAsync(JournalAddV2Input input)
|
||||||
|
{
|
||||||
|
|
||||||
|
var Journal = await JournalRepository.Queryable().Where(w => w.Id == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||||
|
|
||||||
|
BusinessException.ThrowIf(Journal?.Status == (int)JournalStatusEnum.Archive, "书籍已归档不能修改");
|
||||||
|
|
||||||
|
var pages = new List<JournalPage>();
|
||||||
|
//var dotMatrixpages = new List<DotMatrixPage>();
|
||||||
|
|
||||||
|
//var dotMatrixpage = new DotMatrixPage()
|
||||||
|
//{
|
||||||
|
// Id = YitIdHelper.NextId(),
|
||||||
|
// DotMatrixNoteJournalId = input.JournalId,
|
||||||
|
// WidthMilliMeter = Journal.Width,
|
||||||
|
// HightMilliMeter = Journal.Height,
|
||||||
|
//};
|
||||||
|
|
||||||
|
var pageTemp = new JournalPage()
|
||||||
|
{
|
||||||
|
JournalId = input.JournalId,
|
||||||
|
JournalCatalogId = input.JournalCatalogId,
|
||||||
|
//DotMatrixPageId = dotMatrixpage.Id,
|
||||||
|
Url = input.Url,
|
||||||
|
PageNum = input.PageNum,
|
||||||
|
};
|
||||||
|
|
||||||
|
//dotMatrixpages.Add(dotMatrixpage);
|
||||||
|
pages.Add(pageTemp);
|
||||||
|
|
||||||
|
await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
//using var uow = Context.Ado.BeginTran();
|
||||||
|
var pageData = await base.InsertRangeAsync(pages);
|
||||||
|
BusinessException.ThrowIf(pageData.IsNull(), "创建页失败");
|
||||||
|
|
||||||
|
//var result = await dotMatrixPageRepository.InsertRangeAsync(dotMatrixpages);
|
||||||
|
//BusinessException.ThrowIf(result, "创建点阵页失败");
|
||||||
|
});
|
||||||
|
|
||||||
|
return pages.Count; // 返回主目录ID
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateAsync(PageLayoutInput input)
|
||||||
|
{
|
||||||
|
var page = await Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(page == null, "不存在此页");
|
||||||
|
|
||||||
|
var Journal = await JournalRepository.GetByIdAsync(page.JournalId);
|
||||||
|
BusinessException.ThrowIf(Journal == null, "不存在此书");
|
||||||
|
|
||||||
|
BusinessException.ThrowIf(Journal?.Status == (int)JournalStatusEnum.Archive, "书籍已归档不能修改");
|
||||||
|
var transResult = await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
//await dotMatrixPageRepository.Updateable().SetColumns(s => s.Area == input.Layout)
|
||||||
|
// .SetColumns(s => s.AreaPoints == dotMatrixPage.Area)
|
||||||
|
// .SetColumns(s => s.WidthMilliMeter == dotMatrixPage.WidthMilliMeter)
|
||||||
|
// .SetColumns(s => s.HightMilliMeter == dotMatrixPage.HightMilliMeter)
|
||||||
|
// .SetColumns(s => s.WidthDotMatrix == dotMatrixPage.WidthDotMatrix)
|
||||||
|
// .SetColumns(s => s.HightDotMatrix == dotMatrixPage.HightDotMatrix)
|
||||||
|
// .Where(w => w.Id == page.DotMatrixPageId).ExecuteCommandAsync();
|
||||||
|
|
||||||
|
page.Layout = input.Layout;
|
||||||
|
base.Update(page);
|
||||||
|
|
||||||
|
input.TasksImages?.ForEach(it =>
|
||||||
|
{
|
||||||
|
var key = $"Journal/{Journal.Id}/{input.Id}/{it.TaskId}/qustion.{it.Url.ToExtension()}";
|
||||||
|
ossService.CopyObject(it.Url.RemoveDomain(), key);
|
||||||
|
JournalPageTaskRepository.Updateable().SetColumns(s => s.TaskUrl, key).Where(w => w.Id == it.TaskId).ExecuteCommand();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return transResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修改书页的点阵码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> UpdatePageNoAsync(long JournalId)
|
||||||
|
{
|
||||||
|
//这里不能修改 Exchange = "icr.direct" 否则会报错
|
||||||
|
var msRes = await rabbitMqService.SendAsync(new RabbitMQSendParam { Exchange = "icr.direct", Queue = "mq.Journal.updatePageNo.queue", RoutingKey = "mq.Journal.updatePageNo", Data = JournalId });//发生消息
|
||||||
|
return msRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打印书页(全部)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> PrintJournalPageAsync(long JournalId)
|
||||||
|
{
|
||||||
|
//这里不能修改 Exchange = "icr.direct" 否则会报错
|
||||||
|
var msRes = await rabbitMqService.SendAsync(new RabbitMQSendParam { Exchange = "icr.direct", Queue = "mq.Journal.printJournalPage.queue", RoutingKey = "mq.Journal.printJournalPage", Data = JournalId });//发生消息
|
||||||
|
return msRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JournalPageV2Output> DetailAsync(long id)
|
||||||
|
{
|
||||||
|
var output = await Queryable()
|
||||||
|
//.LeftJoin<DotMatrixPage>((a, b) => a.DotMatrixPageId == b.Id)
|
||||||
|
.Where(a => a.Id == id)
|
||||||
|
.Select(a => new JournalPageV2Output
|
||||||
|
{
|
||||||
|
JournalId = a.JournalId,
|
||||||
|
JournalCatalogId = a.JournalCatalogId,
|
||||||
|
JournalPageId = a.Id,
|
||||||
|
Url = a.Url,
|
||||||
|
Layout = a.Layout,
|
||||||
|
PageNum = a.PageNum,
|
||||||
|
})
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (output != null)
|
||||||
|
{
|
||||||
|
output.Tasks = await JournalPageTaskRepository.Queryable()//.Select<IcrJournalAssignTask>()
|
||||||
|
.Where(a => a.JournalPageId == output.JournalPageId)
|
||||||
|
.Select(a => new JournalPageTaskV2Output
|
||||||
|
{
|
||||||
|
Id = a.Id,
|
||||||
|
No = a.No,
|
||||||
|
Type = (TaskBankTypeEnum)a.Type,
|
||||||
|
Options = a.Options,
|
||||||
|
Answers = a.Answer,
|
||||||
|
Analysis = a.Analysis,
|
||||||
|
//Assign = SqlFunc.Subqueryable<IcrJournalAssignTask>().Where(m => a.Id == m.JournalPageTaskId).Any()
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
//if (output?.Areas != null)
|
||||||
|
// output.Areas = await _JournalPageOtherRepository.Queryable().Where(w => w.JournalPageId == output.JournalPageId).Select<JournalPageOtherOutput>().ToListAsync();
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteAsync(long id)
|
||||||
|
{
|
||||||
|
return await Deleteable().Where(d => d.Id == id).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
//public async Task<List<JournalPageNoArticleOutput>> PageNoArticleAsync(long JournalId)
|
||||||
|
//{
|
||||||
|
// return await Queryable().Where(d => d.JournalId == JournalId)
|
||||||
|
// .Where(w => w.JournalArticleId == null)
|
||||||
|
// .ToListAsync(x => new JournalPageNoArticleOutput()
|
||||||
|
// {
|
||||||
|
// Id = x.Id,
|
||||||
|
// PageUrl = x.Url,
|
||||||
|
// PageNum = x.PageNum
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
private int MillimeterToDotMatrixUnit(float millimeterValue)
|
||||||
|
{
|
||||||
|
return (int)(millimeterValue * 8 / 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalDotPage_Task
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public int W { get; set; }
|
||||||
|
public int H { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalDotPage_Answer
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
public int W { get; set; }
|
||||||
|
public int H { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JournalDotPage_Area
|
||||||
|
{
|
||||||
|
public JournalDotPage_Task Task { get; set; }
|
||||||
|
|
||||||
|
public List<JournalDotPage_Answer> AnswerList { get; set; }
|
||||||
|
|
||||||
|
public long TaskId { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
176
QYZH.InteractiveMagazine.Service/JournalPageTaskService.cs
Normal file
176
QYZH.InteractiveMagazine.Service/JournalPageTaskService.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using Mapster;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||||
|
using QYZH.InteractiveMagazine.IService;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Repository;
|
||||||
|
using SqlSugar;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Service;
|
||||||
|
|
||||||
|
public class JournalPageTaskService(BaseRepository<Journal> journalRepository, OssService ossService) : BaseRepository<JournalPageTask>, IJournalPageTaskService
|
||||||
|
{
|
||||||
|
public async Task<long?> InsertAsync(JournalPageTaskAddInput input)
|
||||||
|
{
|
||||||
|
var JournalStatus = await journalRepository.Queryable().Where(w => w.Id == input.JournalId).Select(s => s.Status).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(JournalStatus == (int)JournalStatusEnum.Archive, "书籍已归档不能添加题目");
|
||||||
|
|
||||||
|
var map = input.Adapt<JournalPageTask>();
|
||||||
|
map.Id = YitIdHelper.NextId();
|
||||||
|
map.GroupId = map.Id;
|
||||||
|
var no = input.No.Split('-').Select(int.Parse).ToArray();
|
||||||
|
if (no.Last() >= 2)
|
||||||
|
{
|
||||||
|
var startNo = $"{no[0]}-{no[1]}-{no[2]}-1";
|
||||||
|
var groupTask = await Queryable().Where(w => w.No == startNo && w.JournalId == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(groupTask.IsNull(), "未找到关联跨页的第一部分");
|
||||||
|
map.GroupId = groupTask.GroupId;
|
||||||
|
map.Score = groupTask.Score;
|
||||||
|
map.AnswerTime = groupTask.AnswerTime;
|
||||||
|
map.Type = groupTask.Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = await base.InsertAsync(map);
|
||||||
|
return map?.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> UpdateAsync(JournalPageTaskUpdateInput input)
|
||||||
|
{
|
||||||
|
var JournalStatus = await journalRepository.Queryable().Where(w => w.Id == input.JournalId).Select(s => s.Status).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(JournalStatus == (int)JournalStatusEnum.Archive, "书籍已归档不能修改题目");
|
||||||
|
|
||||||
|
var task = await base.Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(task.IsNull(), "未找到关联的题号");
|
||||||
|
|
||||||
|
var sameTask = await base.Queryable().Where(w => w.No == input.No && w.JournalId == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(sameTask.IsNotEmpty(), "已经存在相同题号题目");
|
||||||
|
|
||||||
|
|
||||||
|
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.GroupId}";
|
||||||
|
//if (task.AnswerUrl != input.AnswerUrl && input.AnswerUrl.NotNull())
|
||||||
|
//{
|
||||||
|
// ossService.CopyObject(input.AnswerUrl.RemoveDomain(), $"{key}/answer.{input.AnswerUrl.ToExtension()}");
|
||||||
|
// input.AnswerUrl = $"{key}/answer.{input.AnswerUrl.ToExtension()}";
|
||||||
|
//}
|
||||||
|
//if (task.AnalysisUrl != input.AnalysisUrl && input.AnalysisUrl.NotNull())
|
||||||
|
//{
|
||||||
|
// ossService.CopyObject(input.AnalysisUrl.RemoveDomain(), $"{key}/analysis.{input.AnalysisUrl.ToExtension()}");
|
||||||
|
// input.AnalysisUrl = $"{key}/analysis.{input.AnalysisUrl.ToExtension()}";
|
||||||
|
//}
|
||||||
|
//if (task.VideoUrl != input.VideoUrl && input.VideoUrl.NotNull())
|
||||||
|
//{
|
||||||
|
// ossService.CopyObject(input.VideoUrl.RemoveDomain(), $"{key}/video.{input.VideoUrl.ToExtension()}");
|
||||||
|
// input.VideoUrl = $"{key}/video.{input.VideoUrl.ToExtension()}";
|
||||||
|
//}
|
||||||
|
|
||||||
|
input.Task = Deal(task.Task, input.Task, key, "task");
|
||||||
|
input.Answer = Deal(task.Answer, input.Answer, key, "answer");
|
||||||
|
input.Analysis = Deal(task.Analysis, input.Analysis, key, "analysis");
|
||||||
|
|
||||||
|
task.Type = input.Type;
|
||||||
|
task.Task = input.Task;
|
||||||
|
task.Options = input.Options;
|
||||||
|
task.Answer = input.Answer;
|
||||||
|
//task.AnswerUrl = input.AnswerUrl;
|
||||||
|
task.Analysis = input.Analysis;
|
||||||
|
//task.AnalysisUrl = input.AnalysisUrl;
|
||||||
|
task.Score = input.Score;
|
||||||
|
task.AnswerTime = input.AnswerTime;
|
||||||
|
// task.VideoUrl = input.VideoUrl;
|
||||||
|
task.No = input.No;
|
||||||
|
var no = input.No.Split('-').Select(int.Parse).ToArray();
|
||||||
|
if (no.Last() >= 2)
|
||||||
|
{
|
||||||
|
var startNo = $"{no[0]}-{no[1]}-{no[2]}-1";
|
||||||
|
var groupTask = await Queryable().Where(w => w.No == startNo && w.JournalId == input.JournalId).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(groupTask.IsNull(), "未找到关联跨页的第一部分");
|
||||||
|
task.GroupId = groupTask.GroupId;
|
||||||
|
task.Score = groupTask.Score;
|
||||||
|
task.AnswerTime = groupTask.AnswerTime;
|
||||||
|
task.Type = groupTask.Type;
|
||||||
|
}
|
||||||
|
//task.JournalId = input.JournalId;
|
||||||
|
//task.JournalPageId = input.JournalPageId;
|
||||||
|
// 这里不能直接用Set,因为jsonmap无法赋值 不知道为啥
|
||||||
|
return await base.Updateable(task).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> KeywordAnalysisAsync(JournalPageTaskKeywordAnalysis input)
|
||||||
|
{
|
||||||
|
return await base.Updateable().SetColumns(s => s.KeywordAnalysis, input.KeywordAnalysis).Where(w => w.GroupId == input.Id).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JournalPageTaskOutput> DetailAsync(long id)
|
||||||
|
{
|
||||||
|
var data = await base.Queryable()
|
||||||
|
.Where(w => w.Id == id)
|
||||||
|
.Select(w => new JournalPageTaskOutput()
|
||||||
|
, true).FirstAsync();
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteAsync(long id)
|
||||||
|
{
|
||||||
|
var task = await base.Queryable().Where(w => w.Id == id).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(task.IsNull(), "问题不存在");
|
||||||
|
|
||||||
|
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{id}";
|
||||||
|
|
||||||
|
ossService.DeleteObject(key);
|
||||||
|
|
||||||
|
return await base.DeleteAsync(d => d.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ComplementAsync(JournalPageTaskComplementInput input)
|
||||||
|
{
|
||||||
|
var task = await base.Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(task.IsNull(), "问题不存在");
|
||||||
|
|
||||||
|
if (input.AudioUrl.NotNull())
|
||||||
|
{
|
||||||
|
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.Id}/audio.{input.AudioUrl.ToExtension()}";
|
||||||
|
ossService.CopyObject(input.AudioUrl, key);
|
||||||
|
input.AudioUrl = key;
|
||||||
|
}
|
||||||
|
if (input.PointsUrl.NotNull())
|
||||||
|
{
|
||||||
|
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.Id}/points.json";
|
||||||
|
ossService.CopyObject(input.PointsUrl, key);
|
||||||
|
input.PointsUrl = key;
|
||||||
|
}
|
||||||
|
return await Updateable(input.Adapt<JournalPageTask>()).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Deal(string oldUrls, string newUrls, string key, string type)
|
||||||
|
{
|
||||||
|
// 如果 newUrls 为空,则直接返回
|
||||||
|
if (newUrls.IsNull()) return null;
|
||||||
|
|
||||||
|
// 处理 oldUrls 为 null 的情况,将其视为空字符串
|
||||||
|
var oldTaskUrls = oldUrls.AllUrl().Distinct();
|
||||||
|
var taskUrls = newUrls.AllUrl().Distinct().Select(s => s.RemoveDomain());
|
||||||
|
|
||||||
|
// 删除不存在的(存在于旧集合但不在新集合中)
|
||||||
|
var removedUrls = oldTaskUrls.Except(taskUrls).ToList();
|
||||||
|
ossService.DeleteObjects(removedUrls);
|
||||||
|
|
||||||
|
// 添加新增的(存在于新集合但不在旧集合中)
|
||||||
|
var addedUrls = taskUrls.Except(oldTaskUrls).ToList();
|
||||||
|
foreach (var item in addedUrls)
|
||||||
|
{
|
||||||
|
var value = $"{key}/{type}/{YitIdHelper.NextId()}.{item.ToExtension()}";
|
||||||
|
ossService.CopyObject(item, value);
|
||||||
|
|
||||||
|
newUrls = newUrls.Replace(item, value);
|
||||||
|
|
||||||
|
}
|
||||||
|
return newUrls;
|
||||||
|
}
|
||||||
|
}
|
||||||
313
QYZH.InteractiveMagazine.Service/JournalService.cs
Normal file
313
QYZH.InteractiveMagazine.Service/JournalService.cs
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
using Mapster;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||||
|
using QYZH.InteractiveMagazine.IService;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Entity;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.Repository;
|
||||||
|
using SqlSugar;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.Service;
|
||||||
|
|
||||||
|
public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
|
||||||
|
BaseRepository<JournalPageTask> JournalPageTaskRepository,
|
||||||
|
BaseRepository<DotFile> dotFileRepository,
|
||||||
|
BaseRepository<DotFileDetail> dotFileDetailRepository, OssService ossService, ILogger<JournalService> logger) : BaseRepository<Journal>, IJournalService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 查询List
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<List<JournalDto>> GetListAsync(JournalQueryDto dto)
|
||||||
|
{
|
||||||
|
var query = await Queryable()
|
||||||
|
.WhereIF(!string.IsNullOrWhiteSpace(dto.Name), a => a.Name.Contains(dto.Name))
|
||||||
|
.WhereIF(dto.Status.HasValue, a => a.Status == (int)dto.Status)
|
||||||
|
.Select(a => new JournalDto(), true)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页查询
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="search"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<PageListModel<JournalDto>> GetPageListAsync(PageQueryModel<JournalQueryDto> search)
|
||||||
|
{
|
||||||
|
RefAsync<int> totalNumber = 0;
|
||||||
|
|
||||||
|
var dataList = await Queryable()
|
||||||
|
//.InnerJoin<IcrJournalOrganization>((a, b) => a.Id == b.JournalId)
|
||||||
|
.WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name))
|
||||||
|
.WhereIF(search.Params.Status.HasValue, a => a.Status == (int)search.Params.Status)
|
||||||
|
.WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name))
|
||||||
|
.OrderByDescending(a => a.CreatedAt)
|
||||||
|
.Select(a => new JournalDto(), true)
|
||||||
|
|
||||||
|
.ToPageListAsync(search.PageIndex, search.PageSize, totalNumber);
|
||||||
|
|
||||||
|
return new PageListModel<JournalDto>(dataList, search.PageIndex, search.PageSize, totalNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<BaseResponse<bool>> EditAsync(JournalEditDto input)
|
||||||
|
{
|
||||||
|
|
||||||
|
var Journal = await base.GetByIdAsync(input.Id);
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "不存在的Id");
|
||||||
|
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Archive, "已归档不可编辑");
|
||||||
|
if (string.IsNullOrWhiteSpace(input.Cover))
|
||||||
|
{
|
||||||
|
Journal.Cover = null;
|
||||||
|
}
|
||||||
|
else if (input.Cover.RemoveDomain() != Journal.Cover)
|
||||||
|
{
|
||||||
|
var key = $"Journal/{Journal.Id}/cover.{input.Cover.ToExtension()}";
|
||||||
|
ossService.CopyObject(input.Cover.RemoveDomain(), key);
|
||||||
|
Journal.Cover = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input.BackCover))
|
||||||
|
{
|
||||||
|
Journal.BackCover = null;
|
||||||
|
}
|
||||||
|
else if (input.BackCover.RemoveDomain() != Journal.BackCover)
|
||||||
|
{
|
||||||
|
var backCoverkey = $"Journal/{Journal.Id}/backCover.{input.BackCover.ToExtension()}";
|
||||||
|
ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverkey);
|
||||||
|
Journal.BackCover = backCoverkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input.PdfUrl))
|
||||||
|
{
|
||||||
|
Journal.PdfUrl = null;
|
||||||
|
}
|
||||||
|
else if (input.PdfUrl.RemoveDomain() != Journal.PdfUrl)
|
||||||
|
{
|
||||||
|
var pdfUrlkey = $"Journal/{Journal.Id}/Journal.{input.PdfUrl.ToExtension()}";
|
||||||
|
ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfUrlkey);
|
||||||
|
Journal.PdfUrl = pdfUrlkey;
|
||||||
|
}
|
||||||
|
Journal.Width = input.Width;
|
||||||
|
Journal.Height = input.Height;
|
||||||
|
Journal.Name = input.Name;
|
||||||
|
Journal.Title = input.Title;
|
||||||
|
|
||||||
|
var res = await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
var result = await Context.Updateable(Journal).IgnoreColumns(c => c.Status).ExecuteCommandAsync();
|
||||||
|
return result > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return BaseResponse<bool>.Success(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<JournalTaskOutput>> Tasks(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskRepository.Queryable().Where(w => w.JournalId == id)
|
||||||
|
.Select(x => new JournalTaskOutput()
|
||||||
|
{
|
||||||
|
TaskId = x.Id,
|
||||||
|
TaskNo = x.No,
|
||||||
|
TaskSubType = x.Type
|
||||||
|
}).ToListAsync();
|
||||||
|
return data.OrderBy(x =>
|
||||||
|
{
|
||||||
|
var parts = x.TaskNo.Split('-').Select(int.Parse).ToArray();
|
||||||
|
return (parts[0], parts[1], parts[2], parts[3]); // 元组
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JournalDto> DetailAsync(long id)
|
||||||
|
{
|
||||||
|
var Journal = await base.Queryable().Where(w => w.Id == id).Select<JournalDto>().FirstAsync();
|
||||||
|
return Journal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteAsync(List<long> ids)
|
||||||
|
{
|
||||||
|
BusinessException.ThrowIf(!base.Queryable().Any(w => ids.Contains(w.Id)), "ID不存在");
|
||||||
|
BusinessException.ThrowIf(base.Queryable().Any(w => ids.Contains(w.Id) && w.Status == (int)JournalStatusEnum.Archive), "已归档不可删除");
|
||||||
|
var result = await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
await base.DeleteAsync(d => ids.Contains(d.Id));
|
||||||
|
await JournalPageRepository.DeleteAsync(d => ids.Contains(d.JournalId));
|
||||||
|
await JournalPageTaskRepository.DeleteAsync(d => ids.Contains(d.JournalId));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
//删除OSS上的Journal/{JournalId}文件夹
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
foreach (var JournalId in ids)
|
||||||
|
{
|
||||||
|
var prefix = $"Journal/{JournalId}/";
|
||||||
|
var keys = ossService.ListObjects(prefix);
|
||||||
|
if (keys?.Count > 0)
|
||||||
|
{
|
||||||
|
ossService.DeleteObjects(keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">书id</param>
|
||||||
|
/// <param name="index">起始页码</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> StartPageAsync(long id, int index)
|
||||||
|
{
|
||||||
|
var oldIndex = await JournalPageRepository.Queryable().Where(w => w.JournalId == id && w.PageNum == 1).Select(x => x.Sort).FirstAsync();
|
||||||
|
|
||||||
|
// 如果位置没有变化,直接返回
|
||||||
|
if (index == oldIndex) return true;
|
||||||
|
|
||||||
|
if (index > oldIndex)
|
||||||
|
{
|
||||||
|
// 向后移动的情况
|
||||||
|
var x = index - oldIndex;
|
||||||
|
await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync();
|
||||||
|
await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.PageNum - x).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 向前移动的情况
|
||||||
|
//var x = oldIndex - input.Index;
|
||||||
|
await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync();
|
||||||
|
|
||||||
|
// 将目标页面设置为第一页
|
||||||
|
await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort - (index - 1)).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort).Where(w => w.JournalId == input.Id).ExecuteCommandAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DotMatrixOutput> PrintCodeAsync(long id)
|
||||||
|
{
|
||||||
|
var Journal = await base.Queryable().Where(w => w.Id == id).FirstAsync();
|
||||||
|
BusinessException.ThrowIf(Journal.IsNull(), "不存在书");
|
||||||
|
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Codeing, "正在生成中...");
|
||||||
|
|
||||||
|
var pages = await JournalPageRepository.Queryable().Where(w => w.JournalId == id).ToListAsync();
|
||||||
|
BusinessException.ThrowIf(pages.IsNull(), "不存在页");
|
||||||
|
|
||||||
|
var output = new DotMatrixOutput()
|
||||||
|
{
|
||||||
|
Method = "Journal",
|
||||||
|
Name = Journal.Name,
|
||||||
|
NoteId = id,
|
||||||
|
PdfUrl = Journal.PdfUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//铺码业务后续调整
|
||||||
|
|
||||||
|
//var exist = pages.All(c => c.PageNo.IsNull());
|
||||||
|
//if (!exist)
|
||||||
|
//{
|
||||||
|
// var dotIds = pages.Select(s => s.DotId).Distinct();
|
||||||
|
// var dots = await dotFileRepository.Queryable().Where(w => dotIds.Contains(w.Id)).ToListAsync();
|
||||||
|
// output.DotMatrixs = dots.Select(s => new DotMatrixMqOutput() { DotId = s.Id, FileAddress = s.FileAddress }).ToList();
|
||||||
|
// foreach (var page in pages)
|
||||||
|
// {
|
||||||
|
// var find = output.DotMatrixs.Find(f => f.DotId == page.DotId);
|
||||||
|
// find!.Pages.Add(new DotMatrixPageDto()
|
||||||
|
// {
|
||||||
|
// PageNo = page.PageNo,
|
||||||
|
// PageNum = 1
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var ok = await producingService.SendAsync(new RabbitMQSendParam() { Exchange = "icr.direct", RoutingKey = "mq.dotmatrix", Data = output });
|
||||||
|
// BusinessException.ThrowIf(!ok, "消息发送失败,生成失败");
|
||||||
|
|
||||||
|
// await base.Updateable().SetColumns(s => s.Status, JournalStatusEnum.Codeing).Where(w => w.Id == id).ExecuteCommandAsync();
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// //var first = await dotFileDetailRepository.Queryable().Where(w => w.IsUse == false).GroupBy(g => g.DotId).Having(h => h.Count() >= pages.Count).FirstAsync(a => new { DotId = a.Key, Count = a.Count() });
|
||||||
|
// var first = await dotFileDetailRepository.Queryable().Where(w => w.IsUse == false).GroupBy(g => g.DotId).Having(h => SqlFunc.AggregateCount(h.Id) >= pages.Count).Select(a => new { DotId = a.DotId, Count = SqlFunc.AggregateCount(a.Id) }).FirstAsync();
|
||||||
|
// BusinessException.ThrowIf(first.IsNull(), "点阵页码不足,生成失败");
|
||||||
|
// var dotFile = await dotFileRepository.Queryable().Where(w => w.Id == first.DotId).FirstAsync();
|
||||||
|
// if (dotFile != null)
|
||||||
|
// dotFile.Details = dotFileDetailRepository.Queryable().Where(w => !w.IsUse && w.DotId == dotFile.Id).Take(pages.Count()).ToList();
|
||||||
|
// var dotMatrix = new DotMatrixMqOutput()
|
||||||
|
// {
|
||||||
|
// DotId = dotFile.Id,
|
||||||
|
// FileAddress = dotFile.FileAddress,
|
||||||
|
// };
|
||||||
|
// output.DotMatrixs.Add(dotMatrix);
|
||||||
|
|
||||||
|
// foreach (var item in dotFile.Details)
|
||||||
|
// {
|
||||||
|
// var index = dotFile.Details.IndexOf(item);
|
||||||
|
// pages[index].PageNo = item.PageName;
|
||||||
|
// pages[index].DotId = dotFile.Id;
|
||||||
|
|
||||||
|
// dotMatrix.Pages.Add(new DotMatrixPageDto()
|
||||||
|
// {
|
||||||
|
// PageNo = item.PageName,
|
||||||
|
// PageNum = 1
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// await UseTranAsync(async () =>
|
||||||
|
// {
|
||||||
|
// // 更新使用数量
|
||||||
|
// //await dotFileRepository.Updateable().SetColumns(s => s.TotalUse, dotFile.TotalUse + pages.Count).Where(w => w.Id == dotFile.Id).ExecuteCommandAsync();
|
||||||
|
// // 更新使用
|
||||||
|
// await dotFileDetailRepository.Updateable().SetColumns(s => s.IsUse, true).Where(w => dotFile.Details.Select(s => s.Id).Contains(w.Id)).ExecuteCommandAsync();
|
||||||
|
// // 更新页码
|
||||||
|
// await dotMatrixPageRepository.UpdateRangeAsync(pages);
|
||||||
|
// await Updateable().SetColumns(s => s.Status, JournalStatusEnum.CodeSuccess).Where(w => w.Id == id).ExecuteCommandAsync();
|
||||||
|
// //发送消息
|
||||||
|
// //var ok = await producingService.SendAsync(new RabbitMQSendParam() { Exchange = "icr.direct", RoutingKey = "mq.dotmatrix", Data = output });
|
||||||
|
// //BusinessException.ThrowIf(!ok, "消息发送失败,生成失败");
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ResultReportAsync(DotMatrixNoteJournalReportInput input)
|
||||||
|
{
|
||||||
|
if (input.FileUrl.IsNull() && input.FileKey.IsNull())
|
||||||
|
{
|
||||||
|
return await Updateable().SetColumns(s => s.Status, input.Success ? JournalStatusEnum.CodeSuccess : JournalStatusEnum.CodeFail)
|
||||||
|
.SetColumns(s => s.PdfPreviewUrl, input.FileKey)
|
||||||
|
.Where(w => w.Id == input.Id)
|
||||||
|
.ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
return await Updateable().SetColumns(s => s.Status, JournalStatusEnum.CodeFail).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> StatusAsync(long id, JournalStatusEnum status)
|
||||||
|
{
|
||||||
|
var book = await base.GetByIdAsync(id);
|
||||||
|
var tasks = await Context.Queryable<JournalPageTask>().Where(w => w.JournalId == id).ToListAsync();
|
||||||
|
BusinessException.ThrowIf(tasks.Count == 0 && status == JournalStatusEnum.Archive, "未添加任何题目,无法归档");
|
||||||
|
BusinessException.ThrowIf(tasks.Any(a => string.IsNullOrWhiteSpace(a.TaskUrl)) && status == JournalStatusEnum.Archive, $"{string.Join(',', tasks.Where(a => string.IsNullOrWhiteSpace(a.TaskUrl)).Select(a => a.No).ToList())}未保存,无法归档");
|
||||||
|
var res = await base.Updateable().SetColumns(s => s.Status, status).Where(w => w.Id == id).ExecuteCommandAsync() > 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -253,79 +253,103 @@ public class PetService(
|
|||||||
throw new BusinessException("宠物未激活,无法喂养", 400);
|
throw new BusinessException("宠物未激活,无法喂养", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 事务保证一致性
|
||||||
|
await UseTranAsync(async () =>
|
||||||
|
{
|
||||||
|
result = await FeedPetInTranAsync(userId, input);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.LogInformation("喂养宠物成功,PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
|
||||||
|
input.PetId, result.GrowthBefore, result.GrowthAfter, result.HasEvolved);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 喂养宠物(无事务,需在外部事务中调用)
|
||||||
|
/// </summary>
|
||||||
|
public async Task<FeedPetOutput> FeedPetInTranAsync(long userId, FeedPetInput input)
|
||||||
|
{
|
||||||
|
// 查询宠物
|
||||||
|
var pet = await petRepository.GetByIdAsync(input.PetId);
|
||||||
|
if (pet == null || pet.IsDeleted)
|
||||||
|
throw new BusinessException("宠物不存在", 404);
|
||||||
|
|
||||||
|
if (pet.UserId != userId)
|
||||||
|
throw new BusinessException("无权操作该宠物", 403);
|
||||||
|
|
||||||
|
if (pet.Status != (int)UserPetStatusEnum.Active)
|
||||||
|
throw new BusinessException("宠物未激活,无法喂养", 400);
|
||||||
|
|
||||||
var growthBefore = pet.GrowthPoints;
|
var growthBefore = pet.GrowthPoints;
|
||||||
var growthAfter = growthBefore + input.GrowthPoints;
|
var growthAfter = growthBefore + input.GrowthPoints;
|
||||||
var hasEvolved = false;
|
var hasEvolved = false;
|
||||||
string? evolvedStageName = null;
|
string? evolvedStageName = null;
|
||||||
|
|
||||||
// 事务保证一致性
|
// 累加成长值和喂养次数
|
||||||
await UseTranAsync(async () =>
|
var updateResult = await petRepository.Context.Updateable<UserPet>()
|
||||||
|
.SetColumns(p => p.GrowthPoints == growthAfter)
|
||||||
|
.SetColumns(p => p.FeedingCount == p.FeedingCount + 1)
|
||||||
|
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
||||||
|
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
||||||
|
.Where(p => p.Id == input.PetId)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
|
if (updateResult <= 0)
|
||||||
{
|
{
|
||||||
// 累加成长值和喂养次数
|
throw new BusinessException("更新宠物成长值失败", 500);
|
||||||
var updateResult = await petRepository.Context.Updateable<UserPet>()
|
}
|
||||||
.SetColumns(p => p.GrowthPoints == growthAfter)
|
|
||||||
.SetColumns(p => p.FeedingCount == p.FeedingCount + 1)
|
// 进化检查:查找下一阶段进化形态(PreviousEvolutionId 类型为 long?)
|
||||||
|
var nextEvolution = await petEvolutionRepository.Queryable()
|
||||||
|
.Where(e => e.PreviousEvolutionId == pet.CurrentEvolutionId
|
||||||
|
&& e.RequiredGrowth <= growthAfter
|
||||||
|
&& e.Status == (int)DefaultStatusEnum.Active)
|
||||||
|
.OrderBy(e => e.RequiredGrowth, OrderByType.Desc)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (nextEvolution != null)
|
||||||
|
{
|
||||||
|
// 触发进化
|
||||||
|
var evolveResult = await petRepository.Context.Updateable<UserPet>()
|
||||||
|
.SetColumns(p => p.CurrentEvolutionId == nextEvolution.Id)
|
||||||
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
||||||
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
||||||
.Where(p => p.Id == input.PetId)
|
.Where(p => p.Id == input.PetId)
|
||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
if (updateResult <= 0)
|
if (evolveResult > 0)
|
||||||
{
|
{
|
||||||
throw new BusinessException("更新宠物成长值失败", 500);
|
hasEvolved = true;
|
||||||
|
evolvedStageName = nextEvolution.StageName;
|
||||||
|
logger.LogInformation("宠物进化成功,PetId: {PetId}, 新形态: {StageName} (Level {StageLevel})",
|
||||||
|
input.PetId, nextEvolution.StageName, nextEvolution.StageLevel);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 进化检查:查找下一阶段进化形态(PreviousEvolutionId 类型为 long?)
|
// 写入喂养记录
|
||||||
var nextEvolution = await petEvolutionRepository.Queryable()
|
var record = new PetFeedingRecord
|
||||||
.Where(e => e.PreviousEvolutionId == pet.CurrentEvolutionId
|
{
|
||||||
&& e.RequiredGrowth <= growthAfter
|
PetId = input.PetId,
|
||||||
&& e.Status == (int)DefaultStatusEnum.Active)
|
UserId = userId,
|
||||||
.OrderBy(e => e.RequiredGrowth, OrderByType.Desc)
|
PointsUsed = 0, // 预留:后期可扩展为消耗积分喂养
|
||||||
.FirstAsync();
|
GrowthChange = input.GrowthPoints,
|
||||||
|
GrowthBefore = growthBefore,
|
||||||
|
GrowthAfter = growthAfter,
|
||||||
|
Type = PetFeedingRecordTypeEnum.Normal,
|
||||||
|
Status = (int)PetFeedingRecordStatusEnum.Success,
|
||||||
|
IsDeleted = false,
|
||||||
|
CreatedBy = userId.ToString(),
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
UpdatedBy = userId.ToString(),
|
||||||
|
UpdatedAt = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
if (nextEvolution != null)
|
var insertResult = await feedingRecordRepository.InsertAsync(record);
|
||||||
{
|
if (!insertResult)
|
||||||
// 触发进化
|
{
|
||||||
var evolveResult = await petRepository.Context.Updateable<UserPet>()
|
throw new BusinessException("写入喂养记录失败", 500);
|
||||||
.SetColumns(p => p.CurrentEvolutionId == nextEvolution.Id)
|
}
|
||||||
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
|
||||||
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
|
||||||
.Where(p => p.Id == input.PetId)
|
|
||||||
.ExecuteCommandAsync();
|
|
||||||
|
|
||||||
if (evolveResult > 0)
|
|
||||||
{
|
|
||||||
hasEvolved = true;
|
|
||||||
evolvedStageName = nextEvolution.StageName;
|
|
||||||
logger.LogInformation("宠物进化成功,PetId: {PetId}, 新形态: {StageName} (Level {StageLevel})",
|
|
||||||
input.PetId, nextEvolution.StageName, nextEvolution.StageLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入喂养记录
|
|
||||||
var record = new PetFeedingRecord
|
|
||||||
{
|
|
||||||
PetId = input.PetId,
|
|
||||||
UserId = userId,
|
|
||||||
PointsUsed = 0, // 预留:后期可扩展为消耗积分喂养
|
|
||||||
GrowthChange = input.GrowthPoints,
|
|
||||||
GrowthBefore = growthBefore,
|
|
||||||
GrowthAfter = growthAfter,
|
|
||||||
Type = PetFeedingRecordTypeEnum.Normal,
|
|
||||||
Status = (int)PetFeedingRecordStatusEnum.Success,
|
|
||||||
IsDeleted = false,
|
|
||||||
CreatedBy = userId.ToString(),
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
UpdatedBy = userId.ToString(),
|
|
||||||
UpdatedAt = DateTime.Now
|
|
||||||
};
|
|
||||||
|
|
||||||
var insertResult = await feedingRecordRepository.InsertAsync(record);
|
|
||||||
if (!insertResult)
|
|
||||||
{
|
|
||||||
throw new BusinessException("写入喂养记录失败", 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.LogInformation("喂养宠物成功,PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
|
logger.LogInformation("喂养宠物成功,PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
|
||||||
input.PetId, growthBefore, growthAfter, hasEvolved);
|
input.PetId, growthBefore, growthAfter, hasEvolved);
|
||||||
|
|||||||
@ -158,7 +158,7 @@ public class UsersService(
|
|||||||
if (journalDict.TryGetValue(item.JournalId, out var journal))
|
if (journalDict.TryGetValue(item.JournalId, out var journal))
|
||||||
{
|
{
|
||||||
item.JournalTitle = journal.Title;
|
item.JournalTitle = journal.Title;
|
||||||
item.CoverImageUrl = journal.CoverImageUrl;
|
item.CoverImageUrl = journal.Cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MiniExcelLibs;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.Context;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
using QYZH.InteractiveMagazine.Models.Dto;
|
using QYZH.InteractiveMagazine.Models.Dto;
|
||||||
using QYZH.InteractiveMagazine.Models.Enum;
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
namespace QYZH.InteractiveMagazine.WebApi.Controllers;
|
namespace QYZH.InteractiveMagazine.WebApi.Controllers;
|
||||||
|
|
||||||
@ -75,4 +79,57 @@ public abstract class BaseController : ControllerBase
|
|||||||
{
|
{
|
||||||
return Ok(BaseResponse.Fail(msg));
|
return Ok(BaseResponse.Fail(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导出Excel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">完整文件路径</param>
|
||||||
|
/// <param name="fileName">带扩展文件名</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected IActionResult ExportExcel(string path, string fileName)
|
||||||
|
{
|
||||||
|
//var webHostEnvironment = App.WebHostEnvironment;
|
||||||
|
if (!Path.Exists(path))
|
||||||
|
{
|
||||||
|
throw new BusinessException(fileName + "文件不存在");
|
||||||
|
}
|
||||||
|
var stream = System.IO.File.OpenRead(path); //创建文件流
|
||||||
|
|
||||||
|
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
|
||||||
|
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected (string, string) DownloadImportTemplate<T>(List<T> list, string fileName)
|
||||||
|
{
|
||||||
|
IWebHostEnvironment webHostEnvironment = ServiceContext.GetService<IWebHostEnvironment>();
|
||||||
|
string sFileName = $"{fileName}.xlsx";
|
||||||
|
string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "ImportTemplate", sFileName);
|
||||||
|
|
||||||
|
//不存在模板创建模板
|
||||||
|
if (!Directory.Exists(fullPath))
|
||||||
|
{
|
||||||
|
var directoryPath = Path.GetDirectoryName(fullPath);
|
||||||
|
if (directoryPath != null)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Path.Exists(fullPath))
|
||||||
|
{
|
||||||
|
MiniExcel.SaveAs(fullPath, list, overwriteFile: true);
|
||||||
|
}
|
||||||
|
return (sFileName, fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected (string, string) ExportExcelMini<T>(List<T> list, string sheetName, string fileName)
|
||||||
|
{
|
||||||
|
IWebHostEnvironment webHostEnvironment = ServiceContext.GetService<IWebHostEnvironment>();
|
||||||
|
string sFileName = $"{fileName}_{DateTime.Now:MMdd_HHmmss}.xlsx";
|
||||||
|
string fullPath = Path.Combine(webHostEnvironment.WebRootPath, "export", sFileName);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
||||||
|
|
||||||
|
MiniExcel.SaveAs(fullPath, list, sheetName: sheetName);
|
||||||
|
return (sFileName, fullPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
559
QYZH.InteractiveMagazine.WebApi/Controllers/JournalController.cs
Normal file
559
QYZH.InteractiveMagazine.WebApi/Controllers/JournalController.cs
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
|
||||||
|
using QYZH.InteractiveMagazine.Models.Base;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MiniExcelLibs;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||||
|
using QYZH.InteractiveMagazine.IService;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Common;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Dto;
|
||||||
|
using QYZH.InteractiveMagazine.Models.Enum;
|
||||||
|
using QYZH.InteractiveMagazine.WebApi.Controllers;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
|
||||||
|
namespace QYZH.InteractiveMagazine.WebApi.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 期刊管理
|
||||||
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Platform))]
|
||||||
|
[Route("api/icr")]
|
||||||
|
public class JournalController(
|
||||||
|
OssService ossService,
|
||||||
|
IJournalCatalogService JournalCatalogService,
|
||||||
|
IJournalService JournalService,
|
||||||
|
IJournalPageService JournalPageService,
|
||||||
|
IJournalPageTaskService JournalPageTaskService,
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
Microsoft.Extensions.Configuration.IConfiguration configuration) : BaseController
|
||||||
|
{
|
||||||
|
|
||||||
|
#region 制书
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID查询书籍数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("Journal/detail/{id:long}")]
|
||||||
|
public async Task<BaseResponse<JournalDto>> GetByIdAsync([Required] long id)
|
||||||
|
{
|
||||||
|
var result = await JournalService.GetByExpressionAsync<JournalDto>(c => c.Id == id);
|
||||||
|
return BaseResponse<JournalDto>.Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分页查询书籍列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="search">查询条件</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/pagelist")]
|
||||||
|
public async Task<BaseResponse<PageListModel<JournalDto>>> GetPageListAsync([FromBody] PageQueryModel<JournalQueryDto> search)
|
||||||
|
{
|
||||||
|
var result = await JournalService.GetPageListAsync(search);
|
||||||
|
return BaseResponse<PageListModel<JournalDto>>.Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询书籍列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/list")]
|
||||||
|
public async Task<BaseResponse<List<JournalDto>>> GetListAsync([FromQuery] JournalQueryDto dto)
|
||||||
|
{
|
||||||
|
var result = await JournalService.GetListAsync(dto);
|
||||||
|
return BaseResponse<List<JournalDto>>.Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 创建书籍
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="dto">书籍对象</param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("Journal/add")]
|
||||||
|
//public async Task<BaseResponse<long>> AddAsync([FromBody] JournalEditDto dto)
|
||||||
|
//{
|
||||||
|
// var result = await JournalService.AddAsync(dto);
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑书籍
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto">书籍对象</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/update")]
|
||||||
|
public async Task<BaseResponse> Edit([FromBody] JournalEditDto dto)
|
||||||
|
{
|
||||||
|
var result = await JournalService.EditAsync(dto);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量删除
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ids"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/delete")]
|
||||||
|
public async Task<BaseResponse<bool>> DeleteAsync([Required][FromBody] List<long> ids)
|
||||||
|
{
|
||||||
|
var result = await JournalService.DeleteAsync(ids);
|
||||||
|
return BaseResponse<bool>.Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍起始页
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/startpage")]
|
||||||
|
public async Task<BaseResponse<bool>> StartPageAsync([FromQuery]long id, [FromQuery] int index)
|
||||||
|
{
|
||||||
|
var data = await JournalService.StartPageAsync(id, index);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍归档
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/rchive/{id:long}")]
|
||||||
|
public async Task<BaseResponse<bool>> Archive(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalService.StatusAsync(id, JournalStatusEnum.Archive);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍废弃
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost]
|
||||||
|
[HttpPost, Route("Journal/abandon/{id:long}")]
|
||||||
|
public async Task<BaseResponse<bool>> Abandon(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalService.StatusAsync(id, JournalStatusEnum.Abandoned);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍铺码
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/printcode/{id:long}")]
|
||||||
|
public async Task<BaseResponse<DotMatrixOutput>> PrintCodeAsync(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalService.PrintCodeAsync(id);
|
||||||
|
return BaseResponse<DotMatrixOutput>.Success(data);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 上报铺码结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/resultreport")]
|
||||||
|
public async Task<BaseResponse<bool>> GenerateAsync([FromBody] DotMatrixNoteJournalReportInput input, [FromForm] IFormFile? file)
|
||||||
|
{
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
input.FileKey = $"Journal/{input.Id}/dotMatrixJournal.pdf";
|
||||||
|
input.FileUrl = ossService.PutObject(input.FileKey, file.OpenReadStream());
|
||||||
|
}
|
||||||
|
var data = await JournalService.ResultReportAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍导入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Journal/import")]
|
||||||
|
public async Task<BaseResponse<long>> JournalImport(JournalImportDto input)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.ImportAsync(input);
|
||||||
|
return BaseResponse<long>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 工单分页列表
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="search"></param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("tasklibrary/workorder/pagelist")]
|
||||||
|
//public async Task<BaseResponse<PageListModel<IcrJournalWorkOrderDto>>> WorkOrderPageListAsync([FromBody] PageQueryModel<IcrWorkOrderQueryDto> search)
|
||||||
|
//{
|
||||||
|
// var data = await JournalWorkOrderService.WorkOrderPageListAsync(search);
|
||||||
|
// return BaseResponse<PageListModel<IcrJournalWorkOrderDto>>.Success(data);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍发起工单
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
//[HttpPost, Route("tasklibrary/workorder/create")]
|
||||||
|
//public async Task<BaseResponse<bool>> CreateWorkOrderAsync([FromBody] IcrWorkOrderCreateDto dto)
|
||||||
|
//{
|
||||||
|
// var result = await JournalWorkOrderService.CreateWorkOrderAsync(dto);
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 更新工单状态
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="JournalId"></param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("tasklibrary/workorder/setstatus/{JournalId:long}")]
|
||||||
|
//public async Task<BaseResponse<int>> UpdateWorkOrderAsync(long JournalId)
|
||||||
|
//{
|
||||||
|
// var result = await JournalWorkOrderService.UpdateWorkOrderAsync(JournalId);
|
||||||
|
// return BaseResponse<int>.Success(result);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 题库代理请求接口
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("tasklibrary/proxyget")]
|
||||||
|
public async Task<IActionResult> GetTaskLibraryData(string apiUrl = "")
|
||||||
|
{
|
||||||
|
var client = httpClientFactory.CreateClient();
|
||||||
|
var response = await client.GetAsync(configuration["TaskLibraryApi"] + apiUrl);
|
||||||
|
// 确保请求成功(状态码 200)
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string result = await response.Content.ReadAsStringAsync();
|
||||||
|
return Content(result, "application/json");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 书籍目录
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 书籍目录详情
|
||||||
|
///// </summary>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpGet, Route("catalog/tree/{JournalId:long}")]
|
||||||
|
//public async Task<BaseResponse<List<IcrJournalCatalog>>> CatalogDetailAsync(long JournalId)
|
||||||
|
//{
|
||||||
|
// var data = await _JournalCatalogService.DetailAsync(JournalId);
|
||||||
|
// return BaseResponse<List<IcrJournalCatalog>>.Success(data);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录详情
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("catalog/tree/{JournalId:long}")]
|
||||||
|
public async Task<BaseResponse<List<IcrJournalCatalogTreeDto>>> CatalogDetailAsync(long JournalId)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.GetJournalCataloTreeAsync(JournalId);
|
||||||
|
return BaseResponse<List<IcrJournalCatalogTreeDto>>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取书分类及页码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("catalog/list")]
|
||||||
|
public async Task<BaseResponse<List<JournalCatalogTreeListDto>>> GetJournalCatelogListAsync(long JournalId)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.GetJournalCatalogListAsync(JournalId);
|
||||||
|
return BaseResponse<List<JournalCatalogTreeListDto>>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载书籍目录导入模板
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("catalog/template")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult ImportCatalogTemplateExcel()
|
||||||
|
{
|
||||||
|
var result = DownloadImportTemplate(new List<JournalCatalogTreeListDto>() { }, "书籍目录导入模板");
|
||||||
|
return ExportExcel(result.Item2, result.Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录导出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("catalog/export/{JournalId}")]
|
||||||
|
public async Task<IActionResult> CatalogExport(long JournalId)
|
||||||
|
{
|
||||||
|
var list = await JournalCatalogService.GetJournalCatalogListAsync(JournalId);
|
||||||
|
|
||||||
|
var result = ExportExcelMini(list, "sheet1", "书籍目录导出");
|
||||||
|
return ExportExcel(result.Item2, result.Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录导入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="JournalId">file</param>
|
||||||
|
/// <param name="file">file</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("catalog/import/{JournalId}")]
|
||||||
|
public async Task<BaseResponse<bool>> ImportAsync(long JournalId, [FromForm(Name = "file")] IFormFile file)
|
||||||
|
{
|
||||||
|
List<JournalCatalogTreeListDto> dto = new();
|
||||||
|
using (var stream = file.OpenReadStream())
|
||||||
|
{
|
||||||
|
dto = stream.Query<JournalCatalogTreeListDto>().ToList();
|
||||||
|
}
|
||||||
|
var result = await JournalCatalogService.ImportCatalogAsync(JournalId, dto);
|
||||||
|
return BaseResponse<bool>.Success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录新增
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("catalog/add")]
|
||||||
|
public async Task<BaseResponse<long>> CatalogAdd(JournalCatalogInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.InsertAsync(input);
|
||||||
|
return BaseResponse<long>.Success(data);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录修改
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("catalog/update")]
|
||||||
|
public async Task<BaseResponse<bool>> CatalogUpdate(JournalCatalogUpdateInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.UpdateAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录删除
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("catalog/delete/{id:long}")]
|
||||||
|
public async Task<BaseResponse<bool>> CatalogDelete(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.DeleteAsync(id);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 书籍目录复制
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="input"></param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("catalog/copy")]
|
||||||
|
//[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
|
||||||
|
//public async Task<BaseResponse> CatalogCopy(CopyInput input)
|
||||||
|
//{
|
||||||
|
// var data = await _JournalCatalogServices.CopyAsync(input);
|
||||||
|
// return ApiResult(data, "书籍目录删除失败!");
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书籍目录移动
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("catalog/move")]
|
||||||
|
public async Task<BaseResponse<bool>> CatalogMove(MoveInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalCatalogService.MoveAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 书页
|
||||||
|
/// <summary>
|
||||||
|
/// 书页新增
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/add")]
|
||||||
|
public async Task<BaseResponse<long>> PageAdd(JournalAddV2Input input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageService.InsertAsync(input);
|
||||||
|
return BaseResponse<long>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页修改
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/update")]
|
||||||
|
public async Task<BaseResponse<bool>> PageUpdate(PageLayoutInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageService.UpdateAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动铺码
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/updatepageno/{JournalId:long}")]
|
||||||
|
public async Task<BaseResponse<bool>> PageUpdatePageNo([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
|
||||||
|
{
|
||||||
|
var data = await JournalPageService.UpdatePageNoAsync(JournalId);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载书籍页码点阵码PDF文件名称
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("page/downloadJournalpagePdf/{JournalId:long}")]
|
||||||
|
public async Task<IActionResult> DownloadJournalPagePdf([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
|
||||||
|
{
|
||||||
|
var entity = await JournalService.GetFirstAsync(x => x.Id == JournalId);
|
||||||
|
if (entity == null)
|
||||||
|
throw new BusinessException("书籍不存在");
|
||||||
|
|
||||||
|
var downloadUrl = entity.DownloadJournalPagePdfName;
|
||||||
|
if (string.IsNullOrWhiteSpace(downloadUrl))
|
||||||
|
throw new BusinessException("下载点阵码PDF文件为空,请先铺码");
|
||||||
|
|
||||||
|
var stream = await httpClientFactory.CreateClient().GetStreamAsync(downloadUrl); //创建文件流
|
||||||
|
if (stream == null)
|
||||||
|
throw new BusinessException("下载点阵码PDF文件失败");
|
||||||
|
|
||||||
|
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
|
||||||
|
|
||||||
|
return File(stream, "application/pdf", HttpUtility.UrlEncode(Path.GetFileName(downloadUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 打印书页(全部) 暂时废弃这个方,直接使用 Adobe Reader X 软件打印就可以了
|
||||||
|
///// </summary>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("page/printJournalPage/{JournalId:long}")]
|
||||||
|
//public async Task<BaseResponse<bool>> PrintJournalPage([Required(ErrorMessage = "书籍编号不允许为空")] long JournalId)
|
||||||
|
//{
|
||||||
|
// var data = await JournalPageService.PrintJournalPageAsync(JournalId);
|
||||||
|
// return BaseResponse<bool>.Success(data);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 书页删除
|
||||||
|
///// </summary>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpPost, Route("page/delete/{id:long}")]
|
||||||
|
//[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
|
||||||
|
//public async Task<IActionResult> PageDelete(long id)
|
||||||
|
//{
|
||||||
|
// var data = await _JournalPageServices.DeleteAsync(id);
|
||||||
|
// return ApiResult(data, "更新书页失败!");
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页详情
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet, Route("page/detail/{id:long}")]
|
||||||
|
public async Task<BaseResponse<JournalPageV2Output>> PageDetail(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalPageService.DetailAsync(id);
|
||||||
|
return BaseResponse<JournalPageV2Output>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 不包含有文章的书页
|
||||||
|
///// </summary>
|
||||||
|
///// <returns></returns>
|
||||||
|
//[HttpGet, Route("page/no-article/{JournalId:long}")]
|
||||||
|
//[ProducesResponseType(typeof(BaseResponse<List<JournalPageNoArticleOutput>>), 200)]
|
||||||
|
//public async Task<IActionResult> PageNoArticleAsync(long JournalId)
|
||||||
|
//{
|
||||||
|
// var data = await _JournalPageService.PageNoArticleAsync(JournalId);
|
||||||
|
// return BaseResponse<List<JournalPageNoArticleOutput>>(data, "查询书页失败!");
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 书页问题
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题新增
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/task/add")]
|
||||||
|
[ProducesResponseType(typeof(BaseResponse<long>), 200)]
|
||||||
|
public async Task<BaseResponse<long>> PageTaskAdd(JournalPageTaskAddInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.InsertAsync(input);
|
||||||
|
return BaseResponse<long>.Success(data.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题单项解析
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/task/keyword-analysis")]
|
||||||
|
[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
|
||||||
|
public async Task<BaseResponse<bool>> KeywordAnalysisAsync(JournalPageTaskKeywordAnalysis input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.KeywordAnalysisAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题更新
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/task/update")]
|
||||||
|
[ProducesResponseType(typeof(BaseResponse<bool>), 200)]
|
||||||
|
public async Task<BaseResponse<bool>> PageTaskUpdate(JournalPageTaskUpdateInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.UpdateAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题详情 + 题库那边需要用
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet, Route("page/task/detail/{id:long}")]
|
||||||
|
public async Task<BaseResponse<JournalPageTaskOutput>> PageTaskDetail(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.DetailAsync(id);
|
||||||
|
return BaseResponse<JournalPageTaskOutput>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题删除
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/task/delete/{id:long}")]
|
||||||
|
public async Task<BaseResponse<bool>> PageTaskDelete(long id)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.DeleteAsync(id);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 书页问题补充
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("page/task/complement")]
|
||||||
|
public async Task<BaseResponse<bool>> ComplementAnsync(JournalPageTaskComplementInput input)
|
||||||
|
{
|
||||||
|
var data = await JournalPageTaskService.ComplementAsync(input);
|
||||||
|
return BaseResponse<bool>.Success(data);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ using Swashbuckle.AspNetCore.SwaggerGen;
|
|||||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Yitter.IdGenerator;
|
using Yitter.IdGenerator;
|
||||||
|
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@ -51,6 +52,9 @@ builder.Services.AddDataProtection()
|
|||||||
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
|
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
|
||||||
//redis
|
//redis
|
||||||
builder.Services.AddCSRedisCacheExtension(builder.Configuration.GetSection("RedisSettings"));
|
builder.Services.AddCSRedisCacheExtension(builder.Configuration.GetSection("RedisSettings"));
|
||||||
|
|
||||||
|
//MQ
|
||||||
|
builder.Services.AddRabbitMQ(builder.Configuration);
|
||||||
// 配置Serilog
|
// 配置Serilog
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(builder.Configuration)
|
.ReadFrom.Configuration(builder.Configuration)
|
||||||
|
|||||||
@ -13,12 +13,13 @@
|
|||||||
"Sentinels": [],
|
"Sentinels": [],
|
||||||
"ExpireSecondRange": [ 3600, 7200 ]
|
"ExpireSecondRange": [ 3600, 7200 ]
|
||||||
},
|
},
|
||||||
"RabbitMQSettings": {
|
"RabbitMq": {
|
||||||
"HostName": "192.168.20.150",
|
"HostName": "192.168.20.150",
|
||||||
"Port": 5672,
|
"Port": 5672,
|
||||||
"UserName": "smartschool",
|
"UserName": "smartschool",
|
||||||
"Password": "@ss%&*otz%d*pq2S",
|
"Password": "@ss%&*otz%d*pq2S",
|
||||||
"VirtualHost": "InteractiveMagazine"
|
"VirtualHost": "InteractiveMagazine",
|
||||||
|
"ClientProvidedName": "Custom connection name"
|
||||||
},
|
},
|
||||||
"WeChatSettings": {
|
"WeChatSettings": {
|
||||||
"AppId": "wx7922cc9b6023f3ac",
|
"AppId": "wx7922cc9b6023f3ac",
|
||||||
|
|||||||
Reference in New Issue
Block a user