1
0
فهرست منبع

Merge remote-tracking branch 'origin/master' into feature/基础数据bug修复1209

qng 3 سال پیش
والد
کامیت
91773dc180
28فایلهای تغییر یافته به همراه2328 افزوده شده و 22 حذف شده
  1. 14 0
      kmall-admin/pom.xml
  2. 43 0
      kmall-admin/src/main/java/com/kmall/admin/config/AliSMSConfig.java
  3. 90 2
      kmall-admin/src/main/java/com/kmall/admin/controller/CategoryController.java
  4. 10 1
      kmall-admin/src/main/java/com/kmall/admin/controller/OrderProcessRecordExternalController.java
  5. 20 0
      kmall-admin/src/main/java/com/kmall/admin/dao/CategoryDao.java
  6. 24 0
      kmall-admin/src/main/java/com/kmall/admin/dao/aliyunsms/SendSmsRecordDao.java
  7. 338 0
      kmall-admin/src/main/java/com/kmall/admin/dto/CategoryDto.java
  8. 140 0
      kmall-admin/src/main/java/com/kmall/admin/entity/aliyunsms/SendSmsRecord.java
  9. 76 0
      kmall-admin/src/main/java/com/kmall/admin/properties/AliSMSProperties.java
  10. 20 0
      kmall-admin/src/main/java/com/kmall/admin/service/CategoryService.java
  11. 35 0
      kmall-admin/src/main/java/com/kmall/admin/service/aliyunsms/SendSmsRecordService.java
  12. 128 0
      kmall-admin/src/main/java/com/kmall/admin/service/aliyunsms/impl/SendSmsRecordServiceImpl.java
  13. 107 4
      kmall-admin/src/main/java/com/kmall/admin/service/impl/CategoryServiceImpl.java
  14. 27 0
      kmall-admin/src/main/java/com/kmall/admin/task/AliyunSmsTask.java
  15. 285 0
      kmall-admin/src/main/java/com/kmall/admin/utils/SMSUtils.java
  16. 24 0
      kmall-admin/src/main/resources/XmlTemplate/CategoryDtoList.xml
  17. 12 0
      kmall-admin/src/main/resources/conf/aliyun-sms.properties
  18. 35 0
      kmall-admin/src/main/resources/mybatis/mapper/CategoryDao.xml
  19. 69 0
      kmall-admin/src/main/resources/mybatis/mapper/aliyunsms/SendSmsRecordDao.xml
  20. 22 1
      kmall-admin/src/main/webapp/WEB-INF/page/shop/category.html
  21. 3 0
      kmall-admin/src/main/webapp/WEB-INF/page/sys/header.html
  22. 99 4
      kmall-admin/src/main/webapp/js/shop/category.js
  23. 10 10
      kmall-admin/src/main/webapp/js/shop/offilineOrderList.js
  24. BIN
      kmall-admin/src/main/webapp/statics/file/category_yyyy_mm_dd_v1.0.0.xls
  25. 678 0
      kmall-admin/src/main/webapp/statics/plugins/bootstrap-table/bootstrap-paginator.js
  26. 0 0
      kmall-admin/src/main/webapp/statics/plugins/bootstrap-table/bootstrap-paginator.min.js
  27. 3 0
      kmall-common/src/main/java/com/kmall/common/constant/JxlsXmlTemplateName.java
  28. 16 0
      sql/send_sms_record.sql

+ 14 - 0
kmall-admin/pom.xml

@@ -223,6 +223,20 @@
             <version>1.6.1</version>
         </dependency>
 
+        <!--    阿里云核心库    -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>tea-openapi</artifactId>
+            <version>0.0.19</version>
+        </dependency>
+
+        <!--    阿里云短信服务api    -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <version>2.0.8</version>
+        </dependency>
+
 
     </dependencies>
 

+ 43 - 0
kmall-admin/src/main/java/com/kmall/admin/config/AliSMSConfig.java

@@ -0,0 +1,43 @@
+package com.kmall.admin.config;
+
+import com.kmall.admin.properties.AliSMSProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+
+/**
+ * 阿里云短信接口
+ * 获取配置文件参数
+ *
+ * @author zhuhh
+ * @date 2021-12-4 15:27:33
+ */
+@Configuration
+@PropertySource("classpath:conf/aliyun-sms.properties")
+public class AliSMSConfig {
+
+    @Autowired
+    private Environment environment;
+
+    @Bean
+    public AliSMSProperties aliSMSProperties() {
+        String accessKeyId = environment.getProperty("ali.sms.accessKeyId");
+        String accessKeySecret = environment.getProperty("ali.sms.accessKeySecret");
+        String endpoint = environment.getProperty("ali.sms.endpoint");
+        String signName = environment.getProperty("ali.sms.signName");
+        String customTemplateCode = environment.getProperty("ali.sms.customTemplateCode");
+
+        AliSMSProperties aliSMSProperties = new AliSMSProperties();
+        aliSMSProperties.setAccessKeyId(accessKeyId);
+        aliSMSProperties.setAccessKeySecret(accessKeySecret);
+        aliSMSProperties.setEndpoint(endpoint);
+        aliSMSProperties.setSignName(signName);
+        aliSMSProperties.setCustomTemplateCode(customTemplateCode);
+
+        return aliSMSProperties;
+    }
+
+
+}

+ 90 - 2
kmall-admin/src/main/java/com/kmall/admin/controller/CategoryController.java

@@ -1,16 +1,22 @@
 package com.kmall.admin.controller;
 
+import com.kmall.admin.dto.CategoryDto;
 import com.kmall.admin.dto.CopyCategoryDto;
 import com.kmall.admin.dto.StoreDto;
 import com.kmall.admin.dto.StoreIdDto;
 import com.kmall.admin.entity.CategoryEntity;
+import com.kmall.admin.entity.SupplierEntity;
 import com.kmall.admin.service.CategoryService;
 import com.kmall.admin.utils.ParamUtils;
 import com.kmall.common.constant.JxlsXmlTemplateName;
 import com.kmall.common.utils.*;
 import com.kmall.common.utils.excel.ExcelUtil;
+import com.kmall.manager.manager.express.sf.ServiceException;
 import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.jxls.reader.XLSDataReadException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -27,10 +33,14 @@ import java.util.Map;
  * @author Scott
  * @email
  * @date 2017-08-21 15:32:31
+ * @author zhuhh
+ * @date 2021-12-7 16:12:16
  */
 @RestController
 @RequestMapping("category")
 public class CategoryController {
+    private static final Log logger = LogFactory.getLog(CategoryController.class);
+
     @Autowired
     private CategoryService categoryService;
     @Autowired
@@ -38,19 +48,51 @@ public class CategoryController {
 
     /**
      * 查看列表
+     *
+     * @author zhuhh
+     * @param params
      */
     @RequestMapping("/list")
     @RequiresPermissions("category:list")
     public R list(@RequestParam Map<String, Object> params) {
 //        ParamUtils.setQueryPowerByRoleType(params, "storeId", "merchSn", "thirdPartyMerchCode");
 //        ParamUtils.setName(params, "categoryName");
-        //查询列表数据
+        // 查询所有一级节点数据
         Query query = new Query(params);
+        query.put("parentId", "0");
 
         List<CategoryEntity> categoryList = categoryService.queryList(query);
+
+        // 获取父id
+        List<Integer> parentIdList = new ArrayList<>();
+         for (CategoryEntity c : categoryList) {
+             parentIdList.add(c.getId());
+        }
+
+        // 获取对应的所有子节点
+        List<CategoryEntity> categoryChildList = categoryService.queryListByParentId(parentIdList);
+        categoryList.addAll(categoryChildList);
+
+        return R.ok().put("list", categoryList);
+    }
+
+    /**
+     * 获取页数和总数
+     *
+     * @author zhuhh
+     * @param params
+     * @return R
+     */
+    @RequestMapping("/queryTotal")
+    @RequiresPermissions("category:list")
+    public R queryTotal(@RequestBody Map<String, Object> params) {
+        // 查询列表数据
+        Query query = new Query(params);
+        query.put("parentId", "0");
+
         int total = categoryService.queryTotal(query);
 
-        PageUtils pageUtil = new PageUtils(categoryList, total, query.getLimit(), query.getPage());
+        PageUtils pageUtil = new PageUtils(null, total, query.getLimit(), query.getPage());
 
         return R.ok().put("page", pageUtil);
     }
@@ -221,4 +263,50 @@ public class CategoryController {
         return R.ok("刷新成功!");
     }
 
+    /**
+     * 导入excel
+     *
+     * @param file
+     * @return
+     */
+    @RequestMapping("/upload")
+    public R upload(@RequestParam("file") MultipartFile file) {
+        List<CategoryDto> categoryDtoList = new ArrayList<>();
+        // 读取 excel 数据
+        try {
+            CategoryDto categoryDto = new CategoryDto();
+            Map<String, Object> beans = new HashMap<>();
+            beans.put("categoryDto", categoryDto);
+            beans.put("categoryDtoList", categoryDtoList);
+            if (file.isEmpty()) {
+                return R.error("文件不能为空!");
+            }
+            excelUtil.readExcel(JxlsXmlTemplateName.CATEGORY_DTO_LIST, beans, file.getInputStream());
+        } catch (XLSDataReadException e) {
+            logger.error("商品类型读取excel失败:" + e.getMessage());
+            return R.error("导入失败!请在单元格输入正确的数据格式");
+        } catch (Exception e) {
+            logger.error("商品类型读取excel失败:" + e.getMessage());
+            return R.error("读取excel数据失败!");
+        }
+        // 新增数据
+        try {
+            if (categoryDtoList == null || categoryDtoList.size() == 0) {
+                return R.error("读取excel失败!单元格内容不能为空!");
+            }
+
+            categoryService.uploadExcel(categoryDtoList);
+        } catch (ServiceException e) {
+            logger.error("商品类型数据格式校验失败:" + e.getMessage());
+            return R.error(e.getMessage());
+        } catch (RRException e) {
+            logger.error("商品类型添加数据失败:" + e.getMsg());
+            return R.error(e.getMsg());
+        } catch (Exception e) {
+            logger.error("商品类型添加数据失败:" + e.getMessage());
+            return R.error("数据保存数据库失败!");
+        }
+        return R.ok("导入成功!");
+    }
+
 }

+ 10 - 1
kmall-admin/src/main/java/com/kmall/admin/controller/OrderProcessRecordExternalController.java

@@ -8,6 +8,7 @@ import com.kmall.admin.fromcomm.service.SysConfigService;
 import com.kmall.admin.haikong.utils.Message;
 import com.kmall.admin.haikong.utils.OutRequestMessage;
 import com.kmall.admin.service.OrderProcessRecordService;
+import com.kmall.admin.service.aliyunsms.SendSmsRecordService;
 import com.kmall.admin.utils.jackson.JacksonUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,6 +37,8 @@ public class OrderProcessRecordExternalController {
     private OrderProcessRecordService orderProcessRecordService;
     @Autowired
     private SysConfigService sysConfigService;
+    @Autowired
+    private SendSmsRecordService sendSmsRecordService;
 
     /**
      * 订单流转信息回调通知接口
@@ -68,7 +71,13 @@ public class OrderProcessRecordExternalController {
             if(num>0L){
                 return Message.error("订单数据存在重复");
             }
-            return orderProcessRecordService.saveHkNoticeMsg(orderProcessRecordList,outRequestMessage);
+
+            Message message = orderProcessRecordService.saveHkNoticeMsg(orderProcessRecordList,outRequestMessage);
+
+            // 发送清关成功的订单的短信
+            sendSmsRecordService.sendCustomClearSuccessMsg(orderProcessRecordList);
+
+            return message;
         }catch (Exception e){
             logger.error("hknoticeMsg---订单流转信息回调通知接口数据="+ JSON.toJSONString(outRequestMessage),e);
             return Message.error("参数错误");

+ 20 - 0
kmall-admin/src/main/java/com/kmall/admin/dao/CategoryDao.java

@@ -1,5 +1,6 @@
 package com.kmall.admin.dao;
 
+import com.kmall.admin.dto.CategoryDto;
 import com.kmall.admin.dto.Mall2RulesDto;
 import com.kmall.admin.entity.CategoryEntity;
 import com.kmall.admin.entity.vip.Mall2DetilEntity;
@@ -30,4 +31,23 @@ public interface CategoryDao extends BaseDao<CategoryEntity> {
     Long queryObjectCategoryByName(String trim);
 
     void updateObjectCategory(List<Mall2RulesDto> pointsRulesList);
+
+    /**
+     * 根据父id查询子对象
+     *
+     * @author zhuhh
+     * @param parentId
+     * @return
+     */
+    List<CategoryEntity> queryListByParentId(List<Integer> parentId);
+
+    /**
+     * 根据名称和级别查询是否存在对象
+     *
+     * @param name
+     * @param level
+     * @return
+     */
+    CategoryDto queryExistByNameAndLevel(@Param("name") String name, @Param("level") String level);
+
 }

+ 24 - 0
kmall-admin/src/main/java/com/kmall/admin/dao/aliyunsms/SendSmsRecordDao.java

@@ -0,0 +1,24 @@
+package com.kmall.admin.dao.aliyunsms;
+
+import com.kmall.admin.entity.aliyunsms.SendSmsRecord;
+import com.kmall.manager.dao.BaseDao;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 短信发送记录表Dao
+ *
+ * @author zhuhh
+ * @date 2021-12-8 18:13:11
+ */
+public interface SendSmsRecordDao extends BaseDao<SendSmsRecord> {
+
+    /**
+     * 根据发送状态查询
+     *
+     * @param sendStatus 发送状态
+     */
+    List<SendSmsRecord> queryBySendStatus(@Param("sendStatus") Integer sendStatus);
+
+}

+ 338 - 0
kmall-admin/src/main/java/com/kmall/admin/dto/CategoryDto.java

@@ -0,0 +1,338 @@
+package com.kmall.admin.dto;
+
+import com.kmall.manager.entity.Tree;
+
+/**
+ * category导入excel实体
+ *
+ * 表名 mall_category
+ *
+ * @author zhuhh
+ * @email
+ * @date 2021-12-8 15:02:08
+ */
+public class CategoryDto extends Tree<CategoryDto> {
+
+    //主键
+    private Integer id;
+    //分类名称
+    private String name;
+    //关键字
+    private String keywords;
+    //描述
+    private String frontDesc;
+    //父节点
+    private Integer parentId;
+    //父分类名称
+    private String parentName;
+    //排序
+    private Integer sortOrder;
+    //首页展示
+    private Integer showIndex;
+    //显示
+    private Integer isShow;
+    //banner图片
+    private String bannerUrl;
+    //icon链接
+    private String iconUrl;
+    //图片
+    private String imgUrl;
+    //手机banner
+    private String wapBannerUrl;
+    //级别
+    private String level;
+    //类型
+    private Integer type;
+    //
+    private String frontName;
+
+    //翻译用字段
+    private String show;
+
+    private Integer storeId;
+
+    private String merchSn;
+
+    private String storeName;
+    private String merchName;
+
+    private String share;
+
+    //积分生成规则id
+    private Long pointsRulesId;
+
+    public String getStoreName() {
+        return storeName;
+    }
+
+    public void setStoreName(String storeName) {
+        this.storeName = storeName;
+    }
+
+    public String getMerchName() {
+        return merchName;
+    }
+
+    public void setMerchName(String merchName) {
+        this.merchName = merchName;
+    }
+
+    public Integer getStoreId() {
+        return storeId;
+    }
+
+    public void setStoreId(Integer storeId) {
+        this.storeId = storeId;
+    }
+
+    public String getMerchSn() {
+        return merchSn;
+    }
+
+    public void setMerchSn(String merchSn) {
+        this.merchSn = merchSn;
+    }
+
+    public String getShow() {
+        return show;
+    }
+
+    public void setShow(String show) {
+        this.show = show;
+    }
+
+    /**
+     * 设置:主键
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取:主键
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * 设置:分类名称
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * 获取:分类名称
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * 设置:关键字
+     */
+    public void setKeywords(String keywords) {
+        this.keywords = keywords;
+    }
+
+    /**
+     * 获取:关键字
+     */
+    public String getKeywords() {
+        return keywords;
+    }
+
+    /**
+     * 设置:描述
+     */
+    public void setFrontDesc(String frontDesc) {
+        this.frontDesc = frontDesc;
+    }
+
+    /**
+     * 获取:描述
+     */
+    public String getFrontDesc() {
+        return frontDesc;
+    }
+
+    /**
+     * 设置:父节点
+     */
+    public void setParentId(Integer parentId) {
+        this.parentId = parentId;
+    }
+
+    /**
+     * 获取:父节点
+     */
+    public Integer getParentId() {
+        return parentId;
+    }
+
+    /**
+     * 设置:排序
+     */
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+
+    /**
+     * 获取:排序
+     */
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+
+    /**
+     * 设置:首页展示
+     */
+    public void setShowIndex(Integer showIndex) {
+        this.showIndex = showIndex;
+    }
+
+    /**
+     * 获取:首页展示
+     */
+    public Integer getShowIndex() {
+        return showIndex;
+    }
+
+    /**
+     * 设置:显示
+     */
+    public void setIsShow(Integer isShow) {
+        this.isShow = isShow;
+    }
+
+    /**
+     * 获取:显示
+     */
+    public Integer getIsShow() {
+        return isShow;
+    }
+
+    /**
+     * 设置:banner图片
+     */
+    public void setBannerUrl(String bannerUrl) {
+        this.bannerUrl = bannerUrl;
+    }
+
+    /**
+     * 获取:banner图片
+     */
+    public String getBannerUrl() {
+        return bannerUrl;
+    }
+
+    /**
+     * 设置:icon链接
+     */
+    public void setIconUrl(String iconUrl) {
+        this.iconUrl = iconUrl;
+    }
+
+    /**
+     * 获取:icon链接
+     */
+    public String getIconUrl() {
+        return iconUrl;
+    }
+
+    /**
+     * 设置:图片
+     */
+    public void setImgUrl(String imgUrl) {
+        this.imgUrl = imgUrl;
+    }
+
+    /**
+     * 获取:图片
+     */
+    public String getImgUrl() {
+        return imgUrl;
+    }
+
+    /**
+     * 设置:手机banner
+     */
+    public void setWapBannerUrl(String wapBannerUrl) {
+        this.wapBannerUrl = wapBannerUrl;
+    }
+
+    /**
+     * 获取:手机banner
+     */
+    public String getWapBannerUrl() {
+        return wapBannerUrl;
+    }
+
+    /**
+     * 设置:级别
+     */
+    public void setLevel(String level) {
+        this.level = level;
+    }
+
+    /**
+     * 获取:级别
+     */
+    public String getLevel() {
+        return level;
+    }
+
+    /**
+     * 设置:类型
+     */
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    /**
+     * 获取:类型
+     */
+    public Integer getType() {
+        return type;
+    }
+
+    /**
+     * 设置:
+     */
+    public void setFrontName(String frontName) {
+        this.frontName = frontName;
+    }
+
+    /**
+     * 获取:
+     */
+    public String getFrontName() {
+        return frontName;
+    }
+
+    public String getShare() {
+        return share;
+    }
+
+    public void setShare(String share) {
+        this.share = share;
+    }
+
+    public Long getPointsRulesId() {
+        return pointsRulesId;
+    }
+
+    public void setPointsRulesId(Long pointsRulesId) {
+        this.pointsRulesId = pointsRulesId;
+    }
+
+    public String getParentName() {
+        return parentName;
+    }
+
+    public void setParentName(String parentName) {
+        this.parentName = parentName;
+    }
+
+}

+ 140 - 0
kmall-admin/src/main/java/com/kmall/admin/entity/aliyunsms/SendSmsRecord.java

@@ -0,0 +1,140 @@
+package com.kmall.admin.entity.aliyunsms;
+
+import java.util.Date;
+
+/**
+ * 短信发送记录表
+ * 
+ * @author zhuhh
+ * @date 2021-12-8 17:56:44
+ */
+public class SendSmsRecord {
+
+    private Integer id;
+
+    /**
+     * 订单编号
+     */
+    private String orderSn;
+
+    /**
+     * 清单编号
+     */
+    private String clearNo;
+
+    /**
+     * 手机号码
+     */
+    private String phoneNumbers;
+
+    /**
+     * 发送状态
+     */
+    private Integer sendStatus;
+
+    /**
+     * 发送失败原因
+     */
+    private String sendFailMsg;
+
+    /**
+     * 请求id
+     */
+    private String requestId;
+
+    /**
+     * 回执id
+     */
+    private String bizId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getOrderSn() {
+        return orderSn;
+    }
+
+    public void setOrderSn(String orderSn) {
+        this.orderSn = orderSn;
+    }
+
+    public String getClearNo() {
+        return clearNo;
+    }
+
+    public void setClearNo(String clearNo) {
+        this.clearNo = clearNo;
+    }
+
+    public String getPhoneNumbers() {
+        return phoneNumbers;
+    }
+
+    public void setPhoneNumbers(String phoneNumbers) {
+        this.phoneNumbers = phoneNumbers;
+    }
+
+    public Integer getSendStatus() {
+        return sendStatus;
+    }
+
+    public void setSendStatus(Integer sendStatus) {
+        this.sendStatus = sendStatus;
+    }
+
+    public String getSendFailMsg() {
+        return sendFailMsg;
+    }
+
+    public void setSendFailMsg(String sendFailMsg) {
+        this.sendFailMsg = sendFailMsg;
+    }
+
+    public String getRequestId() {
+        return requestId;
+    }
+
+    public void setRequestId(String requestId) {
+        this.requestId = requestId;
+    }
+
+    public String getBizId() {
+        return bizId;
+    }
+
+    public void setBizId(String bizId) {
+        this.bizId = bizId;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+}

+ 76 - 0
kmall-admin/src/main/java/com/kmall/admin/properties/AliSMSProperties.java

@@ -0,0 +1,76 @@
+package com.kmall.admin.properties;
+
+
+/**
+ * 阿里云短信接口参数类
+ *
+ * @author zhuhh
+ * @date 2021-12-4 15:26:35
+ */
+public class AliSMSProperties {
+
+    /**
+     * AccessKey ID
+     */
+    private String accessKeyId;
+
+    /**
+     * AccessKey Secret
+     */
+    private String accessKeySecret;
+
+    /**
+     * 访问的域名
+     */
+    private String endpoint;
+
+    /**
+     * 短信签名名称
+     */
+    private String signName;
+
+    /**
+     * 清关短信模板CODE
+     */
+    private String customTemplateCode;
+
+    public String getAccessKeyId() {
+        return accessKeyId;
+    }
+
+    public void setAccessKeyId(String accessKeyId) {
+        this.accessKeyId = accessKeyId;
+    }
+
+    public String getAccessKeySecret() {
+        return accessKeySecret;
+    }
+
+    public void setAccessKeySecret(String accessKeySecret) {
+        this.accessKeySecret = accessKeySecret;
+    }
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getSignName() {
+        return signName;
+    }
+
+    public void setSignName(String signName) {
+        this.signName = signName;
+    }
+
+    public String getCustomTemplateCode() {
+        return customTemplateCode;
+    }
+
+    public void setCustomTemplateCode(String customTemplateCode) {
+        this.customTemplateCode = customTemplateCode;
+    }
+}

+ 20 - 0
kmall-admin/src/main/java/com/kmall/admin/service/CategoryService.java

@@ -1,6 +1,7 @@
 package com.kmall.admin.service;
 
 import com.kmall.admin.dto.CateStoreDto;
+import com.kmall.admin.dto.CategoryDto;
 import com.kmall.admin.dto.CopyCategoryDto;
 import com.kmall.admin.entity.CategoryEntity;
 
@@ -77,4 +78,23 @@ public interface CategoryService {
     int saveCopyCategory(CopyCategoryDto copyCategoryDto);
 
     void refreshBrandCache();
+
+    /**
+     * 根据父id获取所有子对象
+     *
+     * @author zhuhh
+     * @param parentId 父id,多个以逗号分割
+     * @return List
+     */
+    List<CategoryEntity> queryListByParentId(List<Integer> parentId);
+
+
+    /**
+     * 导入excel
+     *
+     * @author zhuhh
+     * @param categoryDtoList
+     */
+    void uploadExcel(List<CategoryDto> categoryDtoList);
+
 }

+ 35 - 0
kmall-admin/src/main/java/com/kmall/admin/service/aliyunsms/SendSmsRecordService.java

@@ -0,0 +1,35 @@
+package com.kmall.admin.service.aliyunsms;
+
+import com.kmall.admin.entity.OrderProcessRecordHkNewEntity;
+import com.kmall.admin.entity.aliyunsms.SendSmsRecord;
+
+import java.util.List;
+
+/**
+ * 短信发送记录表Service
+ *
+ * @author zhuhh
+ * @date 2021-12-8 18:03:00
+ */
+public interface SendSmsRecordService {
+
+    /**
+     * 根据发送状态查询
+     *
+     * @param sendStatus 发送状态
+     */
+    List<SendSmsRecord> queryBySendStatus(Integer sendStatus);
+
+    /**
+     * 发送清关成功的短信
+     *
+     * @param orderProcessRecordList
+     */
+    void sendCustomClearSuccessMsg(List<OrderProcessRecordHkNewEntity> orderProcessRecordList);
+
+    /**
+     * 重新发送之前发送失败的短信
+     */
+    void reSendFailMsg();
+
+}

+ 128 - 0
kmall-admin/src/main/java/com/kmall/admin/service/aliyunsms/impl/SendSmsRecordServiceImpl.java

@@ -0,0 +1,128 @@
+package com.kmall.admin.service.aliyunsms.impl;
+
+import com.kmall.admin.dao.aliyunsms.SendSmsRecordDao;
+import com.kmall.admin.entity.OrderEntity;
+import com.kmall.admin.entity.OrderProcessRecordHkNewEntity;
+import com.kmall.admin.entity.aliyunsms.SendSmsRecord;
+import com.kmall.admin.properties.AliSMSProperties;
+import com.kmall.admin.service.OrderService;
+import com.kmall.admin.service.aliyunsms.SendSmsRecordService;
+import com.kmall.admin.utils.SMSUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 短信发送记录表Service实现类
+ *
+ * @author zhuhh
+ * @date 2021-12-8 18:03:00
+ */
+@Service
+public class SendSmsRecordServiceImpl implements SendSmsRecordService {
+    private static final Logger logger = LoggerFactory.getLogger(SendSmsRecordServiceImpl.class);
+
+    @Autowired
+    private SendSmsRecordDao sendSmsRecordDao;
+
+    @Autowired
+    private AliSMSProperties aliSMSProperties;
+
+    @Autowired
+    private OrderService orderService;
+
+    @Override
+    public List<SendSmsRecord> queryBySendStatus(Integer sendStatus) {
+        return sendSmsRecordDao.queryBySendStatus(sendStatus);
+    }
+
+    @Override
+    public void sendCustomClearSuccessMsg(List<OrderProcessRecordHkNewEntity> orderProcessRecordList) {
+        try {
+            // 判断清关状态
+            for (OrderProcessRecordHkNewEntity entity : orderProcessRecordList) {
+                // 清关成功
+                if ("1".equals(entity.getClearType())) {
+                    // 获取对应的订单实体
+                    OrderEntity orderEntity = orderService.queryObjectByOrderSn(entity.getOrderSn());
+
+                    if (orderEntity == null) {
+                        String errorInfo = "订单数据不存在,不存在的订单编号:" + entity.getOrderSn();
+                        throw new Exception(errorInfo);
+                    }
+
+                    // 发送短信
+                    Map<String, Object> params = new HashMap<>(2);
+                    params.put("phoneNumbers", orderEntity.getPayMobile());
+                    Map<String, Object> resp = SMSUtils.sendSms(aliSMSProperties, params);
+
+                    // 添加参数
+                    SendSmsRecord smsRecord = new SendSmsRecord();
+                    smsRecord.setOrderSn(entity.getOrderSn());
+                    smsRecord.setClearNo(entity.getClearNo());
+                    smsRecord.setPhoneNumbers(orderEntity.getPayMobile());
+                    if ("OK".equals(resp.get("code").toString())) {
+                        smsRecord.setSendStatus(1);
+                    } else {
+                        smsRecord.setSendStatus(0);
+                        smsRecord.setSendFailMsg(resp.get("message").toString());
+                    }
+                    smsRecord.setBizId(resp.get("bizId").toString());
+                    smsRecord.setRequestId(resp.get("requestId").toString());
+                    smsRecord.setCreateTime(new Date());
+                    smsRecord.setUpdateTime(smsRecord.getCreateTime());
+
+                    // 添加短信发送记录
+                    sendSmsRecordDao.save(smsRecord);
+                }
+            }
+
+        } catch (RuntimeException e) {
+            logger.error("发送清关成功的短信失败:", e);
+        } catch (Exception e) {
+            logger.error("发送清关成功的短信失败:", e);
+        }
+    }
+
+    @Override
+    public void reSendFailMsg() {
+        List<SendSmsRecord> sendSmsRecordList = sendSmsRecordDao.queryBySendStatus(0);
+
+        try {
+            for (SendSmsRecord smsRecord : sendSmsRecordList) {
+                // 发送短信
+                Map<String, Object> params = new HashMap<>(2);
+                params.put("phoneNumbers", smsRecord.getPhoneNumbers());
+                // Map<String, Object> resp = SMSUtils.sendSms(aliSMSProperties, params);
+                Map<String, Object> resp = new HashMap<>();
+                resp.put("code", "1");
+                resp.put("message", "sdfs1");
+                resp.put("bizId", "asdasd");
+                resp.put("requestId", "asd");
+
+                // 添加参数
+                if ("OK".equals(resp.get("code").toString())) {
+                    smsRecord.setSendStatus(1);
+                    smsRecord.setSendFailMsg("");
+                } else {
+                    smsRecord.setSendStatus(0);
+                    smsRecord.setSendFailMsg(resp.get("message").toString());
+                }
+                smsRecord.setBizId(resp.get("bizId").toString());
+                smsRecord.setRequestId(resp.get("requestId").toString());
+                smsRecord.setUpdateTime(new Date());
+
+                // 添加短信发送记录
+                sendSmsRecordDao.update(smsRecord);
+            }
+        } catch (RuntimeException e) {
+            logger.error("发送清关成功的短信失败:", e);
+        } catch (Exception e) {
+            logger.error("发送清关成功的短信失败:", e);
+        }
+    }
+
+}

+ 107 - 4
kmall-admin/src/main/java/com/kmall/admin/service/impl/CategoryServiceImpl.java

@@ -6,17 +6,19 @@ import com.kmall.admin.dao.GoodsDao;
 import com.kmall.admin.dao.ProductStoreRelaDao;
 import com.kmall.admin.dao.StoreDao;
 import com.kmall.admin.dto.CateStoreDto;
+import com.kmall.admin.dto.CategoryDto;
 import com.kmall.admin.dto.CopyCategoryDto;
 import com.kmall.admin.entity.*;
 import com.kmall.admin.service.CategoryService;
+import com.kmall.admin.utils.ValidateUtils;
 import com.kmall.common.constant.Dict;
-import com.kmall.common.utils.MapBeanUtil;
-import com.kmall.common.utils.R;
-import com.kmall.common.utils.RRException;
-import com.kmall.common.utils.ValidatorUtil;
+import com.kmall.common.utils.*;
+import com.kmall.manager.manager.express.sf.ServiceException;
 import com.kmall.manager.manager.redis.JedisUtil;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -299,4 +301,105 @@ public class CategoryServiceImpl implements CategoryService {
         }
 
     }
+
+    @Override
+    public List<CategoryEntity> queryListByParentId(List<Integer> parentId) {
+        return categoryDao.queryListByParentId(parentId);
+    }
+
+    @Override
+    @Transactional
+    public void uploadExcel(List<CategoryDto> categoryDtoList) {
+        int parentId = 0;
+
+        for (CategoryDto categoryDto : categoryDtoList) {
+            // 分类名称
+            String name = categoryDto.getName();
+            // 关键字
+            String keywords = categoryDto.getKeywords();
+            // 描述
+            String frontDesc = categoryDto.getFrontDesc();
+            // 排序
+            Integer sortOrder = categoryDto.getSortOrder();
+            // 是否显示
+            Integer isShow = categoryDto.getIsShow();
+            // 级别
+            String level = categoryDto.getLevel();
+            // 父节点
+            String parentName = categoryDto.getParentName();
+            // 类型
+            Integer type = categoryDto.getType();
+
+            // 校验
+            if (!ValidateUtils.validateSpecialAllowChinese(name)) {
+                throw new ServiceException("请输入正确的分类名称,有问题的分类名称:" + name);
+            }
+            if (StringUtils.isNotBlank(keywords)) {
+                if (!ValidateUtils.validateSpecialAllowChinese(keywords)) {
+                    throw new ServiceException("请输入正确的关键字,有问题的关键字:" + keywords);
+                }
+            }
+            if (!ValidateUtils.validateSpecialAllowChinese(frontDesc)) {
+                throw new ServiceException("请输入正确的描述,有问题的描述:" + frontDesc);
+            }
+            if (sortOrder == null || sortOrder < 0) {
+                throw new ServiceException("请输入正确的排序,有问题的排序:" + sortOrder);
+            }
+            if (isShow < 0 || isShow > 1) {
+                throw new ServiceException("请填写正确的是否显示,有问题的是否显示:" + isShow);
+            }
+            if (!(Dict.Level.item_L1.getItem()).equals(level) && !(Dict.Level.item_L2.getItem()).equals(level)) {
+                throw new ServiceException("请填写正确的级别,有问题的级别:" + level);
+            }
+            if (StringUtils.isNotBlank(parentName)) {
+                if (!ValidateUtils.validateSpecialAllowChinese(parentName)) {
+                    throw new ServiceException("请输入正确的上级分类名称,有问题的上级分类名称:" + parentName);
+                }
+            }
+
+            // 判断分类是否已存在
+            CategoryDto isExist = categoryDao.queryExistByNameAndLevel(name, level);
+            if (isExist != null) {
+                throw new RRException("该级别下的分类名称已存在,已存在的分类名称:" + name);
+            }
+            // 判断上级分类是否已存在
+            if (StringUtils.isNotBlank(parentName)) {
+                // 查询数据库判断有没有
+                CategoryDto isParentExist = categoryDao.queryExistByNameAndLevel(parentName, Dict.Level.item_L1.getItem());
+                if (isParentExist == null) {
+                    // 再去判断excel表是否有对应的上级分类
+                    boolean flag = true;
+                    for (CategoryDto temp : categoryDtoList) {
+                        if (temp.getName().equals(parentName) && Dict.Level.item_L1.getItem().equals(temp.getLevel())) {
+                            flag = false;
+                            break;
+                        }
+                    }
+
+                    if (flag) {
+                        throw new RRException("不存在的父分类名称:" + parentName);
+                    }
+                }
+                // 如果excel表分类的父分类在数据库中,则获取父分类
+                parentId = isParentExist.getId();
+            }
+
+            CategoryEntity categoryEntity = new CategoryEntity();
+            BeanUtils.copyProperties(categoryDto, categoryEntity);
+            // 父分类的添加
+            if (Dict.Level.item_L1.getItem().equals(level)) {
+                categoryEntity.setParentId(0);
+
+                parentId = categoryDao.save(categoryEntity);
+            } else if (Dict.Level.item_L2.getItem().equals(level)) {
+                // 子分类的添加
+                categoryEntity.setParentId(parentId);
+
+                categoryDao.save(categoryEntity);
+            }
+        }
+
+    }
+
+
 }

+ 27 - 0
kmall-admin/src/main/java/com/kmall/admin/task/AliyunSmsTask.java

@@ -0,0 +1,27 @@
+package com.kmall.admin.task;
+
+import com.kmall.admin.service.aliyunsms.SendSmsRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * 阿里云短信定时任务
+ *
+ * @author zhuhh
+ * @date 2021-12-9 11:37:26
+ */
+@Component
+public class AliyunSmsTask {
+    @Autowired
+    private SendSmsRecordService sendSmsRecordService;
+
+    /**
+     * 定时扫描 send_sms_record 表的发送失败短信并重新发送
+     */
+    @Scheduled(fixedRate = 10000)
+    public void reSendFailMsg() {
+       // todo,测试屏蔽
+       // sendSmsRecordService.reSendFailMsg();
+    }
+}

+ 285 - 0
kmall-admin/src/main/java/com/kmall/admin/utils/SMSUtils.java

@@ -0,0 +1,285 @@
+package com.kmall.admin.utils;
+
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.*;
+import com.aliyun.teaopenapi.models.Config;
+import com.kmall.admin.properties.AliSMSProperties;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 阿里云短信工具类
+ *
+ * @author zhuhh
+ * @date 2021-12-4 14:44:56
+ */
+public class SMSUtils {
+    private static Log logger = LogFactory.getLog(SMSUtils.class);
+
+    /**
+     * 初始化账号Client
+     *
+     * @param aliSMSProperties
+     * @return client
+     * @throws Exception
+     */
+    public static Client createClient(AliSMSProperties aliSMSProperties) throws Exception {
+        Config config = new Config()
+                .setAccessKeyId(aliSMSProperties.getAccessKeyId())
+                .setAccessKeySecret(aliSMSProperties.getAccessKeySecret())
+                .setEndpoint(aliSMSProperties.getEndpoint());
+
+        return new Client(config);
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param aliSMSProperties 短信参数,配置文件里配置
+     * @param params 待发送参数集合
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> sendSms(AliSMSProperties aliSMSProperties, Map<String, Object> params) throws Exception {
+        Client client = createClient(aliSMSProperties);
+
+        // 1.设置发送参数
+        SendSmsRequest request = new SendSmsRequest();
+        // 接收短信的手机号码(必填)。支持对多个手机号码发送短信,手机号码之间以半角逗号(,)分隔。上限为1000个手机号码
+        request.setPhoneNumbers(params.get("phoneNumbers").toString());
+        // 短信签名名称(必填)
+        if (params.get("signName") != null && StringUtils.isNotBlank(params.get("signName").toString())) {
+            request.setSignName(params.get("signName").toString());
+        } else {    // 短信签名名称为空则读取配置文件默认短信签名名称
+            request.setSignName(aliSMSProperties.getSignName());
+        }
+        // 短信模板CODE(必填)
+        if (params.get("templateCode") != null && StringUtils.isNotBlank(params.get("templateCode").toString())) {
+            request.setTemplateCode(params.get("templateCode").toString());
+        } else {    // 读取配置文件的清关短信模板CODE
+            request.setTemplateCode(aliSMSProperties.getCustomTemplateCode());
+        }
+        // 短信模板变量对应的实际值(非必填),JSON字符串,支持多个参数,示例:{"name":"张三","number":"15038****76"}
+        if (params.get("templateParam") != null && StringUtils.isNotBlank(params.get("templateParam").toString())) {
+            request.setTemplateParam(params.get("templateParam").toString());
+        }
+        // 外部流水扩展字段(非必填)
+        if (params.get("outId") != null && StringUtils.isNotBlank(params.get("outId").toString())) {
+            request.setOutId(params.get("outId").toString());
+        }
+
+        // 2.发送短信
+        SendSmsResponse resp = null;
+        Map<String, Object> result = new HashMap<>(5);
+        try {
+            resp = client.sendSms(request);
+            result.put("code", resp.body.getCode());
+            result.put("message", resp.body.getMessage());
+            // 发送回执ID
+            result.put("bizId", resp.body.getBizId());
+            // 请求ID
+            result.put("requestId", resp.body.getRequestId());
+        } catch (RuntimeException e) {
+            logger.error("发送短信失败,失败手机号码:" + params.get("phoneNumbers").toString());
+            logger.error("发送短信失败:" + resp.body.getMessage());
+            throw new RuntimeException("发送短信失败:", e);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * 批量发送消息,支持在一次请求中分别向多个不同的手机号码发送不同签名的短信
+     *
+     * @param aliSMSProperties 短信参数,配置文件里配置
+     * @param params 待发送参数集合
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> SendBatchSms(AliSMSProperties aliSMSProperties, Map<String, Object> params) throws Exception {
+        Client client = createClient(aliSMSProperties);
+
+        // 1.设置发送参数
+        SendBatchSmsRequest request = new SendBatchSmsRequest();
+        // 接收短信的手机号码(必填),JSON数组格式的字符串
+        request.setPhoneNumberJson(params.get("phoneNumbersJson").toString());
+        // 短信签名名称(必填),JSON数组格式的字符串,短信签名的个数必须与手机号码的个数相同
+        request.setSignNameJson(params.get("signNameJson").toString());
+        // 短信模板CODE(必填)
+        request.setTemplateCode(params.get("templateCode").toString());
+        // 短信模板变量对应的实际值(非必填),JSON数组格式的字符串
+        if (params.get("templateParamJson") != null && StringUtils.isNotBlank(params.get("templateParamJson").toString())) {
+            request.setTemplateParamJson(params.get("templateParamJson").toString());
+        }
+
+        // 2.发送短信
+        SendBatchSmsResponse resp = null;
+        Map<String, Object> result = new HashMap<>(5);
+        try {
+            resp = client.sendBatchSms(request);
+
+            result.put("code", resp.body.getCode());
+            result.put("message", resp.body.getMessage());
+            // 发送回执ID
+            result.put("bizId", resp.body.getBizId());
+            // 请求ID
+            result.put("requestId", resp.body.getRequestId());
+        } catch (RuntimeException e) {
+            logger.error("批量发送短信失败,失败手机号码:" + params.get("phoneNumbers").toString());
+            logger.error("批量发送短信失败:" + resp.body.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     * 查询短信发送记录和发送状态
+     *
+     * @param aliSMSProperties 短信参数,配置文件里配置
+     * @param params 待发送参数集合
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> querySendDetails(AliSMSProperties aliSMSProperties, Map<String, Object> params) throws Exception {
+        Client client = createClient(aliSMSProperties);
+
+        // 1.设置发送参数
+        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
+        // 接收短信的手机号码(必填)
+        request.setPhoneNumber(params.get("phoneNumbers").toString());
+        // 短信发送日期,格式为yyyyMMdd(必填)
+        request.setSendDate(params.get("sendDate").toString());
+        // 发送回执ID(非必填)
+        if (params.get("bizId") != null && StringUtils.isNotBlank(params.get("bizId").toString())) {
+            request.setBizId(params.get("bizId").toString());
+        }
+        // 指定每页显示的短信记录数量(必填)
+        request.setPageSize(Long.parseLong(params.get("pageSize").toString()));
+        // 指定发送记录的当前页码(必填)
+        request.setCurrentPage(Long.parseLong(params.get("currentPage").toString()));
+
+        // 发送请求
+        QuerySendDetailsResponse resp = null;
+        Map<String, Object> result = new HashMap<>(5);
+        try {
+            resp = client.querySendDetails(request);
+
+            result.put("code", resp.body.getCode());
+            result.put("message", resp.body.getMessage());
+            // 短信发送总条数
+            result.put("totalCount", resp.body.getTotalCount());
+            // 请求ID
+            result.put("requestId", resp.body.getRequestId());
+            // 短信发送明细
+            result.put("smsSendDetailDTOs", resp.body.getSmsSendDetailDTOs());
+        } catch (RuntimeException e) {
+            logger.error("查询短信发送记录和发送状态失败,失败手机号码:" + params.get("phoneNumbers").toString());
+            logger.error("查询短信发送记录和发送状态失败:" + resp.body.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     * 申请短信签名
+     *
+     * @param aliSMSProperties 短信参数,配置文件里配置
+     * @param params 待发送参数集合
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> addSMSSign(AliSMSProperties aliSMSProperties, Map<String, Object> params) throws Exception {
+        Client client = createClient(aliSMSProperties);
+
+        // 1.设置发送参数
+        AddSmsSignRequest request = new AddSmsSignRequest();
+        // 签名名称
+        request.setSignName(params.get("signName").toString());
+        // 签名来源
+        request.setSignSource(Integer.parseInt(params.get("signSource").toString()));
+        // 短信签名申请说明
+        request.setRemark(params.get("remark").toString());
+        // 签名文件列表
+        request.setSignFileList((List<AddSmsSignRequest.AddSmsSignRequestSignFileList>)params.get("signFileList"));
+
+        // 发送请求
+        AddSmsSignResponse resp = null;
+        Map<String, Object> result = new HashMap<>(5);
+        try {
+            resp = client.addSmsSign(request);
+            result.put("code", resp.body.getCode());
+            result.put("message", resp.body.getMessage());
+            // 请求ID
+            result.put("requestId", resp.body.getRequestId());
+            // 签名名称
+            result.put("signName", resp.body.getSignName());
+        } catch (RuntimeException e) {
+            logger.error("申请短信签名失败:" + resp.body.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     * 申请短信模板
+     *
+     * @param aliSMSProperties 短信参数,配置文件里配置
+     * @param params 待发送参数集合
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> AddSMSTemplate(AliSMSProperties aliSMSProperties, Map<String, Object> params) throws Exception {
+        Client client = createClient(aliSMSProperties);
+
+        // 1.设置发送参数
+        AddSmsTemplateRequest request = new AddSmsTemplateRequest();
+        // 短信类型
+        request.setTemplateType(Integer.parseInt(params.get("templateType").toString()));
+        // 模板名称
+        request.setTemplateName(params.get("templateName").toString());
+        // 模板内容
+        request.setTemplateContent(params.get("templateContent").toString());
+        // 短信模板申请说明
+        request.setRemark(params.get("remark").toString());
+
+        // 发送请求
+        AddSmsTemplateResponse resp = null;
+        Map<String, Object> result = new HashMap<>(5);
+        try {
+            resp = client.addSmsTemplate(request);
+            result.put("code", resp.body.getCode());
+            result.put("message", resp.body.getMessage());
+            // 请求ID
+            result.put("requestId", resp.body.getRequestId());
+            // 短信发送明细
+            result.put("templateCode", resp.body.getTemplateCode());
+        } catch (RuntimeException e) {
+            logger.error("申请短信模板失败:" + resp.body.getMessage());
+        }
+
+        return result;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // 本地测试
+        AliSMSProperties aliSMSProperties = new AliSMSProperties();
+        aliSMSProperties.setCustomTemplateCode("SMS_229645099");
+        aliSMSProperties.setEndpoint("dysmsapi.aliyuncs.com");
+        aliSMSProperties.setSignName("珠海免税MALL");
+        aliSMSProperties.setAccessKeyId("LTAI5tJRFwGrMcqt8RcKgkuy");
+        aliSMSProperties.setAccessKeySecret("ffflN7qdiU8z9yJOunwaH0kYNrwkLJ");
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("phoneNumbers", "13160675953");
+
+        // Map<String, Object> result = sendSms(aliSMSProperties, map);
+        // System.out.println(result.get("code"));
+    }
+
+}

+ 24 - 0
kmall-admin/src/main/resources/XmlTemplate/CategoryDtoList.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<workbook>
+    <worksheet name="Sheet1">
+        <section startRow="0" endRow="1"/>
+        <loop startRow="1" endRow="1" items="categoryDtoList" var="categoryDto"
+              varType="com.kmall.admin.dto.CategoryDto">
+            <section startRow="1" endRow="1">
+                <mapping row="1" col="0">categoryDto.name</mapping>
+                <mapping row="1" col="1">categoryDto.keywords</mapping>
+                <mapping row="1" col="2">categoryDto.frontDesc</mapping>
+                <mapping row="1" col="3">categoryDto.sortOrder</mapping>
+                <mapping row="1" col="4">categoryDto.isShow</mapping>
+                <mapping row="1" col="5">categoryDto.level</mapping>
+                <mapping row="1" col="6">categoryDto.parentName</mapping>
+                <mapping row="1" col="7">categoryDto.type</mapping>
+            </section>
+            <loopbreakcondition>
+                <rowcheck offset="0">
+                    <cellcheck offset="0">end</cellcheck>
+                </rowcheck>
+            </loopbreakcondition>
+        </loop>
+    </worksheet>
+</workbook>

+ 12 - 0
kmall-admin/src/main/resources/conf/aliyun-sms.properties

@@ -0,0 +1,12 @@
+# \u963F\u91CC\u4E91\u77ED\u4FE1\u53C2\u6570\u914D\u7F6E
+
+# AccessKey ID\uFF0C\u6D77\u63A7\u63D0\u4F9B
+ali.sms.accessKeyId=LTAI5tJRFwGrMcqt8RcKgkuy
+# AccessKey Secret\uFF0C\u6D77\u63A7\u63D0\u4F9B
+ali.sms.accessKeySecret=ffflN7qdiU8z9yJOunwaH0kYNrwkLJ
+# \u8BBF\u95EE\u7684\u57DF\u540D
+ali.sms.endpoint=dysmsapi.aliyuncs.com
+# \u77ED\u4FE1\u7B7E\u540D\u540D\u79F0\uFF0C\u6D77\u63A7\u63D0\u4F9B
+ali.sms.signName=\u73E0\u6D77\u514D\u7A0EMALL
+# \u77ED\u4FE1\u6A21\u677FCODE\uFF0C\u6D77\u63A7\u63D0\u4F9B
+ali.sms.customTemplateCode=SMS_229645099

+ 35 - 0
kmall-admin/src/main/resources/mybatis/mapper/CategoryDao.xml

@@ -299,4 +299,39 @@
 		</foreach>
 	</update>
 
+	<!--  根据父id查询子对象	-->
+	<select id="queryListByParentId" resultType="com.kmall.admin.entity.CategoryEntity">
+		select
+		`id`,
+		`name`,
+		`store_id`,
+		`merch_sn`,
+		`keywords`,
+		`front_desc`,
+		`parent_id`,
+		`sort_order`,
+		`show_index`,
+		`banner_url`,
+		`icon_url`,
+		`img_url`,
+		`wap_banner_url`,
+		`level`,
+		`type`,
+		`front_name`,
+		`is_show` as `show`
+		from mall_category
+		where `parent_id` in
+		<foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
+			#{id}
+		</foreach>
+	</select>
+
+	<select id="queryExistByNameAndLevel" resultType="com.kmall.admin.dto.CategoryDto">
+		select
+		 id
+		from mall_category
+		where `name` = #{name} and `level` = #{level}
+		limit 1
+	</select>
+
 </mapper>

+ 69 - 0
kmall-admin/src/main/resources/mybatis/mapper/aliyunsms/SendSmsRecordDao.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<!--短信发送记录表-->
+<mapper namespace="com.kmall.admin.dao.aliyunsms.SendSmsRecordDao">
+    <resultMap type="com.kmall.admin.entity.aliyunsms.SendSmsRecord" id="sendSmsRecordMap">
+        <result property="id" column="id"/>
+        <result property="orderSn" column="order_sn"/>
+        <result property="clearNo" column="clear_no"/>
+        <result property="phoneNumbers" column="phone_numbers"/>
+        <result property="sendStatus" column="send_status"/>
+        <result property="sendFailMsg" column="send_fail_msg"/>
+        <result property="requestId" column="request_id"/>
+        <result property="bizId" column="biz_id"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateTime" column="update_time"/>
+    </resultMap>
+
+    <insert id="save" parameterType="com.kmall.admin.entity.aliyunsms.SendSmsRecord" useGeneratedKeys="true" keyProperty="id">
+        insert into send_sms_record
+            (
+            `order_sn`,
+            `clear_no`,
+            `phone_numbers`,
+            `send_status`,
+            `send_fail_msg`,
+            `request_id`,
+            `biz_id`,
+            `create_time`,
+            `update_time`
+            )
+        values
+            (
+            #{orderSn},
+            #{clearNo},
+            #{phoneNumbers},
+            #{sendStatus},
+            #{sendFailMsg},
+            #{requestId},
+            #{bizId},
+            #{createTime},
+            #{updateTime}
+            )
+    </insert>
+
+    <update id="update" parameterType="com.kmall.admin.entity.aliyunsms.SendSmsRecord">
+        update send_sms_record
+        <set>
+            <if test="orderSn != null">`order_sn` = #{orderSn},</if>
+            <if test="clearNo != null">`clear_no` = #{clearNo},</if>
+            <if test="phoneNumbers != null">`phone_numbers` = #{phoneNumbers},</if>
+            <if test="sendStatus != null">`send_status` = #{sendStatus}, </if>
+            <if test="sendFailMsg != null">`send_fail_msg` = #{sendFailMsg}, </if>
+            <if test="requestId != null">`request_id` = #{requestId}, </if>
+            <if test="bizId != null">`biz_id` = #{bizId}, </if>
+            <if test="createTime != null">`create_time` = #{createTime},</if>
+            <if test="updateTime != null">`update_time` = #{updateTime},</if>
+        </set>
+        where id = #{id}
+    </update>
+
+    <select id="queryBySendStatus" resultMap="sendSmsRecordMap">
+		select
+		    `id`,`order_sn`,`clear_no`,`phone_numbers`,`send_status`,`send_fail_msg`,`request_id`,`biz_id`
+		from send_sms_record
+		where `send_status` = #{sendStatus}
+	</select>
+
+</mapper>

+ 22 - 1
kmall-admin/src/main/webapp/WEB-INF/page/shop/category.html

@@ -23,6 +23,16 @@
                 <i-button @click="reloadSearch"><i class="fa fa-refresh"></i>&nbsp;重置</i-button>
             </div>
             <div class="buttons-group">
+                <a @click="importTemplate">导入模板下载</a>&nbsp;&nbsp;&nbsp;&nbsp;
+                <i-col style="display: inline-grid;">
+                    <Upload :show-upload-list="false" :on-success="uploadExcelSuccess" :on-format-error="uploadExcelFormatError"
+                            :format="['xls','xlsx']"
+                            :on-progress="uploadExcelProgress"
+                            action="../category/upload">
+                        <i-button type="ghost" icon="ios-cloud-upload-outline">导入</i-button>
+                    </Upload>
+                </i-col>
+
                 <!--<i-button type="ghost" @click="reload"><i class="fa fa-refresh"></i>&nbsp;刷新</i-button>-->
                 <i-button type="primary" @click="refreshCache"><i class="fa fa-cloud-download"></i>&nbsp;刷新缓存</i-button>
                 #if($shiro.hasPermission("category:save"))
@@ -37,7 +47,18 @@
             </div>
         </Row>
         <table id="jqGrid"></table>
-        <div id="jqGridPager"></div>
+        <!--    分页插件    -->
+        <div class="row">
+            <div class="col-lg-6 col-md-6 col-xs-6 col-sm-6" align="right">
+                <ul id="page-nav"></ul>
+            </div>
+            <div style="margin: 25px 0;" align="left" class="col-lg-3 col-md-3 col-xs-3 col-sm-3">
+                <span>当前第 {{ page.currPage }} 页,共 {{ page.totalPage }} 页</span>
+            </div>
+            <div style="margin: 25px 0;" align="right" class="col-lg-3 col-md-3 col-xs-3 col-sm-3">
+                <span>每页 20 条,共 {{ page.totalCount }} 条</span>
+            </div>
+        </div>
     </div>
 
     <Card v-show="!showList">

+ 3 - 0
kmall-admin/src/main/webapp/WEB-INF/page/sys/header.html

@@ -41,6 +41,9 @@
 <script src="${rc.contextPath}/statics/plugins/treegrid/jquery.treegrid.bootstrap3.js"></script>
 <script src="${rc.contextPath}/statics/plugins/treegrid/tree.table.js"></script>
 
+<!--分页插件bootstrap-paginator-->
+<script src="${rc.contextPath}/statics/plugins/bootstrap-table/bootstrap-paginator.js"></script>
+
 <!--simplemde富文本-->
 <script src='${rc.contextPath}/statics/plugins/froala_editor/js/froala_editor.min.js'></script>
 <!--[if lt IE 9]>

+ 99 - 4
kmall-admin/src/main/webapp/js/shop/category.js

@@ -11,7 +11,7 @@ function initialPage() {
 
 function getGrid() {
     var colunms = TreeGrid.initColumn();
-    var table = new TreeTable(TreeGrid.id, '../category/queryAll', colunms, {'categoryName': vm.q.categoryName,'storeId':vm.q.storeId});
+    var table = new TreeTable(TreeGrid.id, '../category/list', colunms, {'categoryName': vm.q.categoryName,'storeId':vm.q.storeId, 'page': vm.page.currPage});
     table.setExpandColumn(2);
     table.setIdField("id");
     table.setCodeField("id");
@@ -20,6 +20,9 @@ function getGrid() {
     table.setHeight($(window).height() - 100);
     table.init();
     TreeGrid.table = table;
+
+    // 查询页数总数,生成分页插件
+    queryTotal();
 }
 
 var TreeGrid = {
@@ -58,7 +61,7 @@ TreeGrid.initColumn = function () {
             formatter: function (item, index) {
                 return transIsNot(item.show)
             }
-        },
+        }
         // {title: '操作', width: '90px', align: 'center', valign: 'middle',
         //     formatter: function (item, index) {
         //         if(hasPermission('category:saveCopyCategory')) {
@@ -105,6 +108,11 @@ var vm = new Vue({
         q: {
             name: ''
         },
+        page: {
+            currPage: 1,
+            totalPage: 1,
+            totalCount: 0
+        },
         categoryList: [],
         storeList: [],
         stores: [],
@@ -162,7 +170,6 @@ var vm = new Vue({
             });
         },
         uploadExcelL2Success: function (data) {
-            console.log(data);
             if(data.code==0){
                 alert(data.msg, function (index) {
                     vm.copyCategoryDto.storeList = data.copyCategoryDto.storeList;
@@ -182,7 +189,6 @@ var vm = new Vue({
             }
         },
         uploadExcelError: function (data) {
-            console.log(data);
             alert('上传出现异常,请重试!');
         },
         uploadExcelFormatError: function (file) {
@@ -380,7 +386,51 @@ var vm = new Vue({
         eyeImageWapBannerUrl: function () {
             var url = vm.category.wapBannerUrl;
             eyeImage(url);
+        },
+        // 下载导入的模板方法
+        importTemplate: function (){
+            const msg = this.$Message.loading({
+                content: 'Loading...',
+                duration: 0
+            });
+            window.location.href='../statics/file/category_yyyy_mm_dd_v1.0.0.xls';
+            setTimeout(msg, 1000);
+        },
+        // 上传方法开始 ----
+        uploadExcelSuccess: function (data) {
+            if (data.code == 0) {
+                alert('导入成功', function (index) {
+                    $("#jqGrid").trigger("reloadGrid");
+                    vm.page = {
+                        currPage: 1,
+                        totalPage: 1,   // 数值不影响,主要是设置参数
+                        totalCount: 1   // 数值不影响,主要是设置参数
+                    }
+                    vm.reload();
+                });
+            } else {
+                alert(data.msg);
+            }
+            setTimeout(exportMsg, 100);
+        },
+        uploadExcelError: function () {
+            alert('上传出现异常,请重试!');
+            setTimeout(exportMsg, 100);
+        },
+        uploadExcelProgress: function (event, file, fileList) {
+            exportMsg = this.$Message.loading({
+                content: 'Loading...',
+                duration: 0
+            });
+        },
+        uploadExcelFormatError: function (file) {
+            this.$Notice.warning({
+                title: '文件格式不正确',
+                desc: '文件 ' + file.name + ' 格式不正确,请上传 xls 或 xlsx 格式的文件。'
+            });
+            setTimeout(exportMsg, 100);
         }
+        // 上传方法结束----
     },
     mounted() {
         $.get("../store/queryAll", function (r) {
@@ -388,3 +438,48 @@ var vm = new Vue({
         });
     }
 });
+
+// 查询页数、总页数和总数量
+function queryTotal() {
+    $.ajax({
+        type: "POST",
+        url: "../category/queryTotal",
+        contentType: "application/json",
+        data: JSON.stringify({'categoryName': vm.q.categoryName,'storeId':vm.q.storeId, 'page': vm.page.currPage}),
+        dataType: "JSON",
+        success: function (r) {
+            var options = {}
+            if (r.code === 0) {
+                // 分页插件 Bootstrap Paginator 参数设置
+                options = {
+                    currentPage: r.page.currPage,   // 当前页数
+                    totalPages: r.page.totalPage,   // 总页数
+                    bootstrapMajorVersion: 3,    // bootstrap 版本
+                    shouldShowPage: true,
+                    onPageClicked: function (event, originalEvent, type, page) {
+                        // 点击当前页,不做变化
+                        if (vm.page.currPage == page) {
+                            return;
+                        }
+                        vm.page = {
+                            currPage: page,
+                            totalPage: vm.page.totalPage,
+                            totalCount: vm.page.totalCount
+                        }
+                        vm.reload();
+                    }
+                }
+
+                vm.page = {
+                    currPage: r.page.currPage,
+                    totalPage: r.page.totalPage,
+                    totalCount: r.page.totalCount
+                }
+            } else {
+                alert(r.msg);
+            }
+            // 初始化分页插件
+            $("#page-nav").bootstrapPaginator(options);
+        }
+    });
+}

+ 10 - 10
kmall-admin/src/main/webapp/js/shop/offilineOrderList.js

@@ -152,16 +152,16 @@ $(function () {
             // {label: '快递公司', name: 'shippingName', index: 'shipping_name', width: 80},
             // {label: '配送单号', name: 'shippingNo', index: 'shipping_No', width: 80},
             // {label: '快递费用', name: 'shippingFee', index: 'shipping_fee', width: 80},
-            {
-                label: '操作', width: 180, sortable: false,align: 'center',
-                formatter: function (value, col, row) {
-                    let htmlStr = '<button class="btn btn-outline btn-info" onclick="vm.lookDetail(' + row.id + ')"><i class="fa fa-info-circle"></i>详情</button>&nbsp;';
-                    if (row.orderStatus == 201 && (row.orderBizType == '11')) {
-                        htmlStr += '<button class="btn btn-outline btn-primary" onclick="vm.printDetail(' + row.id + ')"><i class="fa fa-print"></i>打印</button>&nbsp;';
-                    }
-                    if (row.orderStatus == 0) {
-                        htmlStr += '<button class="btn btn-outline btn-danger" onclick="vm.cancelUpdate(' + row.id + ')"><i class="fa fa-times-circle-o"></i>&nbsp;取消</button>&nbsp;';
-                    }
+            // {
+            //     label: '操作', width: 180, sortable: false,align: 'center',
+            //     formatter: function (value, col, row) {
+            //         let htmlStr = '<button class="btn btn-outline btn-info" onclick="vm.lookDetail(' + row.id + ')"><i class="fa fa-info-circle"></i>详情</button>&nbsp;';
+            //         if (row.orderStatus == 201 && (row.orderBizType == '11')) {
+            //             htmlStr += '<button class="btn btn-outline btn-primary" onclick="vm.printDetail(' + row.id + ')"><i class="fa fa-print"></i>打印</button>&nbsp;';
+            //         }
+            //         if (row.orderStatus == 0) {
+            //             htmlStr += '<button class="btn btn-outline btn-danger" onclick="vm.cancelUpdate(' + row.id + ')"><i class="fa fa-times-circle-o"></i>&nbsp;取消</button>&nbsp;';
+            //         }
                     // if (row.orderStatus == 201) {
                     //     htmlStr += '<button class="btn btn-outline btn-danger" onclick="vm.refundUpdate(' + row.id + ')"><i class="fa fa-times-circle-o"></i>&nbsp;退款</button>&nbsp;';
                     // }

BIN
kmall-admin/src/main/webapp/statics/file/category_yyyy_mm_dd_v1.0.0.xls


+ 678 - 0
kmall-admin/src/main/webapp/statics/plugins/bootstrap-table/bootstrap-paginator.js

@@ -0,0 +1,678 @@
+/**
+ * bootstrap-paginator-lj.js v1.0.0
+ * 基于bootstrap-paginator.js v1.0.2的优化
+ * --
+ * Copyright 2018 Liu HeJun <liuhejun108@163.com>
+ * --
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function ($) {
+
+    "use strict"; // jshint ;_;
+
+
+    /* Paginator PUBLIC CLASS DEFINITION
+     * ================================= */
+
+    /**
+     * Boostrap Paginator Constructor
+     *
+     * @param element element of the paginator
+     * @param options the options to config the paginator
+     *
+     * */
+    var BootstrapPaginator = function (element, options) {
+        this.init(element, options);
+    },
+        old = null;
+
+    BootstrapPaginator.prototype = {
+
+        /**
+         * Initialization function of the paginator, accepting an element and the options as parameters
+         *
+         * @param element element of the paginator
+         * @param options the options to config the paginator
+         *
+         * */
+        init: function (element, options) {
+
+            this.$element = $(element);
+
+            var version = (options && options.bootstrapMajorVersion) ? options.bootstrapMajorVersion : $.fn.bootstrapPaginator.defaults.bootstrapMajorVersion,
+                id = this.$element.attr("id");
+
+            if (version === 2 && !this.$element.is("div")) {
+
+                throw "in Bootstrap version 2 the pagination must be a div element. Or if you are using Bootstrap pagination 3. Please specify it in bootstrapMajorVersion in the option";
+            } else if (version > 2 && !this.$element.is("ul")) {
+                throw "in Bootstrap version 3 the pagination root item must be an ul element."
+            }
+
+
+
+            this.currentPage = 1;
+
+            this.lastPage = 1;
+
+            this.setOptions(options);
+
+            this.initialized = true;
+        },
+
+        /**
+         * Update the properties of the paginator element
+         *
+         * @param options options to config the paginator
+         * */
+        setOptions: function (options) {
+
+            this.options = $.extend({}, (this.options || $.fn.bootstrapPaginator.defaults), options);
+
+            // .LJ  若设置了totalPages, 则用, 否则根据total和pageSize计算得到
+            if ((this.options && this.options.totalPages)) {
+                this.totalPages = parseInt(this.options.totalPages, 10);  //setup the total pages property.
+            }else{
+                if (this.options.total == 0) {
+                    console.warn("正在根据'total'(0)和'pageSize'计算'totalPages', \n请确保您的数据量确实为0条!");
+                }
+                this.totalPages = parseInt((this.options.total + this.options.pageSize - 1) / this.options.pageSize);   //DEBUG: js里面整数相除, 需要手动取整, 否则结果为小数.  LJ: 2018-2-9 00:31:24
+            }
+
+            // this.totalPages = parseInt(this.options.totalPages, 10);  //setup the total pages property.
+            this.numberOfPages = parseInt(this.options.numberOfPages, 10); //setup the numberOfPages to be shown
+
+            //move the set current page after the setting of total pages. otherwise it will cause out of page exception.
+            if (options && typeof (options.currentPage)  !== 'undefined') {
+
+                this.setCurrentPage(options.currentPage);
+            }
+
+            this.listen();
+
+            //render the paginator
+            this.render();
+
+            if (!this.initialized && this.lastPage !== this.currentPage) {
+                this.$element.trigger("page-changed", [this.lastPage, this.currentPage]);
+            }
+
+        },
+
+        /**
+         * Sets up the events listeners. Currently the pageclicked and pagechanged events are linked if available.
+         *
+         * */
+        listen: function () {
+
+            this.$element.off("page-clicked");
+
+            this.$element.off("page-changed");// unload the events for the element
+
+            if (typeof (this.options.onPageClicked) === "function") {
+                this.$element.bind("page-clicked", this.options.onPageClicked);
+            }
+
+            if (typeof (this.options.onPageChanged) === "function") {
+                this.$element.on("page-changed", this.options.onPageChanged);
+            }
+
+            this.$element.bind("page-clicked", this.onPageClicked);
+        },
+
+
+        /**
+         *
+         *  Destroys the paginator element, it unload the event first, then empty the content inside.
+         *
+         * */
+        destroy: function () {
+
+            this.$element.off("page-clicked");
+
+            this.$element.off("page-changed");
+
+            this.$element.removeData('bootstrapPaginator');
+
+            this.$element.empty();
+
+        },
+
+        /**
+         * Shows the page
+         *
+         * */
+        show: function (page) {
+
+            this.setCurrentPage(page);
+
+            this.render();
+
+            if (this.lastPage !== this.currentPage) {
+                this.$element.trigger("page-changed", [this.lastPage, this.currentPage]);
+            }
+        },
+
+        /**
+         * Shows the next page
+         *
+         * */
+        showNext: function () {
+            var pages = this.getPages();
+
+            if (pages.next) {
+                this.show(pages.next);
+            }
+
+        },
+
+        /**
+         * Shows the previous page
+         *
+         * */
+        showPrevious: function () {
+            var pages = this.getPages();
+
+            if (pages.prev) {
+                this.show(pages.prev);
+            }
+
+        },
+
+        /**
+         * Shows the first page
+         *
+         * */
+        showFirst: function () {
+            var pages = this.getPages();
+
+            if (pages.first) {
+                this.show(pages.first);
+            }
+
+        },
+
+        /**
+         * Shows the last page
+         *
+         * */
+        showLast: function () {
+            var pages = this.getPages();
+
+            if (pages.last) {
+                this.show(pages.last);
+            }
+
+        },
+
+        /**
+         * Internal on page item click handler, when the page item is clicked, change the current page to the corresponding page and
+         * trigger the pageclick event for the listeners.
+         *
+         *
+         * */
+        onPageItemClicked: function (event) {
+
+            var type = event.data.type,
+                page = event.data.page;
+
+            this.$element.trigger("page-clicked", [event, type, page]);
+
+        },
+
+        onPageClicked: function (event, originalEvent, type, page) {
+
+            //show the corresponding page and retrieve the newly built item related to the page clicked before for the event return
+
+            var currentTarget = $(event.currentTarget);
+
+            switch (type) {
+            case "first":
+                currentTarget.bootstrapPaginator("showFirst");
+                break;
+            case "prev":
+                currentTarget.bootstrapPaginator("showPrevious");
+                break;
+            case "next":
+                currentTarget.bootstrapPaginator("showNext");
+                break;
+            case "last":
+                currentTarget.bootstrapPaginator("showLast");
+                break;
+            case "page":
+                currentTarget.bootstrapPaginator("show", page);
+                break;
+            }
+
+        },
+
+        /**
+         * Renders the paginator according to the internal properties and the settings.
+         *
+         *
+         * */
+        render: function () {
+
+            //fetch the container class and add them to the container
+            var containerClass = this.getValueFromOption(this.options.containerClass, this.$element),
+                size = this.options.size || "normal",
+                alignment = this.options.alignment || "left",
+                pages = this.getPages(),
+                listContainer = this.options.bootstrapMajorVersion === 2 ? $("<ul></ul>") : this.$element,
+                listContainerClass = this.options.bootstrapMajorVersion === 2 ? this.getValueFromOption(this.options.listContainerClass, listContainer) : null,
+                first = null,
+                prev = null,
+                next = null,
+                last = null,
+                p = null,
+                i = 0;
+
+
+            this.$element.prop("class", "");
+
+            this.$element.addClass("pagination");
+
+            switch (size.toLowerCase()) {
+            case "large":
+            case "small":
+            case "mini":
+                this.$element.addClass($.fn.bootstrapPaginator.sizeArray[this.options.bootstrapMajorVersion][size.toLowerCase()]);
+                break;
+            default:
+                break;
+            }
+
+            if (this.options.bootstrapMajorVersion === 2) {
+                switch (alignment.toLowerCase()) {
+                case "center":
+                    this.$element.addClass("pagination-centered");
+                    break;
+                case "right":
+                    this.$element.addClass("pagination-right");
+                    break;
+                default:
+                    break;
+                }
+            }
+
+
+            this.$element.addClass(containerClass);
+
+            //empty the outter most container then add the listContainer inside.
+            this.$element.empty();
+
+            if (this.options.bootstrapMajorVersion === 2) {
+                this.$element.append(listContainer);
+
+                listContainer.addClass(listContainerClass);
+            }
+
+            //update the page element reference
+            this.pageRef = [];
+
+            if (pages.first) {//if the there is first page element
+                first = this.buildPageItem("first", pages.first);
+
+                if (first) {
+                    listContainer.append(first);
+                }
+
+            }
+
+            if (pages.prev) {//if the there is previous page element
+
+                prev = this.buildPageItem("prev", pages.prev);
+
+                if (prev) {
+                    listContainer.append(prev);
+                }
+
+            }
+
+
+            for (i = 0; i < pages.length; i = i + 1) {//fill the numeric pages.
+
+                p = this.buildPageItem("page", pages[i]);
+
+                if (p) {
+                    listContainer.append(p);
+                }
+            }
+
+            if (pages.next) {//if there is next page
+
+                next = this.buildPageItem("next", pages.next);
+
+                if (next) {
+                    listContainer.append(next);
+                }
+            }
+
+            if (pages.last) {//if there is last page
+
+                last = this.buildPageItem("last", pages.last);
+
+                if (last) {
+                    listContainer.append(last);
+                }
+            }
+        },
+
+        /**
+         *
+         * Creates a page item base on the type and page number given.
+         *
+         * @param page page number
+         * @param type type of the page, whether it is the first, prev, page, next, last
+         *
+         * @return Object the constructed page element
+         * */
+        buildPageItem: function (type, page) {
+
+            var itemContainer = $("<li></li>"),//creates the item container
+                itemContent = $("<a></a>"),//creates the item content
+                text = "",
+                title = "",
+                itemContainerClass = this.options.itemContainerClass(type, page, this.currentPage),
+                itemContentClass = this.getValueFromOption(this.options.itemContentClass, type, page, this.currentPage),
+                tooltipOpts = null;
+
+
+            switch (type) {
+
+            case "first":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "last":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "prev":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "next":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            case "page":
+                if (!this.getValueFromOption(this.options.shouldShowPage, type, page, this.currentPage)) { return; }
+                text = this.options.itemTexts(type, page, this.currentPage);
+                title = this.options.tooltipTitles(type, page, this.currentPage);
+                break;
+            }
+
+            itemContainer.addClass(itemContainerClass).append(itemContent);
+
+            itemContainer.addClass("page-item");
+            itemContainer.find("a").addClass("page-link");
+
+            itemContent.addClass(itemContentClass).html(text).on("click", null, {type: type, page: page}, $.proxy(this.onPageItemClicked, this));
+
+            if (this.options.pageUrl) {
+                itemContent.attr("href", this.getValueFromOption(this.options.pageUrl, type, page, this.currentPage));
+            }
+
+            if (this.options.useBootstrapTooltip) {
+                tooltipOpts = $.extend({}, this.options.bootstrapTooltipOptions, {title: title});
+
+                itemContent.tooltip(tooltipOpts);
+            } else {
+                itemContent.attr("title", title);
+            }
+
+            return itemContainer;
+
+        },
+
+        setCurrentPage: function (page) {
+            if (page > this.totalPages || page < 1) {// if the current page is out of range, throw exception.
+
+                throw "Page out of range";
+
+            }
+
+            this.lastPage = this.currentPage;
+
+            this.currentPage = parseInt(page, 10);
+
+        },
+
+        /**
+         * Gets an array that represents the current status of the page object. Numeric pages can be access via array mode. length attributes describes how many numeric pages are there. First, previous, next and last page can be accessed via attributes first, prev, next and last. Current attribute marks the current page within the pages.
+         *
+         * @return object output objects that has first, prev, next, last and also the number of pages in between.
+         * */
+        getPages: function () {
+
+            var totalPages = this.totalPages,// get or calculate the total pages via the total records
+                pageStart = (this.currentPage % this.numberOfPages === 0) ? (parseInt(this.currentPage / this.numberOfPages, 10) - 1) * this.numberOfPages + 1 : parseInt(this.currentPage / this.numberOfPages, 10) * this.numberOfPages + 1,//calculates the start page.
+                output = [],
+                i = 0,
+                counter = 0;
+
+            pageStart = pageStart < 1 ? 1 : pageStart;//check the range of the page start to see if its less than 1.
+
+            for (i = pageStart, counter = 0; counter < this.numberOfPages && i <= totalPages; i = i + 1, counter = counter + 1) {//fill the pages
+                output.push(i);
+            }
+
+            output.first = 1;//add the first when the current page leaves the 1st page.
+
+            if (this.currentPage > 1) {// add the previous when the current page leaves the 1st page
+                output.prev = this.currentPage - 1;
+            } else {
+                output.prev = 1;
+            }
+
+            if (this.currentPage < totalPages) {// add the next page when the current page doesn't reach the last page
+                output.next = this.currentPage + 1;
+            } else {
+                output.next = totalPages;
+            }
+
+            output.last = totalPages;// add the last page when the current page doesn't reach the last page
+
+            output.current = this.currentPage;//mark the current page.
+
+            output.total = totalPages;
+
+            output.numberOfPages = this.options.numberOfPages;
+
+            return output;
+
+        },
+
+        /**
+         * Gets the value from the options, this is made to handle the situation where value is the return value of a function.
+         *
+         * @return mixed value that depends on the type of parameters, if the given parameter is a function, then the evaluated result is returned. Otherwise the parameter itself will get returned.
+         * */
+        getValueFromOption: function (value) {
+
+            var output = null,
+                args = Array.prototype.slice.call(arguments, 1);
+
+            if (typeof value === 'function') {
+                output = value.apply(this, args);
+            } else {
+                output = value;
+            }
+
+            return output;
+
+        }
+
+    };
+
+
+    /* TYPEAHEAD PLUGIN DEFINITION
+     * =========================== */
+
+    old = $.fn.bootstrapPaginator;
+
+    $.fn.bootstrapPaginator = function (option) {
+
+        var args = arguments,
+            result = null;
+
+        $(this).each(function (index, item) {
+            var $this = $(item),
+                data = $this.data('bootstrapPaginator'),
+                options = (typeof option !== 'object') ? null : option;
+
+            if (!data) {
+                data = new BootstrapPaginator(this, options);
+
+                $this = $(data.$element);
+
+                $this.data('bootstrapPaginator', data);
+
+                return;
+            }
+
+            if (typeof option === 'string') {
+
+                if (data[option]) {
+                    result = data[option].apply(data, Array.prototype.slice.call(args, 1));
+                } else {
+                    throw "Method " + option + " does not exist";
+                }
+
+            } else {
+                result = data.setOptions(option);
+            }
+        });
+
+        return result;
+
+    };
+
+    $.fn.bootstrapPaginator.sizeArray = {
+
+        "2": {
+            "large": "pagination-large",
+            "small": "pagination-small",
+            "mini": "pagination-mini"
+        },
+        "3": {
+            "large": "pagination-lg",
+            "small": "pagination-sm",
+            "mini": ""
+        }
+
+    };
+
+    $.fn.bootstrapPaginator.defaults = {
+        containerClass: "",
+        size: "normal",
+        alignment: "left",
+        bootstrapMajorVersion: 2,
+        listContainerClass: "",
+        itemContainerClass: function (type, page, current) {
+            return (page === current) ? "active" : "";
+        },
+        itemContentClass: function (type, page, current) {
+            return "";
+        },
+        currentPage: 1,
+        numberOfPages: 5,
+        total:0,    //总记录数, 来自后台    +LJ
+        pageSize:10, //前台或后台均可设定    +LJ
+        // totalPages: 1,   //源码        -LJ
+        // totalPages:function (total,pageSize) {    // +LJ  !设置了totalPages, 则total, pageSize设置失效!
+        //     return (total + pageSize - 1) / pageSize;
+        // },
+        pageUrl: function (type, page, current) {
+            return null;
+        },
+        onPageClicked: null,
+        onPageChanged: null,
+        useBootstrapTooltip: false,
+        shouldShowPage: function (type, page, current) {
+            console.log(type)
+            console.log(page);
+            console.log(current);
+            var result = true;
+
+            switch (type) {
+            case "first":
+                result = (current !== 1);
+                break;
+            case "prev":
+                result = (current !== 1);
+                break;
+            case "next":
+                result = (current !== this.totalPages);
+                break;
+            case "last":
+                result = (current !== this.totalPages);
+                break;
+            case "page":
+                result = true;
+                break;
+            }
+
+            return result;
+
+        },
+        itemTexts: function (type, page, current) {
+            switch (type) {
+            case "first":
+                return "&lt;&lt;";
+            case "prev":
+                return "&lt;";
+            case "next":
+                return "&gt;";
+            case "last":
+                return "&gt;&gt;";
+            case "page":
+                return page;
+            }
+        },
+        tooltipTitles: function (type, page, current) {
+
+            switch (type) {
+            case "first":
+                return "Go to first page";
+            case "prev":
+                return "Go to previous page";
+            case "next":
+                return "Go to next page";
+            case "last":
+                return "Go to last page";
+            case "page":
+                return (page === current) ? "Current page is " + page : "Go to page " + page;
+            }
+        },
+        bootstrapTooltipOptions: {
+            animation: true,
+            html: true,
+            placement: 'top',
+            selector: false,
+            title: "",
+            container: false
+        }
+    };
+
+    $.fn.bootstrapPaginator.Constructor = BootstrapPaginator;
+
+
+
+}(window.jQuery));

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
kmall-admin/src/main/webapp/statics/plugins/bootstrap-table/bootstrap-paginator.min.js


+ 3 - 0
kmall-common/src/main/java/com/kmall/common/constant/JxlsXmlTemplateName.java

@@ -79,4 +79,7 @@ public class JxlsXmlTemplateName {
     // 计量单位代码导入
     public static final String SYS_CUS_UNIT_CODE_DTO_List = "/XmlTemplate/SysCusUnitCodeDtoList.xml";
 
+    // 商品类型导入
+    public static final String CATEGORY_DTO_LIST = "/XmlTemplate/CategoryDtoList.xml";
+
 }

+ 16 - 0
sql/send_sms_record.sql

@@ -0,0 +1,16 @@
+create table send_sms_record
+(
+    id            int auto_increment
+        primary key,
+    order_sn      varchar(32)  not null comment '订单编号',
+    clear_no      varchar(20)  not null comment '清单编号',
+    phone_numbers varchar(256) not null comment '手机号码,多个以英文逗号分隔',
+    send_status   tinyint      not null comment '发送状态,0:发送失败,1:发送成功',
+    send_fail_msg varchar(256) null comment '发送失败原因',
+    request_id    varchar(64)  null comment '请求id-阿里云短信接口返回',
+    biz_id        varchar(32)  null comment '回执id-阿里云短信接口返回',
+    create_time   datetime     not null comment '创建时间',
+    update_time   datetime     not null comment '更新时间'
+)
+    comment '短信发送记录表';
+

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است