極簡實用的Asp.NetCore框架再新增商城模組

realyrare發表於2021-10-28

概述

關於這個框架的背景,在前面我已經交代過了。不清楚的可以檢視這個連結 

1、極簡實用的Asp.NetCore模組化框架決定免費開源了

2、極簡實用的Asp.NetCore模組化框架新增CMS模組

算下來確實好長時間沒更新部落格了,在這段時間內一直在出差,閒暇時間一直在想dotnetcore框架本身就是模組化的,為什麼還要在這個上層應用上面繼續進行模組化封裝,意義何在?是為了更好地劃分業務還是輪子重複利用?

細細想來,這個框架不應該再繼續模組化下去,主要有以下幾點理由支援:

1、本身於我現有地業務而言,沒必要模組化,我只是做個大而全地系統(許可權管理,內容管理,商城,微信管理等)。

2、如果要做模組化,本身就要犧牲掉一些效能,這是我反反覆覆斟酌以後不能接受的,主要是犧牲效能有點多!

3、dotnetcore本身就更友好模組化,沒必要在這個上層應用上面再包裹一層,沒有任何意義,我下載了dotnetcore原始碼後,覺得它的設計理念特別棒,於是“dotnetcore”本身就是最好的模組化(元件化)框架,可以把很多時間和精力投身於原始碼上面研究,沒有必要在糾結於模組化這個概念,在上層應用折騰來折騰去,對於技術的成長微乎其微。

4、以前劃分為模組化,是想朝微服務的方向發展,到時儘量改動小一點。現在想想我就一個後臺管理,沒必要想那麼多。

  基於以上幾點,我於是把框架進行了更改,對於原來的模組化框架也進行了分支保留(一定意義上來說也不全是模組化)。

新增業務功能

新增加一個商城模組,主要包含商品管理(支援多個sku),商品分類,小程式使用者,使用者收穫地址、訂單各種狀態的列表。

 

 關於新增和修改商品的部分後端程式碼:

 public async Task<ApiResult> AddAsync(GoodsInput input)
        {
            try
            {
                Db.BeginTran();
                // 儲存商品
                var goods = _mapper.Map<Goods>(input);
                var goodsId = await Db.Insertable<Goods>(goods).ExecuteReturnIdentityAsync();
                // 儲存規格
                await DealwithGoodsSpec(goodsId, input);
                Db.CommitTran();
            }
            catch (Exception e)
            {
                Db.RollbackTran();
                return new ApiResult(e.Message);
            }
            return new ApiResult();
        }
        /// <summary>
        /// 公共的商品規格資訊處理
        /// </summary>
        /// <param name="goodsId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        private async Task DealwithGoodsSpec(int goodsId, GoodsInput input)
        {
            // 儲存規格
            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("商品規格實體資料不能為空!");
                }
                await Db.Insertable(goodsSpec).ExecuteReturnIdentityAsync();
            }
            else
            {
                var goodsSpecs = input.BuildGoodsSpecs(goodsId);
                if (null == goodsSpecs || goodsSpecs.Count == 0)
                {
                    throw new FriendlyException("商品規格實體資料集合不能為空!");
                }
                await Db.Insertable(goodsSpecs).ExecuteReturnIdentityAsync();
                var goodsSpecRels = input.BuildGoodsSpecRels(goodsId);
                if (goodsSpecRels.Count == 0 || goodsSpecRels == null)
                {
                    throw new FriendlyException("商品規格實體關係集合資料不能為空!");
                }
                //根據規格值反推規格組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($"此商品{input.Id}沒有查詢對應的商品資訊");
            try
            {
                Db.BeginTran();
                // 更新商品
                var model = _mapper.Map<Goods>(input);
                var goodsId = await Db.Updateable(model).IgnoreColumns(d => new { d.CreateTime }).ExecuteCommandAsync();
                // 更新規格 
                await Db.Deleteable<GoodsSpec>().Where(d => d.GoodsId == input.Id).ExecuteCommandAsync();
                await Db.Deleteable<GoodsSpecRel>().Where(d => d.GoodsId == input.Id).ExecuteCommandAsync();
                // 儲存規格
                await DealwithGoodsSpec(input.Id, input);
                Db.CommitTran();
            }
            catch (Exception e)
            {
                Db.RollbackTran();
                return new ApiResult(e.Message);
            }
            return new ApiResult();
        }

前端新增商品部分程式碼:

@{
    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">基本屬性</div>
                <div class="form-cur-wall">
                    <label>所屬分類</label>
                    <div class="layui-input-block">
                        <select name="categoryId" id="categoryId" lay-search="">
                            <option value="0">父級</option>
                        </select>
                    </div>
                </div>
                <div class="form-cur-wall">
                    <label>初始銷量</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>商品狀態</label>
                    <div class="layui-input-block">
                        <input type="radio" name="goodsStatus" lay-skin="primary" value="10" title="上架" />
                        <input type="radio" name="goodsStatus" lay-skin="primary" value="20" title="下架" />
                    </div>
                </div>
                <div class="form-cur-wall">
                    <label>運費模板</label>
                    @*目前現呼叫字典表的模板*@
                    <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>商品名稱</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">商品圖片</label>                    
                    <div class="layui-upload">
                        <button type="button" class="layui-btn" id="btnUploadShowImg">上傳圖片</button>
                        <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;margin-left:110px;">
                            預覽圖:
                            <div class="layui-upload-list" id="demo2"></div>
                        </blockquote>
                    </div>
                </div>

                <hr />

                <div class="layui-form-item">
                    <label class="layui-form-label" required>商品規格</label>
                    <div class="layui-input-block">
                        <input type="radio" name="specType" lay-skin="primary" value="10" lay-filter="specType" title="單規格" class="layui-input" checked/>
                        <input type="radio" name="specType" lay-skin="primary" value="20" lay-filter="specType" title="多規格" 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">
                        <!-- 規格屬性 -->
                        <div class="spec-attr"></div>

                        <!-- 新增規格:按鈕 -->
                        <div class="spec-group-button">
                            <button type="button" class="btn-addSpecGroup am-btn">新增規格</button>
                        </div>

                        <!-- 新增規格:表單 -->
                        <div class="spec-group-add">
                            <div class="spec-group-add-item am-form-group">
                                <label class="am-form-label form-require">規格名 </label>
                                <input type="text" class="input-specName tpl-form-input" placeholder="請輸入規格名稱">
                            </div>
                            <div class="spec-group-add-item am-form-group">
                                <label class="am-form-label form-require">規格值 </label>
                                <input type="text" class="input-specValue tpl-form-input" placeholder="請輸入規格值">
                            </div>
                            <div class="spec-group-add-item am-margin-top">
                                <button type="button" class="btn-addSpecName am-btn am-btn-xs
                                            am-btn-secondary">
                                    確定
                                </button>
                                <button type="button" class="btn-cancleAddSpecName am-btn am-btn-xs
                                              am-btn-default">
                                    取消
                                </button>
                            </div>
                        </div>
                        <!-- 商品多規格sku資訊 -->
                        <div class="goods-sku am-scrollable-horizontal">
                            <!-- 分割線 -->
                            <div class="goods-spec-line am-margin-top-lg am-margin-bottom-lg"></div>
                            <!-- sku 批量設定 -->
                            <div class="spec-batch am-form-inline">
                                <div class="am-form-group">
                                    <label class="am-form-label">批量設定</label>
                                </div>
                                <div class="am-form-group">
                                    <input type="text" data-type="goods_no" placeholder="商家編碼">
                                </div>
                                <div class="am-form-group">
                                    <input type="number" data-type="goods_price" placeholder="銷售價">
                                </div>
                                <div class="am-form-group">
                                    <input type="number" data-type="line_price" placeholder="劃線價">
                                </div>
                                <div class="am-form-group">
                                    <input type="number" data-type="stock_num" placeholder="庫存數量">
                                </div>
                                <div class="am-form-group">
                                    <input type="number" data-type="goods_weight" placeholder="重量">
                                </div>
                                <div class="am-form-group">
                                    <button type="button" class="btn-specBatchBtn am-btn am-btn-sm am-btn-secondary
                                                 am-radius">
                                        確定
                                    </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>商品價格</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>商品劃線價</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">商品編碼</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>庫存數量</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>商品重量(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>庫存計算</label>
                    <div class="layui-input-block">
                        <input type="radio" name="deductStockType" value="10" lay-skin="primary" title="下單減庫存" />
                        <input type="radio" name="deductStockType" value="20" lay-skin="primary" title="付款減庫存" />
                    </div>
                </div>
            </div>
            <div class="layui-row">
                <div class="layui-form-item layui-form-text">
                    <label class="layui-form-label">商品詳情</label>
                    <div class="layui-input-block">
                        <textarea id="content" name="content" placeholder="請輸入內容" 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">確認儲存</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>
    <!-- 商品多規格模板 -->
    @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,//開啟圖片上傳的高階選項功能
            images_upload_url: '/api/goods/UploadImg',//圖片上傳
            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;
            // 當前彈出層,防止ID被覆蓋
            var parentIndex = parent.layer.getFrameIndex(window.name);

            apiUtil.BindParentCategory();
            form.render();

            // 註冊商品多規格元件
            var specMany = new GoodsSpec({
                container: '.goods-spec-many'
            });

            //處理單/多規格的顯示問題
            form.on('radio(specType)', function (data) {
                //但規格
                if (data.value == 10) {
                    $("#sigleSpec").show() && $(".goods-spec-many").hide();
                }
                //多規格
                if (data.value == 20) {
                    $("#sigleSpec").hide() && $(".goods-spec-many").show();
                }
                //console.log(data.elem); //得到radio原始DOM物件
            });
          
            //監聽提交
            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('商品規格不能為空');
                        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("商品圖片至少需要上傳一張!");
                    return false;
                }
                apiUtil.ajax('goods/add', data.field, "application/json", "post", function (res) {
                    apiUtil.success(res.msg);
                    parent.layer.close(parentIndex);
                });
                return false;
            });           
        });

    </script>
}

這裡只貼部分程式碼吧,更多的細節可以直接去看原始碼。另外關於商城的設計其實寫個系列也不過分,下次抽時間具體寫篇文章介紹下商品的多規格怎麼設計好一點。

原始碼地址:https://gitee.com/shenniu_code_group/shen-nius.-modularity

喜歡交流的人進微信群

 

 

相關文章