summary
I have already explained the background of this framework. If you are not clear, you can view this link
1,The minimalist and practical Asp.NetCore modular framework has decided to open source for free
2,The minimalist and practical Asp.NetCore modular framework adds CMS module
After all, I haven't updated my blog for a long time. During this period, I have been on business. In my spare time, I have been thinking that the dotnetcore framework itself is modular. Why do I continue to carry out modular packaging on this upper application? What's the significance? Is it for better business division or wheel reuse?
On reflection, this framework should not continue to be modularized, mainly for the following reasons:
1. In terms of my existing business, there is no need for modularization. I just make a large and global system (authority management, content management, mall, wechat management, etc.).
2. If you want to be modular, you have to sacrifice some performance. This is unacceptable after I think over and over again. The main reason is that you sacrifice a little more performance!
3. Dotnetcore itself is more friendly and modular. There is no need to wrap another layer on this upper application, which makes no sense. After downloading the dotnetcore source code, I think its design concept is very good, so "dotnetcore" itself is the best modular (Modular) framework, which can devote a lot of time and energy to the research of the source code, There is no need to tangle with the concept of modularity. It is very little for the growth of technology to toss around in the upper layer.
4. In the past, it was divided into modules to develop towards micro services. At that time, try to make small changes. Now think about it. I'm just a background manager. There's no need to think so much.
Based on the above points, I changed the framework and reserved the branches of 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 SKUs), commodity classification, applet users, user harvest address and list of order status.
Some back-end codes for adding and modifying items:
public async Task<ApiResult> AddAsync(GoodsInput input) { try { Db.BeginTran(); // Save goods var goods = _mapper.Map<Goods>(input); var goodsId = await Db.Insertable<Goods>(goods).ExecuteReturnIdentityAsync(); // Save specification 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 specification 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("Commodity 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("Commodity specification entity data set cannot be empty!"); } await Db.Insertable(goodsSpecs).ExecuteReturnIdentityAsync(); var goodsSpecRels = input.BuildGoodsSpecRels(goodsId); if (goodsSpecRels.Count == 0 || goodsSpecRels == null) { throw new FriendlyException("Commodity specification entity relationship set data cannot be empty!"); } //Reverses the specification group based on the specification value id 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 product{input.Id}No corresponding product information was found"); try { Db.BeginTran(); // Update product 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 specification await DealwithGoodsSpec(input.Id, input); Db.CommitTran(); } catch (Exception e) { Db.RollbackTran(); return new ApiResult(e.Message); } return new ApiResult(); }
Add commodity code to the front end:
@{ ViewData["Title"] = "Modify"; Layout = "~/Views/Shared/_Layout.cshtml"; } @section css{ <link href="~/css/site.min.css" rel="stylesheet" asp-append-version="true" /> <link href="~/css/goods.css" rel="stylesheet" asp-append-version="true" /> } <div id="container"> <form class="layui-form form-cus form-back" action="" id="app" lay-filter="column-edit"> <div class="panel-body"> <div class="panel-addpic"> <div class="text">Basic properties</div> <div class="form-cur-wall"> <label>Classification</label> <div class="layui-input-block"> <select name="categoryId" id="categoryId" lay-search=""> <option value="0">Parent</option> </select> </div> </div> <div class="form-cur-wall"> <label>Initial sales</label> <div class="layui-input-block"> <input type="text" name="salesInitial" maxlength="100" autocomplete="off" class="layui-input"> </div> </div> <div class="form-cur-wall"> <label>Commodity status</label> <div class="layui-input-block"> <input type="radio" name="goodsStatus" lay-skin="primary" value="10" title="Put on the shelf" /> <input type="radio" name="goodsStatus" lay-skin="primary" value="20" title="Off the shelf" /> </div> </div> <div class="form-cur-wall"> <label>Freight template</label> @*At present, the template of dictionary table is called*@ <div class="layui-input-block"> <select name="deliveryId" id="deliveryId" lay-verify="required" lay-search="" maxlength="100"> @if (ViewBag.Freights != null) { foreach (var item in (List<Config>)ViewBag.Freights) { <option value="@item.Id">@item.Name</option> } } </select> </div> </div> </div> <div class="layui-row"> <div class="layui-form-item"> <label class="layui-form-label" required>Trade name</label> <div class="layui-input-block"> <input type="text" name="name" maxlength="100" lay-verify="required" lay-verType="tips" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label required">Product picture</label> <div class="layui-upload"> <button type="button" class="layui-btn" id="btnUploadShowImg">Upload pictures</button> <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;margin-left:110px;"> Preview: <div class="layui-upload-list" id="demo2"></div> </blockquote> </div> </div> <hr /> <div class="layui-form-item"> <label class="layui-form-label" required>Commodity specifications</label> <div class="layui-input-block"> <input type="radio" name="specType" lay-skin="primary" value="10" lay-filter="specType" title="Single specification" class="layui-input" checked/> <input type="radio" name="specType" lay-skin="primary" value="20" lay-filter="specType" title="Multi specification" class="layui-input" /> </div> </div> <div class="goods-spec-many am-form-group"> <div class="goods-spec-box am-u-sm-9 am-u-sm-push-2 am-u-end"> <!-- Specification properties --> <div class="spec-attr"></div> <!-- Add specification: button --> <div class="spec-group-button"> <button type="button" class="btn-addSpecGroup am-btn">Add specification</button> </div> <!-- Add specifications: Forms --> <div class="spec-group-add"> <div class="spec-group-add-item am-form-group"> <label class="am-form-label form-require">Specification name </label> <input type="text" class="input-specName tpl-form-input" placeholder="Please enter the specification name"> </div> <div class="spec-group-add-item am-form-group"> <label class="am-form-label form-require">Specification value </label> <input type="text" class="input-specValue tpl-form-input" placeholder="Please enter the specification value"> </div> <div class="spec-group-add-item am-margin-top"> <button type="button" class="btn-addSpecName am-btn am-btn-xs am-btn-secondary"> determine </button> <button type="button" class="btn-cancleAddSpecName am-btn am-btn-xs am-btn-default"> cancel </button> </div> </div> <!-- Multiple specifications of goods sku information --> <div class="goods-sku am-scrollable-horizontal"> <!-- Split line --> <div class="goods-spec-line am-margin-top-lg am-margin-bottom-lg"></div> <!-- sku Batch settings --> <div class="spec-batch am-form-inline"> <div class="am-form-group"> <label class="am-form-label">Batch settings</label> </div> <div class="am-form-group"> <input type="text" data-type="goods_no" placeholder="Merchant code"> </div> <div class="am-form-group"> <input type="number" data-type="goods_price" placeholder="Selling price"> </div> <div class="am-form-group"> <input type="number" data-type="line_price" placeholder="Scribing price"> </div> <div class="am-form-group"> <input type="number" data-type="stock_num" placeholder="Inventory quantity"> </div> <div class="am-form-group"> <input type="number" data-type="goods_weight" placeholder="weight"> </div> <div class="am-form-group"> <button type="button" class="btn-specBatchBtn am-btn am-btn-sm am-btn-secondary am-radius"> determine </button> </div> </div> <!-- sku table --> <table class="spec-sku-tabel am-table am-table-bordered am-table-centered am-margin-bottom-xs am-text-nowrap"></table> </div> </div> </div> <div id="sigleSpec"> <div class="layui-form-item"> <label class="layui-form-label" required>commodity price</label> <div class="layui-input-block"> <input type="text" name="goodsPrice" maxlength="30" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label" required>Crossed price of goods</label> <div class="layui-input-block"> <input type="text" name="linePrice" maxlength="40" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">Commodity code</label> <div class="layui-input-block"> <input type="text" name="goodsNo" maxlength="40" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label" required>Inventory quantity</label> <div class="layui-input-block"> <input type="number" name="stockNum" maxlength="40" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label" required>Commodity weight(Kg)</label> <div class="layui-input-block"> <input type="number" name="goodsWeight" maxlength="40" class="layui-input"> </div> </div> </div> <div class="layui-form-item"> <label class="layui-form-label" required>Inventory calculation</label> <div class="layui-input-block"> <input type="radio" name="deductStockType" value="10" lay-skin="primary" title="Reduce inventory under order" /> <input type="radio" name="deductStockType" value="20" lay-skin="primary" title="Payment minus inventory" /> </div> </div> </div> <div class="layui-row"> <div class="layui-form-item layui-form-text"> <label class="layui-form-label">Product details</label> <div class="layui-input-block"> <textarea id="content" name="content" placeholder="Please enter the content" class="layui-textarea"></textarea> </div> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">Confirm save</button> </div> </div> </div> </form> </div> @section js{ <script src="/lib/tinymce/tinymce.min.js"></script> <script src="/lib/tinymce/langs/zh_CN.js"></script> <script src="~/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script> <script src="~/js/goodsSpec.js" asp-append-version="true"></script> <script src="~/js/art-template.js" asp-append-version="true"></script> <script src="~/js/imgUpload.js" asp-append-version="true"></script> <!-- Product multi specification template --> @await Html.PartialAsync("~/Views/Shared/Templates/tpl_spec_many.cshtml") <script> tinymce.init({ selector: '#content', auto_focus: true, height: 500, content_style: "img {max-width:100%;}", image_advtab: true,//Enable advanced options for image 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, prevent ID Covered var parentIndex = parent.layer.getFrameIndex(window.name); apiUtil.BindParentCategory(); form.render(); // Register product multi specification components var specMany = new GoodsSpec({ container: '.goods-spec-many' }); //Processing sheet/Multi specification display problem 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 DOM object of radio }); //Listening submission 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('Commodity specification 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 product picture 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 directly see the source code for more details. In addition, it's not too much to write a series about the design of the mall. Next time, I'll take the time to write an article to introduce the multi specifications of goods and how to design better.
Source address: https://gitee.com/shenniu_code_group/shen-nius.-modularity
People who like to communicate enter wechat group