Summary
I have explained the background of this framework before. Not clear to view this link
1. Minimalist and practical Asp.NetCore Modular Framework decides to open source for free
2. Minimalist and practical ASP. New CMS module for NetCore modular framework
It has been a long time since I updated my blog. During this time I have been traveling and I have been thinking in my spare time that the dotnetcore framework itself is modular. Why do I need to continue modular encapsulation on this top-level application? Is it for better business division or wheel reuse?
In detail, this framework should not continue to be modular. There are several main reasons to support it:
1. In my existing business, there is no need for modularization, I just make a large and comprehensive system (rights management, content management, store, WeChat management, etc.).
2. If you want to be modular, you will sacrifice some performance. This is unacceptable after repeated deliberations, mainly at the expense of a little more performance.
3. dotnetcore itself is more friendly and modular. There is no need to wrap another layer on top of this application. After I downloaded the source code of dotnetcore, I found its design concept awesome. So dotnetcore itself is the best modular (component) framework, which allows me to devote a lot of time and energy to the research on the source code. There is no need to be obsessed with the concept of modularity. Applying the toss-and-roll concept at the top level will have little impact on the growth of technology.
4. Previously, it was divided into modules in order to develop towards the direction of micro-services, and then try to make small changes. Now think about me as a background manager, there's no need to think about that much.
Based on the above points, I changed the framework and branched the original modular framework (not all modular in a sense).
New Business Functions
A new mall module is added, which mainly includes commodity management (supporting multiple sku), commodity classification, applet users, user receipt address, list of various status of orders.
Part of the back-end code for adding and modifying goods:
{ try { Db.BeginTran(); // Save Goods var goods = _mapper.Map<Goods>(input); var goodsId = await Db.Insertable<Goods>(goods).ExecuteReturnIdentityAsync(); // Save Specifications await DealwithGoodsSpec(goodsId, input); Db.CommitTran(); } catch (Exception e) { Db.RollbackTran(); return new ApiResult(e.Message); } return new ApiResult(); } /// <summary> ///Public commodity specification information processing /// </summary> /// <param name="goodsId"></param> /// <param name="input"></param> /// <returns></returns> private async Task DealwithGoodsSpec(int goodsId, GoodsInput input) { // Save Specifications if (input.SpecType == SpecTypeEnum.Single.GetValue<int>()) { var specSingle = JsonConvert.DeserializeObject<GoodsSpecInput>(input.SpecSingle); input.GoodsSpecInput = specSingle; var goodsSpec = input.BuildGoodsSpec(goodsId); if (null == goodsSpec) { throw new FriendlyException("Merchandise specification entity data cannot be empty!"); } await Db.Insertable(goodsSpec).ExecuteReturnIdentityAsync(); } else { var goodsSpecs = input.BuildGoodsSpecs(goodsId); if (null == goodsSpecs || goodsSpecs.Count == 0) { throw new FriendlyException("Merchandise specification entity data collection cannot be empty!"); } await Db.Insertable(goodsSpecs).ExecuteReturnIdentityAsync(); var goodsSpecRels = input.BuildGoodsSpecRels(goodsId); if (goodsSpecRels.Count == 0 || goodsSpecRels == null) { throw new FriendlyException("Merchandise specification entity relationship collection data cannot be empty!"); } //Reverse specification group id based on specification value var specValues = await Db.Queryable<SpecValue>().Where(d => d.Status).ToListAsync(); foreach (var item in goodsSpecRels) { var specId = specValues.Where(d => d.Status && d.Id == item.SpecValueId).Select(d => d.SpecId); item.SpecId = specId.FirstOrDefault(); } await Db.Insertable(goodsSpecRels).ExecuteReturnIdentityAsync(); } } public async Task<ApiResult> ModifyAsync(GoodsModifyInput input) { var goods = await GetModelAsync(d => d.Id == input.Id); if (goods == null) throw new FriendlyException($"This commodity{input.Id}No corresponding commodity information was found"); try { Db.BeginTran(); // Update commodities var model = _mapper.Map<Goods>(input); var goodsId = await Db.Updateable(model).IgnoreColumns(d => new { d.CreateTime }).ExecuteCommandAsync(); // Update specifications await Db.Deleteable<GoodsSpec>().Where(d => d.GoodsId == input.Id).ExecuteCommandAsync(); await Db.Deleteable<GoodsSpecRel>().Where(d => d.GoodsId == input.Id).ExecuteCommandAsync(); // Save Specifications await DealwithGoodsSpec(input.Id, input); Db.CommitTran(); } catch (Exception e) { Db.RollbackTran(); return new ApiResult(e.Message); } return new ApiResult(); }
Add commodity part code to front end:
tinymce.init({ selector: '#content', auto_focus: true, height: 500, content_style: "img {max-width:100%;}", image_advtab: true,//Turn on advanced options for picture upload images_upload_url: '/api/goods/UploadImg',//Picture Upload plugins: 'print preview code searchreplace autolink directionality visualblocks visualchars fullscreen image link media codesample table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists textcolor wordcount imagetools contextmenu colorpicker textpattern help ', toolbar: 'formatselect styleselect | bold italic forecolor backcolor | link | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat' }); layui.use(['form', 'common'], function () { var form = layui.form, $ = layui.$, apiUtil = layui.common; // Current pop-up layer to prevent ID from being overwritten var parentIndex = parent.layer.getFrameIndex(window.name); apiUtil.BindParentCategory(); form.render(); // Registered Merchandise Multi-Specification Components var specMany = new GoodsSpec({ container: '.goods-spec-many' }); //Handle display of single/multiple specifications form.on('radio(specType)', function (data) { //But specifications if (data.value == 10) { $("#sigleSpec").show() && $(".goods-spec-many").hide(); } //Multi-specification if (data.value == 20) { $("#sigleSpec").hide() && $(".goods-spec-many").show(); } //console.log(data.elem); // Get the original radio DOM object }); //Listen for submissions form.on('submit(saveBtn)', function (data) { data.field.content = tinyMCE.editors[0].getContent(); var specType = $('input[name=specType]:checked').val(); if (specType == 20) { var specMany2 = JSON.stringify(specMany.getData()); console.log("specMany:" + specMany2); var isEmpty = specMany.isEmptySkuList(); if (isEmpty == true) { layer.msg('Item specifications cannot be empty'); return false; } data.field.specMany = specMany2; } else { var specSingle = { goods_no: data.field.goodsNo, line_price: data.field.linePrice, goods_price: data.field.goodsPrice, goods_weight: data.field.goodsWeight, stock_num: data.field.stockNum, }; data.field.specSingle=JSON.stringify(specSingle); } data.field.specType = specType; data.field.goodsStatus = $('input[name=goodsStatus]:checked').val(); data.field.deductStockType = $('input[name=deductStockType]:checked').val(); data.field.imgUrl = $(".div_img input").map(function () { return $(this).attr("value"); }).get().join(','); if (data.field.imgUrl == "" || data.field.imgUrl == null) { apiUtil.error("At least one picture of goods needs to be uploaded!"); return false; } apiUtil.ajax('goods/add', data.field, "application/json", "post", function (res) { apiUtil.success(res.msg); parent.layer.close(parentIndex); }); return false; }); }); </script> }
Only part of the code is posted here. You can go directly to the source code for more details. In addition, it is not too much to write a series about the design of the mall. Next time, I will take time to write a specific article to introduce how to design the multi-specifications of the goods better.
Source address: https://gitee.com/shenniu_code_group/shen-nius.-modularity