licailing 4 лет назад
Родитель
Сommit
a81f6fd881

+ 2 - 0
src/main/java/com/izouma/jiashanxia/config/Constants.java

@@ -14,4 +14,6 @@ public interface Constants {
     String SMS_SIGN_NAME = "走马信息";
 
     String SMS_TEMPLATE_CODE_GENERIC = "SMS_175485688";
+
+    String SMS_TEMPLATE_CODE_NOTIFICATION = "SMS_211489622";
 }

+ 2 - 0
src/main/java/com/izouma/jiashanxia/enums/AuthorityName.java

@@ -5,6 +5,8 @@ public enum AuthorityName {
     ROLE_DEV("开发者"),
     ROLE_WRITER("核销者"),
     ROLE_CREATOR("创建者"),
+    ROLE_ATTRACTIONS("景区管理员"),
+    ROLE_BRAND("优惠券品牌管理员"),
     ROLE_EXPERT("团长"),
     ROLE_ADMIN("管理员");
     private final String description;

+ 15 - 2
src/main/java/com/izouma/jiashanxia/service/OrderInfoService.java

@@ -11,6 +11,7 @@ import com.izouma.jiashanxia.dto.*;
 import com.izouma.jiashanxia.enums.*;
 import com.izouma.jiashanxia.exception.BusinessException;
 import com.izouma.jiashanxia.repo.*;
+import com.izouma.jiashanxia.service.sms.SmsService;
 import com.izouma.jiashanxia.utils.JpaUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.data.domain.Page;
@@ -41,6 +42,7 @@ public class OrderInfoService {
     private final UserPackageFlowRepo  userPackageFlowRepo;
     private final UserService          userService;
     private final StockRepo            stockRepo;
+    private final SmsService           smsService;
 
     public Page<OrderInfo> all(PageQuery pageQuery) {
         pageQuery.setSort("createdAt,desc");
@@ -53,6 +55,7 @@ public class OrderInfoService {
     public Page<OrderInfo> backAll(PageQuery pageQuery) {
         Long attractionsId = JpaUtils.getLong(pageQuery.getQuery(), "attractionsId");
         List<Long> packageIds = null;
+        // 按景区id查找
         if (attractionsId > 0) {
             packageIds = packageRepo.findAllIdByAttractionsId(attractionsId);
         }
@@ -64,7 +67,7 @@ public class OrderInfoService {
             if (StrUtil.isNotEmpty(pageQuery.getSearch())) {
                 withdrawService.getNickname(pageQuery.getSearch(), and, root, criteriaBuilder);
             }
-            if (attractionsId > 0) {
+            if (CollUtil.isNotEmpty(finalPackageIds)) {
                 and.add(root.get("packageId").in(finalPackageIds));
             }
             return criteriaBuilder.and(and.toArray(new Predicate[0]));
@@ -214,6 +217,11 @@ public class OrderInfoService {
 //        }
         // 上级分销
         this.distribution2(order);
+        User user = order.getUser();
+        if (ObjectUtil.isNotNull(user.getPhone())){
+            smsService.sendNotification(user.getPhone(),order.getName(),order.getOrderNumber());
+        }
+
 //        orderDelayService.remove(order.getId(), order.getCreatedAt());
     }
 
@@ -635,6 +643,7 @@ public class OrderInfoService {
     public Page<OrderInfo> children(PageQuery pageQuery) {
         pageQuery.setSort("createdAt,desc");
         Map<String, Object> query = pageQuery.getQuery();
+        // 按父用户查找
         Long parent = JpaUtils.getLong(query, "userId");
         List<Long> childrenId = userService.childrenId(parent);
 
@@ -644,9 +653,13 @@ public class OrderInfoService {
                 withdrawService.getNickname(pageQuery.getSearch(), and, root, criteriaBuilder);
             }
             and.add(criteriaBuilder.equal(root.get("status"), OrderInfoStatus.PAID));
-            and.add(root.get("userId").in(childrenId));
+            if (CollUtil.isNotEmpty(childrenId)) {
+                and.add(root.get("userId").in(childrenId));
+            }
+
             return criteriaBuilder.and(and.toArray(new Predicate[0]));
         }), JpaUtils.toPageRequest(pageQuery));
     }
 
+
 }

+ 43 - 8
src/main/java/com/izouma/jiashanxia/service/sms/AliSmsService.java

@@ -36,7 +36,10 @@ public class AliSmsService implements SmsService {
     public String sendVerify(String phone) {
         smsRecordRepo.findLastByPhoneAndExpiresAtAfterAndExpiredFalse(phone, LocalDateTime.now()).ifPresent(record -> {
             if (record.getCreatedAt().plusMinutes(1L).isAfter(LocalDateTime.now())) {
-                long sec = record.getCreatedAt().plusMinutes(1L).toInstant(ZoneOffset.UTC).getEpochSecond() - LocalDateTime.now().toInstant(ZoneOffset.UTC).getEpochSecond() + 1;
+                long sec = record.getCreatedAt()
+                        .plusMinutes(1L)
+                        .toInstant(ZoneOffset.UTC)
+                        .getEpochSecond() - LocalDateTime.now().toInstant(ZoneOffset.UTC).getEpochSecond() + 1;
                 throw new BusinessException("请" + sec + "秒后再试");
             }
         });
@@ -67,12 +70,12 @@ public class AliSmsService implements SmsService {
             smsRecordRepo.expire(phone);
             String sessionId = RandomStringUtils.randomAlphabetic(10);
             smsRecordRepo.save(SmsRecord.builder()
-                                        .sessionId(sessionId)
-                                        .phone(phone)
-                                        .code(code)
-                                        .expiresAt(LocalDateTime.now().plusMinutes(5))
-                                        .expired(false)
-                                        .build());
+                    .sessionId(sessionId)
+                    .phone(phone)
+                    .code(code)
+                    .expiresAt(LocalDateTime.now().plusMinutes(5))
+                    .expired(false)
+                    .build());
             return sessionId;
         } catch (ClientException | JSONException e) {
             e.printStackTrace();
@@ -80,9 +83,41 @@ public class AliSmsService implements SmsService {
         }
     }
 
+    @Override
+    public String sendNotification(String phone, String goods, String transactionId) {
+        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
+        IAcsClient client = new DefaultAcsClient(profile);
+
+        CommonRequest request = new CommonRequest();
+        request.setMethod(MethodType.POST);
+        request.setDomain("dysmsapi.aliyuncs.com");
+        request.setVersion("2017-05-25");
+        request.setAction("SendSms");
+        request.putQueryParameter("PhoneNumbers", phone);
+        request.putQueryParameter("SignName", Constants.SMS_SIGN_NAME);
+        request.putQueryParameter("TemplateCode", Constants.SMS_TEMPLATE_CODE_NOTIFICATION);
+        request.putQueryParameter("TemplateParam", "[{\"name\":\"天游佳处\"},{\"goods\":" + goods + "},{\"order\":" + transactionId + "}]");
+        try {
+            CommonResponse response = client.getCommonResponse(request);
+            if (response.getHttpStatus() != 200) {
+                throw new BusinessException("发送失败,请稍后再试", response.getHttpStatus() + "," + response.getData());
+            }
+            log.info("send sms response {}", response.getData());
+            JSONObject jsonObject = new JSONObject(response.getData());
+            if (!"ok".equalsIgnoreCase(jsonObject.getString("Code"))) {
+                throw new BusinessException("发送失败,请稍后再试", jsonObject.getString("Message"));
+            }
+            return RandomStringUtils.randomAlphabetic(10);
+        } catch (ClientException | JSONException e) {
+            e.printStackTrace();
+            throw new BusinessException("发送失败,请稍后再试", e.getMessage());
+        }
+    }
+
     @Override
     public void verify(String phone, String code) throws SmsVerifyException {
-        SmsRecord smsRecord = smsRecordRepo.findLastByPhoneAndExpiresAtAfterAndExpiredFalse(phone, LocalDateTime.now()).orElseThrow(new SmsVerifyException("验证码错误"));
+        SmsRecord smsRecord = smsRecordRepo.findLastByPhoneAndExpiresAtAfterAndExpiredFalse(phone, LocalDateTime.now())
+                .orElseThrow(new SmsVerifyException("验证码错误"));
         if (!smsRecord.getCode().equalsIgnoreCase(code)) {
             throw new BusinessException("验证码错误");
         }

+ 2 - 0
src/main/java/com/izouma/jiashanxia/service/sms/SmsService.java

@@ -5,6 +5,8 @@ import java.util.function.Supplier;
 public interface SmsService {
     String sendVerify(String phone);
 
+    String sendNotification(String phone, String goods, String transactionId);
+
     void verify(String phone, String code) throws SmsVerifyException;
 
     class SmsVerifyException extends Exception implements Supplier<SmsVerifyException> {

+ 1 - 1
src/main/java/com/izouma/jiashanxia/web/OrderInfoController.java

@@ -48,7 +48,7 @@ public class OrderInfoController extends BaseController {
         return orderInfoService.all(pageQuery);
     }
 
-    @PreAuthorize("hasRole('ADMIN')")
+    @PreAuthorize("hasAnyRole('ADMIN','CREATOR')")
     @PostMapping("/backAll")
     public Page<OrderInfo> backAll(@RequestBody PageQuery pageQuery) {
         return orderInfoService.backAll(pageQuery);

+ 14 - 5
src/main/vue/src/router.js

@@ -74,7 +74,7 @@ const router = new Router({
                 {
                     path: '/promotionList',
                     name: 'promotionList',
-                    component: () => import(/* webpackChunkName: "userEdit" */ '@/views/PromotionList.vue'),
+                    component: () => import(/* webpackChunkName: "promotionList" */ '@/views/PromotionList.vue'),
                     meta: {
                         title: '推广列表'
                     }
@@ -82,7 +82,7 @@ const router = new Router({
                 {
                     path: '/employeeList',
                     name: 'employeeList',
-                    component: () => import(/* webpackChunkName: "userEdit" */ '@/views/employee/EmployeeList.vue'),
+                    component: () => import(/* webpackChunkName: "employeeList" */ '@/views/employee/EmployeeList.vue'),
                     meta: {
                         title: '员工列表'
                     }
@@ -91,7 +91,7 @@ const router = new Router({
                     path: '/writeOffUserList',
                     name: 'WriteOffUserList',
                     component: () =>
-                        import(/* webpackChunkName: "userEdit" */ '@/views/attractions/WriteOffUserList.vue'),
+                        import(/* webpackChunkName: "writeOffUserList" */ '@/views/attractions/WriteOffUserList.vue'),
                     meta: {
                         title: '员工列表'
                     }
@@ -107,7 +107,7 @@ const router = new Router({
                 {
                     path: '/childrenList',
                     name: 'childrenList',
-                    component: () => import(/* webpackChunkName: "userEdit" */ '@/views/employee/ChildrenList.vue'),
+                    component: () => import(/* webpackChunkName: "childrenList" */ '@/views/employee/ChildrenList.vue'),
                     meta: {
                         title: '下级列表'
                     }
@@ -116,7 +116,16 @@ const router = new Router({
                     path: '/employeeOrderList',
                     name: 'employeeOrderList',
                     component: () =>
-                        import(/* webpackChunkName: "userEdit" */ '@/views/employee/EmployeeOrderList.vue'),
+                        import(/* webpackChunkName: "employeeOrderList" */ '@/views/employee/EmployeeOrderList.vue'),
+                    meta: {
+                        title: '下级列表'
+                    }
+                },
+                {
+                    path: '/orderInfoList2',
+                    name: 'orderInfoList2',
+                    component: () =>
+                        import(/* webpackChunkName: "userEdit" */ '@/views/attractions/OrderInfoList2.vue'),
                     meta: {
                         title: '下级列表'
                     }

+ 3 - 3
src/main/vue/src/views/OrderInfoList.vue

@@ -173,9 +173,9 @@ export default {
             if (this.packageId) {
                 data.query.packageId = this.packageId;
             }
-            if (this.$route.query.id) {
-                data.query.attractionsId = Number(this.$route.query.id);
-            }
+            // if (this.$route.query.id) {
+            //     data.query.attractionsId = Number(this.$route.query.id);
+            // }
             return data;
         },
         toggleMultipleMode(multipleMode) {

+ 2 - 2
src/main/vue/src/views/UserEdit.vue

@@ -132,7 +132,7 @@ export default {
         //         console.log(e);
         //     });
         this.$http
-            .post('/attractions/all', { size: 1000, query: { del: false, brand: true } }, { body: 'json' })
+            .post('/attractions/all', { size: 1000, query: { del: false } }, { body: 'json' })
             .then(res => {
                 if (res.content.length > 0) {
                     res.content.forEach(item => {
@@ -279,7 +279,7 @@ export default {
             let data = this.formData.authorities;
             this.display = false;
             for (let item in data) {
-                if (data[item].name === 'ROLE_WRITER' || data[item].name === 'ROLE_CREATOR') {
+                if (data[item].name === 'ROLE_WRITER' || data[item].name === 'ROLE_BRAND') {
                     this.display = true;
                     break;
                 }

+ 9 - 0
src/main/vue/src/views/attractions/AttractionsList.vue

@@ -43,6 +43,7 @@
                 <template slot-scope="{ row }">
                     <el-button @click="handleCommand1(row.id)" size="mini">套餐</el-button>
                     <el-button @click="handleCommand(row.id)" size="mini">核销员</el-button>
+                    <el-button @click="handleCommand2(row.id)" size="mini">订单</el-button>
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
                     <!-- <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button> -->
                 </template>
@@ -185,6 +186,14 @@ export default {
                     id: id
                 }
             });
+        },
+        handleCommand2(id) {
+            this.$router.push({
+                path: '/orderInfoList2',
+                query: {
+                    id: id
+                }
+            });
         }
     }
 };

+ 254 - 0
src/main/vue/src/views/attractions/OrderInfoList2.vue

@@ -0,0 +1,254 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <!-- <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button> -->
+            <!-- <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button> -->
+            <el-select
+                v-model="status"
+                multiple
+                clearable
+                @change="getData"
+                placeholder="请选择订单状态"
+                class="filter-item"
+            >
+                <el-option v-for="item in statusOptions" :key="item.value" :value="item.value" :label="item.label">
+                </el-option>
+            </el-select>
+            <el-select v-model="packageId" class="filter-item" clearable @change="getData" placeholder="请选择套餐">
+                <el-option v-for="item in packages" :label="item.name" :value="item.id" :key="item.id"></el-option>
+            </el-select>
+            <el-date-picker
+                v-model="dateRange"
+                type="daterange"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                start-placeholder="请选择开始时间"
+                end-placeholder="请选择结束时间"
+                range-separator="至"
+                :default-time="['00:00:00', '23:59:59']"
+                class="filter-item"
+                @change="getData"
+            >
+            </el-date-picker>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <!--<el-table-column prop="id" label="ID" width="100"> </el-table-column>-->
+            <el-table-column prop="orderNumber" label="订单号" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="user.nickname" label="昵称"> </el-table-column>
+            <el-table-column prop="payMethod" label="支付方式" :formatter="payMethodFormatter"> </el-table-column>
+            <el-table-column prop="status" label="订单状态">
+                <template slot-scope="{ row }">
+                    <el-tag type="warning" v-if="row.status == 'UNPAID'">未支付</el-tag>
+                    <el-tag type="success" v-else-if="row.status == 'PAID'">已支付</el-tag>
+                    <el-tag type="info" v-else-if="row.status == 'CANCELLED'">已取消</el-tag>
+                    <el-tag v-else>线下支付</el-tag>
+                </template>
+            </el-table-column>
+            <el-table-column prop="name" label="名称" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="price" label="价钱" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="paidAt" label="支付时间" show-overflow-tooltip> </el-table-column>
+            <!--<el-table-column prop="setId" label="充值套餐"> </el-table-column>-->
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <!-- <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button> -->
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'OrderInfoList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/orderInfo/backAll',
+            downloading: false,
+            payMethodOptions: [
+                { label: '微信', value: 'WEIXIN' },
+                { label: '余额', value: 'YUE' },
+                { label: '线下', value: 'OFFLINE' }
+            ],
+            statusOptions: [
+                { label: '未支付', value: 'UNPAID' },
+                { label: '已支付', value: 'PAID' },
+                { label: '已取消', value: 'CANCELLED' },
+                { label: '线下支付', value: 'OFFLINE_PAID' },
+                { label: '已退款', value: 'REFUNDED' },
+                { label: '申请退款中', value: 'REQUEST_REFUND' }
+            ],
+            status: [],
+            packageId: '',
+            packages: [],
+            dateRange: []
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    mounted() {
+        this.$http
+            .get('/package/allList')
+            .then(res => {
+                this.packages = res;
+            })
+            .catch(e => {
+                console.log(e);
+                this.$message.error(e.error);
+            });
+    },
+    methods: {
+        payMethodFormatter(row, column, cellValue, index) {
+            let selectedOption = this.payMethodOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            // return {
+            //     search: this.search,
+            //     query: {
+            //         status: this.status,
+            //         packageId: this.packageId
+            //     }
+            // };
+            let data = { sort: 'createdAt,desc', query: {} };
+            if (this.search) {
+                data.search = this.search;
+            }
+            if (this.dateRange && this.dateRange.length > 0) {
+                data.query.createdAt = this.dateRange[0] + ',' + this.dateRange[1];
+            }
+            if (this.status) {
+                data.query.status = this.status;
+            }
+            if (this.packageId) {
+                data.query.packageId = this.packageId;
+            }
+            if (this.$route.query.id) {
+                data.query.attractionsId = Number(this.$route.query.id);
+            }
+            return data;
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/orderInfoEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/orderInfoEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/orderInfo/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/orderInfo/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>