Service.cs.vm 21 KB

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