Service.cs.vm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. namespace @(Model.NameSpace);
  9. /// <summary>
  10. /// @(Model.BusName)服务 🧩
  11. /// </summary>
  12. [ApiDescriptionSettings(@(Model.ProjectLastName)Const.GroupName, Order = 100)]
  13. public class @(Model.ClassName)Service : IDynamicApiController, ITransient
  14. {
  15. private readonly SqlSugarRepository<@(Model.ClassName)> _@(Model.LowerClassName)Rep;
  16. @foreach(var kv in Model.InjectServiceMap) {
  17. @:private readonly @(kv.Key) _@(kv.Value);
  18. }
  19. public @(Model.ClassName)Service(SqlSugarRepository<@(Model.ClassName)> @(Model.LowerClassName)Rep@(Model.InjectServiceArgs))
  20. {
  21. _@(Model.LowerClassName)Rep = @(Model.LowerClassName)Rep;
  22. @foreach(var kv in Model.InjectServiceMap) {
  23. @:_@(kv.Value) = @(kv.Value);
  24. }
  25. }
  26. /// <summary>
  27. /// 分页查询@(Model.BusName) 🔖
  28. /// </summary>
  29. /// <param name="input"></param>
  30. /// <returns></returns>
  31. [DisplayName("分页查询@(Model.BusName)")]
  32. [ApiDescriptionSettings(Name = "Page"), HttpPost]
  33. public async Task<SqlSugarPagedList<@(Model.ClassName)Output>> Page(Page@(Model.ClassName)Input input)
  34. {
  35. input.Keyword = input.Keyword?.Trim();
  36. var query = _@(Model.LowerClassName)Rep.AsQueryable()
  37. @{
  38. string joinTableName = "u";
  39. var queryFields = Model.TableField.Where(u => u.WhetherQuery == "Y");
  40. // 关键字模糊查询
  41. if (queryFields.Any(u => u.QueryType == "like")) {
  42. @:.WhereIF(!string.IsNullOrEmpty(input.Keyword), u => @string.Join(" || ", queryFields.Where(u => u.QueryType == "like").Select(col => $"u.{col.PropertyName}.Contains(input.Keyword)")))
  43. }
  44. // 字段组合查询
  45. foreach(var column in queryFields) {
  46. if (column.NetType.TrimEnd('?') == "string") {
  47. @:.WhereIF(!string.IsNullOrWhiteSpace(input.@(column.PropertyName)), u => u.@(column.PropertyName)@(column.QueryType == "like" ? $".Contains(input.{column.PropertyName}.Trim())" : $" {column.QueryType} input.{column.PropertyName}.Trim()"))
  48. } else if (column.NetType.TrimEnd('?') == "int" || column.NetType.TrimEnd('?') == "long") {
  49. @:.WhereIF(input.@(column.PropertyName) != null, u => u.@(column.PropertyName) @(column.QueryType) input.@(column.PropertyName))
  50. } else if (column.NetType.TrimEnd('?').EndsWith("Enum")) {
  51. @:.WhereIF(input.@(column.PropertyName).HasValue, u => u.@(column.PropertyName) == input.@(column.PropertyName))
  52. } else if (column.NetType.TrimEnd('?') == "DateTime" && column.QueryType == "~") {
  53. @:.WhereIF(input.@(column.PropertyName)Range?.Length == 2, u => u.@(column.PropertyName) >= input.@(column.PropertyName)Range[0] && u.@(column.PropertyName) <= input.@(column.PropertyName)Range[1])
  54. }
  55. }
  56. // 联表
  57. if (Model.HasJoinTable) {
  58. foreach (var column in Model.TableField.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")){
  59. joinTableName += ", " + column.LowerPropertyNameTrimEndId;
  60. @:.LeftJoin<@column.FkEntityName>((@joinTableName) => u.@(column.PropertyName) == @(column.LowerPropertyNameTrimEndId).@(column.FkLinkColumnName))
  61. }
  62. // 查询列表
  63. @:.Select((@joinTableName) => new @(Model.ClassName)Output
  64. @:{
  65. foreach (var column in Model.TableField) {
  66. @:@(column.PropertyName) = u.@(column.PropertyName),
  67. if (column.EffectType == "ForeignKey" || column.EffectType == "ApiTreeSelector") {
  68. @:@(column.ExtendedPropertyName) = @column.GetDisplayColumn(column.LowerPropertyNameTrimEndId),
  69. }
  70. }
  71. @:});
  72. } else {
  73. // 无联表
  74. @:.Select<@(Model.ClassName)Output>();
  75. }
  76. }
  77. return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
  78. }
  79. /// <summary>
  80. /// 获取@(Model.BusName)列表 🔖
  81. /// </summary>
  82. /// <param name="input"></param>
  83. /// <returns></returns>
  84. [DisplayName("获取@(Model.BusName)列表")]
  85. [ApiDescriptionSettings(Name = "List"), HttpGet]
  86. public async Task<List<@(Model.ClassName)Output>> List([FromQuery] Page@(Model.ClassName)Input input)
  87. {
  88. return await _@(Model.LowerClassName)Rep.AsQueryable().Select<@(Model.ClassName)Output>().ToListAsync();
  89. }
  90. /// <summary>
  91. /// 获取@(Model.BusName)详情 ℹ️
  92. /// </summary>
  93. /// <param name="input"></param>
  94. /// <returns></returns>
  95. [DisplayName("获取@(Model.BusName)详情")]
  96. [ApiDescriptionSettings(Name = "Detail"), HttpGet]
  97. public async Task<@(Model.ClassName)> Detail([FromQuery] QueryById@(Model.ClassName)Input input)
  98. {
  99. return await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}", false));
  100. }
  101. /// <summary>
  102. /// 增加@(Model.BusName) ➕
  103. /// </summary>
  104. /// <param name="input"></param>
  105. /// <returns></returns>
  106. [DisplayName("增加@(Model.BusName)")]
  107. [ApiDescriptionSettings(Name = "Add"), HttpPost]
  108. public async Task<long> Add(Add@(Model.ClassName)Input input)
  109. {
  110. var entity = input.Adapt<@(Model.ClassName)>();
  111. @foreach (var config in Model.TableUniqueConfigList) {
  112. @: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)已存在");
  113. }
  114. return await _@(Model.LowerClassName)Rep.InsertAsync(entity) ? entity.Id : 0;
  115. }
  116. /// <summary>
  117. /// 更新@(Model.BusName) ✏️
  118. /// </summary>
  119. /// <param name="input"></param>
  120. /// <returns></returns>
  121. [DisplayName("更新@(Model.BusName)")]
  122. [ApiDescriptionSettings(Name = "Update"), HttpPost]
  123. public async Task Update(Update@(Model.ClassName)Input input)
  124. {
  125. @{
  126. var primaryKeyWhere = Model.PrimaryKeysFormat(" && ", "u.{0} != input.{0}", false);
  127. foreach (var config in Model.TableUniqueConfigList) {
  128. @:if (await _@(Model.LowerClassName)Rep.IsAnyAsync(u => @primaryKeyWhere && @config.Format(" && ", "u.{0} != null && u.{0} == input.{0}"))) throw Oops.Oh("@(config.Message)已存在");
  129. }
  130. }
  131. var entity = input.Adapt<@(Model.ClassName)>();
  132. await _@(Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(u => new {
  133. @foreach (var column in Model.IgnoreUpdateFieldList) {
  134. @:u.@(column.PropertyName),
  135. }
  136. }).ExecuteCommandAsync();
  137. }
  138. /// <summary>
  139. /// 删除@(Model.BusName) ❌
  140. /// </summary>
  141. /// <param name="input"></param>
  142. /// <returns></returns>
  143. [DisplayName("删除@(Model.BusName)")]
  144. [ApiDescriptionSettings(Name = "Delete"), HttpPost]
  145. public async Task Delete(Delete@(Model.ClassName)Input input)
  146. {
  147. var entity = await _@(Model.LowerClassName)Rep.GetFirstAsync(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}", false)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
  148. await _@(Model.LowerClassName)Rep.FakeDeleteAsync(entity); //假删除
  149. //await _@(Model.LowerClassName)Rep.DeleteAsync(entity); //真删除
  150. }
  151. /// <summary>
  152. /// 批量删除@(Model.BusName) ❌
  153. /// </summary>
  154. /// <param name="input"></param>
  155. /// <returns></returns>
  156. [DisplayName("批量删除@(Model.BusName)")]
  157. [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
  158. public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<Delete@(Model.ClassName)Input> input)
  159. {
  160. var exp = Expressionable.Create<@(Model.ClassName)>();
  161. foreach (var row in input) exp = exp.Or(it => @Model.PrimaryKeysFormat(" && ", "it.{0} == row.{0}"));
  162. var list = await _@(Model.LowerClassName)Rep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
  163. return await _@(Model.LowerClassName)Rep.FakeDeleteAsync(list); //假删除
  164. //return await _@(Model.LowerClassName)Rep.DeleteAsync(list); //真删除
  165. }
  166. @if (Model.HasSetStatus) {
  167. @:
  168. @:/// <summary>
  169. @:/// 设置@(Model.BusName)状态 🚫
  170. @:/// </summary>
  171. @:/// <param name="input"></param>
  172. @:/// <returns></returns>
  173. @:[DisplayName("设置@(Model.BusName)状态")]
  174. @:[ApiDescriptionSettings(Name = "SetStatus"), HttpPost]
  175. @:public async Task Set@(Model.ClassName)Status(Set@(Model.ClassName)StatusInput input)
  176. @:{
  177. @:await _@(Model.LowerClassName)Rep.AsUpdateable().SetColumns(u => u.Status, input.Status).Where(u => @Model.PrimaryKeysFormat(" && ", "u.{0} == input.{0}", false)).ExecuteCommandAsync();
  178. @:}
  179. }
  180. @foreach (var column in Model.UploadFieldList) {
  181. @:
  182. @:/// <summary>
  183. @:/// 上传@(column.ColumnComment) ⬆️
  184. @:/// </summary>
  185. @:/// <param name="file"></param>
  186. @:/// <returns></returns>
  187. @:[DisplayName("上传@(column.ColumnComment)")]
  188. @:[ApiDescriptionSettings(Name = "Upload@(column.PropertyName)"), HttpPost]
  189. @:public async Task<SysFile> Upload@(column.PropertyName)([Required] IFormFile file)
  190. @:{
  191. @:return await _sysFileService.UploadFile(new FileUploadInput { File = file, SavePath = "upload/@(Model.ClassName)/@(column.PropertyName)" });
  192. @:}
  193. }
  194. @if (Model.DropdownFieldList.Count > 0) {
  195. @:
  196. @:/// <summary>
  197. @:/// 获取下拉列表数据 🔖
  198. @:/// </summary>
  199. @:/// <returns></returns>
  200. @:[DisplayName("获取下拉列表数据")]
  201. @:[ApiDescriptionSettings(Name = "DropdownData"), HttpPost]
  202. @:public async Task<Dictionary<string, dynamic>> DropdownData(DropdownData@(Model.ClassName)Input input)
  203. @:{
  204. foreach (var column in Model.DropdownFieldList) {
  205. @:var @(column.LowerPropertyName)Data = await _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>()
  206. if (column.EffectType != "ApiTreeSelector") {
  207. @:.InnerJoinIF<@Model.ClassName>(input.FromPage, (u, r) => u.@(column.FkLinkColumnName) == r.@(column.PropertyName))
  208. }
  209. @:.Select(u => new {
  210. @:Value = u.@(column.FkLinkColumnName),
  211. @:Label = @column.GetDisplayColumn("u")
  212. if (column.EffectType != "ApiTreeSelector") {
  213. @:}).ToListAsync();
  214. } else {
  215. @:, Id = u.Id,
  216. @:ParentId = u.@(column.PidColumn),
  217. @:Children = new List<dynamic>()
  218. @:}).ToTreeAsync(u => u.Children, u => u.ParentId, @(column.WhetherRequired == "Y" ? "0" : "null"));
  219. }
  220. }
  221. @:return new Dictionary<string, dynamic>
  222. @:{
  223. foreach (var column in Model.DropdownFieldList) {
  224. @:{ "@(column.LowerPropertyName)", @(column.LowerPropertyName)Data },
  225. }
  226. @:};
  227. @:}
  228. }
  229. @if (Model.ImportFieldList.Count > 0) {
  230. @:
  231. @:/// <summary>
  232. @:/// 下载@(Model.BusName)数据导入模板 ⬇️
  233. @:/// </summary>
  234. @:/// <returns></returns>
  235. @:[DisplayName("下载@(Model.BusName)数据导入模板")]
  236. @:[ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
  237. @:public IActionResult DownloadTemplate()
  238. @:{
  239. var fieldsList = Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList();
  240. if (fieldsList.Any()) {
  241. @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板", (_, info) =>
  242. @:{
  243. foreach (var column in fieldsList) {
  244. var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
  245. @:if (nameof(Export@(Model.ClassName)Output.@column.ExtendedPropertyName) == info.Name) return _@(Model.LowerClassName)Rep.Context.Queryable<@(column.FkEntityName)>().Select(u => $"@(string.Join("-", columnList))").Distinct().ToList();
  246. }
  247. @:return null;
  248. @:});
  249. } else {
  250. @:return ExcelHelper.ExportTemplate(new List<Export@(Model.ClassName)Output>(), "@(Model.BusName)导入模板");
  251. }
  252. @:}
  253. @:
  254. @:/// <summary>
  255. @:/// 导入@(Model.BusName)记录 💾
  256. @:/// </summary>
  257. @:/// <returns></returns>
  258. @:[DisplayName("导入@(Model.BusName)记录")]
  259. @:[ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
  260. @:public IActionResult ImportData([Required] IFormFile file)
  261. @:{
  262. @:lock (this)
  263. @:{
  264. var dictTableField = Model.TableField.Where(x => x.WhetherImport == "Y" && x.EffectType == "DictSelector") ?? default;
  265. foreach (var column in dictTableField){
  266. @:var @(column.LowerPropertyName)DictMap = _sysDictTypeService.GetDataList(new GetDataDictTypeInput { Code = "@(column.DictTypeCode)" }).Result.ToDictionary(x => x.Value, x => x.Code);
  267. }
  268. @:var stream = ExcelHelper.ImportData<Import@(Model.ClassName)Input, @(Model.ClassName)>(file, (list, markerErrorAction) =>
  269. @:{
  270. @:_sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
  271. @:{
  272. foreach (var column in Model.ImportFieldList.Where(u => u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector")) {
  273. @:// 链接 @(column.ColumnComment)
  274. @:var @(column.LowerPropertyName)LabelList = pageItems.Where(x => x.@column.ExtendedPropertyName != null).Select(x => x.@column.ExtendedPropertyName).Distinct().ToList();
  275. @:if (@(column.LowerPropertyName)LabelList.Any()) {
  276. var columnList = column.FkDisplayColumnList.Select(n => $"{{u.{n}}}").ToList();
  277. @: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.@(column.FkLinkColumnName));
  278. @:pageItems.ForEach(e => {
  279. @:e.@(column.PropertyName) = @(column.LowerPropertyName)LinkMap?.GetValueOrDefault(e.@column.ExtendedPropertyName);
  280. @:if (e.@(column.PropertyName) == null) e.Error = "@(column.ColumnComment)链接失败";
  281. @:});
  282. @:}
  283. }
  284. @:
  285. @:// 校验并过滤必填基本类型为null的字段
  286. @:var rows = pageItems.Where(x => {
  287. foreach (var column in Model.ImportFieldList.Where(x => x.WhetherRequired == "Y" && Regex.IsMatch(x.NetType, "(int|long|double|float|bool|Enum[?]?)"))){
  288. @:if (!string.IsNullOrWhiteSpace(x.Error)) return false;
  289. @:if (x.@(column.PropertyName) == null){
  290. @:x.Error = "@(column.ColumnComment)不能为空";
  291. @:return false;
  292. @:}
  293. }
  294. @:return true;
  295. @:}).Adapt<List<@(Model.ClassName)>>();
  296. if (dictTableField.Any()) {
  297. @:
  298. @:// 映射字典值
  299. @:foreach(var row in rows) {
  300. foreach (var column in dictTableField){
  301. @:if (row.@(column.PropertyName) == null) continue;
  302. @:row.@(column.PropertyName) = @(column.LowerPropertyName)DictMap.GetValueOrDefault(row.@(column.PropertyName));
  303. @:if (row.@(column.PropertyName) == null) row.Error = "@(column.ColumnComment)字典匹配失败";
  304. }
  305. @:}
  306. }
  307. @:
  308. @:var storageable = _sqlSugarClient.Storageable(rows)
  309. foreach (var column in Model.ImportFieldList){
  310. if (column.WhetherRequired == "Y"){
  311. if(column.NetType.TrimEnd('?') == "string"){
  312. @:.SplitError(it => string.IsNullOrWhiteSpace(it.Item.@(column.PropertyName)), "@(column.ColumnComment)不能为空")
  313. } else if(column.NetType.EndsWith('?') == true){
  314. @:.SplitError(it => it.Item.@(column.PropertyName) == null, "@(column.ColumnComment)不能为空")
  315. }}
  316. if (column.NetType?.TrimEnd('?') == "string"){
  317. @:.SplitError(it => it.Item.@(column.PropertyName)?.Length > @(column.ColumnLength), "@(column.ColumnComment)长度不能超过@(column.ColumnLength)个字符")
  318. }}
  319. foreach (var config in Model.TableUniqueConfigList) {
  320. @:.WhereColumns(it => new { @config.Format(", ", "it.{0}") }).SplitError(it => it.Any(), "@(config.Message)已存在")
  321. }
  322. @:.SplitInsert(_ => true)
  323. @:.ToStorage();
  324. @:
  325. @:storageable.BulkCopy();
  326. @:storageable.BulkUpdate();
  327. @:
  328. @:// 标记错误信息
  329. @:markerErrorAction.Invoke(storageable, pageItems, rows);
  330. @:});
  331. @:});
  332. @:
  333. @:return stream;
  334. @:}
  335. @:}
  336. }
  337. }