Service.cs.vm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
  2. //
  3. // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
  4. //
  5. // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
  6. using Admin.NET.Core.Service;
  7. using Microsoft.AspNetCore.Http;
  8. @{
  9. string LowerFirstLetter(string text) => text.ToString()[..1].ToLower() + text[1..];
  10. var primaryKey = Model.TableField.First(u => u.ColumnKey == "True");
  11. var importFields = Model.TableField.Where(x => x.WhetherImport == "Y");
  12. var injectServices = new List<string>();
  13. if (Model.TableField.Any(u => u.EffectType == "Upload")) injectServices.Add("SysFileService");
  14. if (Model.TableField.Any(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector")) injectServices.Add("SysDictTypeService");
  15. }
  16. namespace @(Model.NameSpace);
  17. /// <summary>
  18. /// @(Model.BusName)服务 🧩
  19. /// </summary>
  20. [ApiDescriptionSettings(@(Model.ProjectLastName)Const.GroupName, Order = 100)]
  21. public class @(Model.ClassName)Service : IDynamicApiController, ITransient
  22. {
  23. private readonly SqlSugarRepository<@(Model.ClassName)> _@(Model.LowerClassName)Rep;
  24. @foreach(var name in injectServices) {
  25. @:private readonly @name _@(LowerFirstLetter(name));
  26. }
  27. public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(injectServices.Count > 0 ? ", " + string.Join(", ", injectServices.Select(name => $"{name} {LowerFirstLetter(name)}")) : ""))
  28. {
  29. _@(Model.LowerClassName)Rep = @(Model.LowerClassName)Rep;
  30. @foreach(var name in injectServices) {
  31. @:_@LowerFirstLetter(name) = @LowerFirstLetter(name);
  32. }
  33. }
  34. /// <summary>
  35. /// 分页查询@(Model.BusName) 🔖
  36. /// </summary>
  37. /// <param name="input"></param>
  38. /// <returns></returns>
  39. [DisplayName("分页查询@(Model.BusName)")]
  40. [ApiDescriptionSettings(Name = "Page"), HttpPost]
  41. public async Task<SqlSugarPagedList<@(Model.ClassName)Output>> Page(Page@(Model.ClassName)Input input)
  42. {
  43. input.Keyword = input.Keyword?.Trim();
  44. var query = _@(Model.LowerClassName)Rep.AsQueryable()
  45. @{
  46. string joinTableName = "u";
  47. var queryFields = Model.TableField.Where(u => u.QueryWhether == "Y");
  48. // 关键字模糊查询
  49. if (queryFields.Any(u => u.QueryType == "like")) {
  50. @:.WhereIF(!string.IsNullOrEmpty(input.Keyword), u => @string.Join(" || ", queryFields.Where(u => u.QueryType == "like").Select(col => $"u.{col.PropertyName}.Contains(input.Keyword)")))
  51. }
  52. // 字段组合查询
  53. foreach(var column in queryFields) {
  54. if (column.NetType.TrimEnd('?') == "string") {
  55. @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName)@(column.QueryType == "like" ? $".Contains(input.{column.PropertyName}.Trim())" : $" {column.QueryType} input.{column.PropertyName}.Trim()"))
  56. } else if (column.NetType.TrimEnd('?') == "int" || column.NetType.TrimEnd('?') == "long") {
  57. @:.WhereIF(input.@(column.PropertyName) != null, u => u.@(column.PropertyName) @(column.QueryType) input.@(column.PropertyName))
  58. } else if (column.NetType.TrimEnd('?').EndsWith("Enum")) {
  59. @:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName))
  60. } else if (column.NetType.TrimEnd('?') == "DateTime" && column.QueryType == "~") {
  61. @:.WhereIF(input.@(column.PropertyName)Range?.Length == 2, u => u.@(column.PropertyName) >= input.@(column.PropertyName)Range[0] && u.@(column.PropertyName) <= input.@(column.PropertyName)Range[1])
  62. }
  63. }
  64. // 联表
  65. if (Model.IsJoinTable) {
  66. @foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")){
  67. var joinTableAlias = Regex.Replace(column.LowerPropertyName, "[iI]d$", "");
  68. joinTableName += ", " + joinTableAlias;
  69. @:.LeftJoin<@column.FkEntityName>((@joinTableName) => u.@(column.PropertyName) == @joinTableAlias.@(column.EffectType == "ForeignKey" ? column.FkLinkColumnName : column.ValueColumn))
  70. }
  71. // 查询列表
  72. @:.Select((@joinTableName) => new @(Model.ClassName)Output
  73. @:{
  74. foreach (var column in Model.TableField) {
  75. var joinTableAlias = Regex.Replace(column.LowerPropertyName, "[iI]d$", "");
  76. if (column.EffectType == "ForeignKey") {
  77. var columnList = column.FkColumnName.Split(",").Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
  78. @:@(column.PropertyName) = u.@(column.PropertyName),
  79. @:@(column.PropertyName)FkColumn = $"@(string.Join("-", columnList))",
  80. } else if (column.EffectType == "ApiTreeSelector") {
  81. var columnList = column.DisplayColumn.Split(",").Select(n => $"{{{joinTableAlias}.{n}}}").ToList();
  82. @:@(column.PropertyName) = u.@(column.PropertyName),
  83. @:@(column.PropertyName)Display = $"@(string.Join("-", columnList))",
  84. } else {
  85. @:@(column.PropertyName) = u.@(column.PropertyName),
  86. }
  87. }
  88. @:});
  89. } else {
  90. // 无联表
  91. @:.Select<@(Model.ClassName)Output>();
  92. }
  93. }
  94. return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
  95. }
  96. /// <summary>
  97. /// 增加@(Model.BusName) ➕
  98. /// </summary>
  99. /// <param name="input"></param>
  100. /// <returns></returns>
  101. [DisplayName("增加@(Model.BusName)")]
  102. [ApiDescriptionSettings(Name = "Add"), HttpPost]
  103. public async Task<long> Add(Add@(Model.ClassName)Input input)
  104. {
  105. var entity = input.Adapt<@(Model.ClassName)>();
  106. @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => Model.TableField.Any(x2 => x2.WhetherAddUpdate == "Y" && x2.PropertyName == x1)))) {
  107. @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @(string.Join(" && ", config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
  108. }
  109. return await _@(Model.LowerClassName)Rep.InsertAsync(entity) ? entity.Id : 0;
  110. }
  111. /// <summary>
  112. /// 删除@(Model.BusName) ❌
  113. /// </summary>
  114. /// <param name="input"></param>
  115. /// <returns></returns>
  116. [DisplayName("删除@(Model.BusName)")]
  117. [ApiDescriptionSettings(Name = "Delete"), HttpPost]
  118. public async Task Delete(Delete@(Model.ClassName)Input input)
  119. {
  120. @foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")) {
  121. @:var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => u.@(column.PropertyName) == input.@(column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
  122. }
  123. await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); //假删除
  124. //await _@(Model.LowerClassName)Rep.DeleteAsync(entity); //真删除
  125. }
  126. /// <summary>
  127. /// 批量删除@(Model.BusName) ❌
  128. /// </summary>
  129. /// <param name="input"></param>
  130. /// <returns></returns>
  131. [DisplayName("批量删除@(Model.BusName)")]
  132. [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
  133. public async Task<int> BatchDelete(BatchDelete@(Model.ClassName)Input input)
  134. {
  135. @foreach (var column in Model.TableField.Where(u => u.ColumnKey == "True")) {
  136. @:var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(u => input.@(column.PropertyName)List.Contains(u.@(column.PropertyName))).ToListAsync() ?? throw Oops.Oh(ErrorCodeEnum.D1002);
  137. }
  138. return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list); //假删除
  139. //return await _@(Model.LowerClassName)Rep.DeleteAsync(list); //真删除
  140. }
  141. /// <summary>
  142. /// 更新@(Model.BusName) ✏️
  143. /// </summary>
  144. /// <param name="input"></param>
  145. /// <returns></returns>
  146. [DisplayName("更新@(Model.BusName)")]
  147. [ApiDescriptionSettings(Name = "Update"), HttpPost]
  148. public async Task Update(Update@(Model.ClassName)Input input)
  149. {
  150. @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => Model.TableField.Any(x2 => x2.WhetherAddUpdate == "Y" && x2.PropertyName == x1)))) {
  151. @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => u.@(primaryKey.PropertyName) != input.@(primaryKey.PropertyName) && @(string.Join(" && ", config.Columns.Select(x => $"u.{x} != null && u.{x} == input.{x}"))))) throw Oops.Oh("@(config.Message)已存在");
  152. }
  153. var entity = input.Adapt<@(Model.ClassName)>();
  154. await _@(Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
  155. }
  156. @if (Model.TableField.Any(col => col.NetType == "StatusEnum" && col.PropertyName == "Status")) {
  157. @:
  158. @:/// <summary>
  159. @:/// 设置@(Model.BusName)状态 🚫
  160. @:/// </summary>
  161. @:/// <param name="input"></param>
  162. @:/// <returns></returns>
  163. @:[DisplayName("设置@(Model.BusName)状态")]
  164. @:[ApiDescriptionSettings(Name = "SetStatus"), HttpPost]
  165. @:public async Task Set@(Model.ClassName)Status(BaseStatusInput input)
  166. @:{
  167. @:await _@(Model.LowerClassName)Rep.AsUpdateable().SetColumns(u => u.Status, input.Status).Where(u => u.Id == input.Id).ExecuteCommandAsync();
  168. @:}
  169. }
  170. /// <summary>
  171. /// 获取@(Model.BusName)详情 ℹ️
  172. /// </summary>
  173. /// <param name="input"></param>
  174. /// <returns></returns>
  175. [DisplayName("获取@(Model.BusName)详情")]
  176. [ApiDescriptionSettings(Name = "Detail"), HttpGet]
  177. public async Task<@(Model.ClassName)> Detail([FromQuery] QueryById@(Model.ClassName)Input input)
  178. {
  179. return await _@(Model.LowerClassName)Rep.GetFirstAsync(u => u.@(primaryKey.PropertyName) == input.@(primaryKey.PropertyName));
  180. }
  181. /// <summary>
  182. /// 获取@(Model.BusName)列表 🔖
  183. /// </summary>
  184. /// <param name="input"></param>
  185. /// <returns></returns>
  186. [DisplayName("获取@(Model.BusName)列表")]
  187. [ApiDescriptionSettings(Name = "List"), HttpGet]
  188. public async Task<List<@(Model.ClassName)Output>> List([FromQuery] Page@(Model.ClassName)Input input)
  189. {
  190. return await _@(Model.LowerClassName)Rep.AsQueryable().Select<@(Model.ClassName)Output>().ToListAsync();
  191. }
  192. @foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.QueryWhether == "Y"))){
  193. @:
  194. var dropdownName = $"{column.FkEntityName}{Regex.Replace(column.PropertyName, "[iI]d$", "")}Dropdown";
  195. @:/// <summary>
  196. @:/// 获取@(column.ColumnComment)列表 🔖
  197. @:/// </summary>
  198. @:/// <returns></returns>
  199. @:[DisplayName("获取@(column.ColumnComment)列表")]
  200. @:[ApiDescriptionSettings(Name = "@(dropdownName)"), HttpGet]
  201. @:public async Task<dynamic> @(dropdownName)([FromQuery]bool all)
  202. @:{
  203. var columnList = column.FkColumnName.Split(",").Select(name => $"{{u.{name}}}").ToList();
  204. @:return await _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>()
  205. @:.InnerJoinIF<@Model.ClassName>(!all, (u, r) => u.@(column.FkLinkColumnName) == r.@(column.PropertyName))
  206. @:.Select(u => new
  207. @:{
  208. @:Label = $"@(string.Join("-", columnList))",
  209. @:Value = u.@(column.FkLinkColumnName)
  210. @:}
  211. @:).ToListAsync();
  212. @:}
  213. }
  214. @{
  215. var definedObjects = new Dictionary<string, object>();
  216. @foreach (var column in Model.TableField.Where(u => u.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(u.FkEntityName)Tree"))){
  217. @:
  218. definedObjects.Add("@(column.FkEntityName)Tree", 1);
  219. @:/// <summary>
  220. @:/// 获取@(column.ColumnComment)选择数据 🔖
  221. @:/// </summary>
  222. @:[DisplayName("获取@(column.ColumnComment)选择数据")]
  223. @:[ApiDescriptionSettings(Name = "@(column.FkEntityName)Tree"), HttpGet]
  224. @:public async Task<List<@(column.FkEntityName)TreeOutput>> @(column.FkEntityName)Tree([FromQuery]bool all)
  225. @:{
  226. var columnList = column.DisplayColumn.Split(",").Select(name => $"{{u.{name}}}").ToList();
  227. @:return await _@(Model.LowerClassName)Rep.Context.Queryable<@column.FkEntityName>()
  228. @:.InnerJoinIF<@Model.ClassName>(!all, (u, r) => u.@(column.ValueColumn) == r.@(column.PropertyName))
  229. @:.Select(u => new @(column.FkEntityName)TreeOutput {
  230. @:Label = $"@(string.Join("-", columnList))",
  231. @:Value = u.@column.ValueColumn
  232. @:}, true).ToTreeAsync(u => u.Children, u => u.@column.PidColumn, @(column.WhetherRequired == "Y" ? "0" : "null"));
  233. @:}
  234. }
  235. }
  236. @foreach (var column in Model.TableField.Where(u => u.EffectType == "Upload")) {
  237. @:
  238. @:/// <summary>
  239. @:/// 上传@(column.ColumnComment) ⬆️
  240. @:/// </summary>
  241. @:/// <param name="file"></param>
  242. @:/// <returns></returns>
  243. @:[DisplayName("上传@(column.ColumnComment)")]
  244. @:[ApiDescriptionSettings(Name = "Upload@(column.PropertyName)"), HttpPost]
  245. @:public async Task<SysFile> Upload@(column.PropertyName)([Required] IFormFile file)
  246. @:{
  247. @:return await _sysFileService.UploadFile(new FileUploadInput { File = file, SavePath = "upload/@(Model.ClassName)/@(column.PropertyName)" });
  248. @:}
  249. }
  250. @if (importFields.Count() > 0) {
  251. @:
  252. @:/// <summary>
  253. @:/// 下载@(Model.BusName)数据导入模板 ⬇️
  254. @:/// </summary>
  255. @:/// <returns></returns>
  256. @:[DisplayName("下载@(Model.BusName)数据导入模板")]
  257. @:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
  258. @:public IActionResult DownloadTemplate()
  259. @:{
  260. var fieldsList = importFields.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList();
  261. if (fieldsList.Any()) {
  262. @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板", (_, info) =>
  263. @:{
  264. foreach (var column in fieldsList) {
  265. var columnList = (column.EffectType == "ForeignKey" ? column.FkColumnName : column.DisplayColumn).Split(",").Select(n => $"{{u.{n}}}").ToList();
  266. @:if (nameof(Export@(Model.ClassName)Output.@(column.PropertyName)Label) == info.Name) return _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Select(u => $"@(string.Join("-", columnList))").Distinct().ToList();
  267. }
  268. @:return null;
  269. @:});
  270. } else {
  271. @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板");
  272. }
  273. @:}
  274. @:
  275. @:/// <summary>
  276. @:/// 导入@(Model.BusName)记录 💾
  277. @:/// </summary>
  278. @:/// <returns></returns>
  279. @:[DisplayName("导入@(Model.BusName)记录")]
  280. @:[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
  281. @:public IActionResult ImportData([Required] IFormFile file)
  282. @:{
  283. @:lock (this)
  284. @:{
  285. var dictTableField = Model.TableField.Where(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector") ?? default;
  286. foreach (var column in dictTableField){
  287. @:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictTypeCode)" }).Result.ToDictionary(x => x.Value, x => x.Code);
  288. }
  289. @:var stream = ExcelHelper.ImportData<Import@(Model.ClassName)Input, @(Model.ClassName)>(file, (list, markerErrorAction) =>
  290. @:{
  291. @:_@(Model.LowerClassName)Rep.Context.Utilities.PageEach(list, 2048, pageItems =>
  292. @:{
  293. foreach (var column in importFields.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")) {
  294. @:// 链接 @(column.ColumnComment)
  295. @:var @(column.LowerPropertyName)LabelList = pageItems.Where(x => x.@(column.PropertyName)Label != null).Select(x => x.@(column.PropertyName)Label).Distinct().ToList();
  296. @:if (@(column.LowerPropertyName)LabelList.Any()) {
  297. var valueColumn = column.EffectType == "ForeignKey" ? column.FkLinkColumnName : column.ValueColumn;
  298. var columnList = (column.EffectType == "ForeignKey" ? column.FkColumnName : column.DisplayColumn).Split(",").Select(n => $"{{u.{n}}}").ToList();
  299. @:var @(column.LowerPropertyName)LinkMap = _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Where(u => @(column.LowerPropertyName)LabelList.Contains($"@(string.Join("-", columnList))")).ToList().ToDictionary(u => $"@(string.Join("-", columnList))", u => u.@(valueColumn));
  300. @:pageItems.ForEach(e => e.@(column.PropertyName) = @(column.LowerPropertyName)LinkMap?.GetValueOrDefault(e.@(column.PropertyName)Label, default));
  301. @:}
  302. }
  303. @:
  304. @:// 校验并过滤必填基本类型为null的字段
  305. @:var rows = pageItems.Where(x => {
  306. foreach (var column in importFields.Where(x => x.WhetherRequired == "Y" && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){
  307. @:if (x.@(column.PropertyName) == null){
  308. @:x.Error = "@(column.ColumnComment)不能为空";
  309. @:return false;
  310. @:}
  311. }
  312. @:return true;
  313. @:}).Adapt<List<@(Model.ClassName)>>();
  314. if (dictTableField.Any()) {
  315. @:
  316. @:// 映射字典值
  317. @:foreach(var row in rows) {
  318. foreach (var column in dictTableField){
  319. @:row.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(row.@(column.PropertyName) ?? "");
  320. }
  321. @:}
  322. }
  323. @:
  324. @:var storageable = _@(Model.LowerClassName)Rep.Context.Storageable(rows)
  325. foreach (var column in importFields){
  326. if (column.WhetherRequired == "Y"){
  327. if(column.NetType.TrimEnd('?') == "string"){
  328. @:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(column.PropertyName)), "@(column.ColumnComment)不能为空")
  329. } else if(column.NetType.EndsWith('?') == true){
  330. @:.SplitError(it => it.Item.@(column.PropertyName) == null, "@(column.ColumnComment)不能为空")
  331. }}
  332. if (column.NetType?.TrimEnd('?') == "string"){
  333. @:.SplitError(it => it.Item.@(column.PropertyName)?.Length > @(column.ColumnLength), "@(column.ColumnComment)长度不能超过@(column.ColumnLength)个字符")
  334. }}
  335. @foreach (var config in Model.TableUniqueList.Where(x => x.Columns.All(x1 => importFields.Any(x2 => x2.PropertyName == x1)))) {
  336. @:.WhereColumns(it => new { @(string.Join(", ", config.Columns.Select(x => $"it.{x}"))) }).SplitError(it => it.Any(), "@(config.Message)已存在")
  337. }
  338. @:.SplitInsert(_ => true)
  339. @:.ToStorage();
  340. @:
  341. @:storageable.BulkCopy();
  342. @:storageable.BulkUpdate();
  343. @:
  344. @:// 标记错误信息
  345. @:markerErrorAction.Invoke(storageable, pageItems, rows);
  346. @:});
  347. @:});
  348. @:
  349. @:return stream;
  350. @:}
  351. @:}
  352. }
  353. @}