Quellcode durchsuchen

Merge branch 'dev' of http://git.izouma.com/xiongzhu/raex_back into dev

wangqifan vor 4 Jahren
Ursprung
Commit
0e7d64eafa

+ 6 - 0
pom.xml

@@ -451,6 +451,12 @@
             <artifactId>tea</artifactId>
             <version>1.1.14</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-green</artifactId>
+            <version>3.6.5</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 37 - 0
src/main/java/com/izouma/nineth/converter/EncryptConverter.java

@@ -0,0 +1,37 @@
+package com.izouma.nineth.converter;
+
+import com.izouma.nineth.utils.AESEncryptUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+@Converter
+@Slf4j
+public class EncryptConverter implements AttributeConverter<String, String> {
+
+    @Override
+    public String convertToDatabaseColumn(String s) {
+        if (StringUtils.isNotBlank(s)) {
+            try {
+                return AESEncryptUtil.encrypt(s);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return s;
+    }
+
+    @Override
+    public String convertToEntityAttribute(String s) {
+        if (StringUtils.isNotBlank(s)) {
+            try {
+                return AESEncryptUtil.decrypt(s);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return s;
+    }
+}

+ 90 - 0
src/main/java/com/izouma/nineth/service/ContentAuditService.java

@@ -0,0 +1,90 @@
+package com.izouma.nineth.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.green.model.v20180509.TextScanRequest;
+import com.aliyuncs.http.FormatType;
+import com.aliyuncs.http.HttpResponse;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.izouma.nineth.config.AliyunProperties;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+public class ContentAuditService {
+    private AliyunProperties aliyunProperties;
+
+    public boolean auditText(String content) {
+        if (StringUtils.isBlank(content)) return true;
+        IClientProfile profile = DefaultProfile.getProfile("cn-shenzhen",
+                aliyunProperties.getAccessKeyId(), aliyunProperties.getAccessKeySecret());
+        DefaultProfile.addEndpoint("cn-shenzhen", "Green", "green.cn-shenzhen.aliyuncs.com");
+        IAcsClient client = new DefaultAcsClient(profile);
+        TextScanRequest textScanRequest = new TextScanRequest();
+        textScanRequest.setAcceptFormat(FormatType.JSON);
+        textScanRequest.setHttpContentType(FormatType.JSON);
+        textScanRequest.setMethod(com.aliyuncs.http.MethodType.POST);
+        textScanRequest.setEncoding("UTF-8");
+        textScanRequest.setRegionId("cn-shenzhen");
+        List<Map<String, Object>> tasks = new ArrayList<>();
+        Map<String, Object> task1 = new LinkedHashMap<>();
+        task1.put("dataId", UUID.randomUUID().toString());
+
+
+        task1.put("content", content);
+        tasks.add(task1);
+        JSONObject data = new JSONObject();
+
+        data.put("scenes", List.of("antispam"));
+        data.put("tasks", tasks);
+        textScanRequest.setHttpContent(data.toJSONString().getBytes(StandardCharsets.UTF_8), "UTF-8", FormatType.JSON);
+        textScanRequest.setConnectTimeout(3000);
+        textScanRequest.setReadTimeout(6000);
+        try {
+            HttpResponse httpResponse = client.doAction(textScanRequest);
+            if (httpResponse.isSuccess()) {
+                JSONObject scrResponse = JSON.parseObject(new String(httpResponse.getHttpContent(), StandardCharsets.UTF_8));
+                log.info(JSON.toJSONString(scrResponse, true));
+                if (200 == scrResponse.getInteger("code")) {
+                    JSONArray taskResults = scrResponse.getJSONArray("data");
+                    for (Object taskResult : taskResults) {
+                        if (200 == ((JSONObject) taskResult).getInteger("code")) {
+                            JSONArray sceneResults = ((JSONObject) taskResult).getJSONArray("results");
+                            for (Object sceneResult : sceneResults) {
+                                String scene = ((JSONObject) sceneResult).getString("scene");
+                                String suggestion = ((JSONObject) sceneResult).getString("suggestion");
+                                // 根据scene和suggetion做相关处理。
+                                // suggestion为pass表示未命中垃圾。suggestion为block表示命中了垃圾,可以通过label字段查看命中的垃圾分类。
+                                log.info("scene = [" + scene + "]");
+                                log.info("suggestion = [" + suggestion + "]");
+                                if ("block".equals(suggestion)) {
+                                    return false;
+                                }
+                            }
+                        } else {
+                            log.info("task process fail:" + ((JSONObject) taskResult).getInteger("code"));
+                        }
+                    }
+                } else {
+                    log.info("detect not success. code:" + scrResponse.getInteger("code"));
+                }
+            } else {
+                log.info("response not success. status:" + httpResponse.getStatus());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+}

+ 11 - 0
src/main/java/com/izouma/nineth/service/UserService.java

@@ -89,6 +89,7 @@ public class UserService {
     private PasswordEncoder               passwordEncoder;
     private WeakPassRepo                  weakPassRepo;
     private UserBalanceRepo               userBalanceRepo;
+    private ContentAuditService           contentAuditService;
 
     public User update(User user) {
         if (!SecurityUtils.hasRole(AuthorityName.ROLE_ADMIN)) {
@@ -122,6 +123,11 @@ public class UserService {
                        Boolean useCollectionPic, Boolean riskWarning, Integer level) {
         User user = userRepo.findById(userId).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isNotBlank(nickname)) {
+            if (!nickname.equals(user.getNickname())) {
+                if (!contentAuditService.auditText(nickname)) {
+                    throw new BusinessException("昵称包含非法内容");
+                }
+            }
             user.setNickname(nickname);
         }
         if (StringUtils.isNotBlank(avatar)) {
@@ -134,6 +140,11 @@ public class UserService {
             user.setBg(bg);
         }
         if (StringUtils.isNotBlank(intro)) {
+            if (!intro.equals(user.getIntro())) {
+                if (!contentAuditService.auditText(nickname)) {
+                    throw new BusinessException("简介包含非法内容");
+                }
+            }
             user.setIntro(intro);
         }
         if (useCollectionPic != null) {

+ 5 - 2
src/main/vue/src/views/Dashboard.vue

@@ -39,6 +39,7 @@ import LineChartWidget from '../widgets/LineChartWidget';
 import BarChartWidget from '../widgets/BarChartWidget';
 import PieChartWidget from '../widgets/PieChartWidget';
 import TopWidget from '../widgets/TopWidget';
+import MonthWidget from '../widgets/MonthWidget';
 
 export default {
     created() {},
@@ -64,7 +65,8 @@ export default {
                 { x: 3, y: 0, w: 3, h: 4, i: '1', name: 'NumWidget' },
                 // { x: 0, y: 12, w: 6, h: 6, i: '4', name: 'BarChartWidget' },
                 { x: 0, y: 4, w: 6, h: 12, i: '6', name: 'PieChartWidget' },
-                { x: 0, y: 20, w: 12, h: 10, i: '7', name: 'TopWidget' }
+                { x: 0, y: 20, w: 6, h: 10, i: '8', name: 'MonthWidget' },
+                { x: 6, y: 20, w: 6, h: 10, i: '7', name: 'TopWidget' }
             ];
         }
         this.$http.get('/statistic/total').then(res => {
@@ -86,7 +88,8 @@ export default {
         LineChartWidget,
         BarChartWidget,
         PieChartWidget,
-        TopWidget
+        TopWidget,
+        MonthWidget
     }
 };
 </script>

+ 148 - 0
src/main/vue/src/widgets/MonthWidget.vue

@@ -0,0 +1,148 @@
+<template>
+    <widget-card :bodyStyle="bodyStyle">
+        <template #header>
+            <div class="header">
+                <span>月度统计</span>
+                <el-select
+                    style="width: 120px;"
+                    size="mini"
+                    v-model="value"
+                    @change="changeSelect"
+                    placeholder="请选择"
+                >
+                    <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
+                    </el-option>
+                </el-select>
+            </div>
+        </template>
+        <div class="box-content">
+            <div class="box">
+                <div class="text1">官方销售额/二手市场</div>
+                <div class="text2">
+                    <span v-html="getNum(order.official, true)"></span>/
+                    <span v-html="getNum(order.transfer, true)"></span>
+                </div>
+            </div>
+            <div class="box">
+                <div class="text1">拉新数(人)</div>
+                <div class="text2">{{ user }}</div>
+            </div>
+        </div>
+    </widget-card>
+</template>
+<script>
+import { format, parse, addMonths } from 'date-fns';
+import WidgetCard from './WidgetCard';
+import acc from '../mixins/acc';
+
+export default {
+    data() {
+        return {
+            bodyStyle: {
+                overflow: 'auto'
+            },
+            value: '',
+            options: [],
+            user: 0,
+            order: {
+                official: 0,
+                transfer: 0
+            }
+        };
+    },
+    mixins: [acc],
+    components: {
+        WidgetCard
+    },
+    mounted() {
+        let options = [];
+        this.value = format(new Date(), 'yyyy-MM');
+        for (let i = 0; i < 7; i++) {
+            options.push({
+                label: format(addMonths(new Date(), 0 - i), 'yyyy-MM'),
+                value: format(addMonths(new Date(), 0 - i), 'yyyy-MM')
+            });
+        }
+        this.options = options;
+
+        this.changeSelect();
+    },
+    methods: {
+        changeSelect() {
+            this.$nextTick(() => {
+                this.$http
+                    .get('/statistic/userTrendV2', {
+                        yearMonth: this.value
+                    })
+                    .then(res => {
+                        this.user = res;
+                    });
+                this.$http
+                    .get('/statistic/orderPriceTrendV2', {
+                        yearMonth: this.value
+                    })
+                    .then(res => {
+                        this.order = res;
+                    });
+            });
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+
+/deep/.el-card {
+    overflow: hidden;
+}
+
+.box-content {
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+    align-self: stretch;
+    padding: 30px;
+    height: 100%;
+    box-sizing: border-box;
+}
+.box {
+    padding: 0 20px;
+    height: 125px;
+    background: #f5f7fa;
+    border-radius: 16px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    min-width: 153px;
+    box-sizing: border-box;
+    .text1 {
+        font-size: 14px;
+        font-weight: bold;
+        color: #666666;
+        line-height: 20px;
+        white-space: nowrap;
+    }
+    .text2 {
+        color: #feb30e;
+        font-size: 22px;
+        font-weight: bold;
+        line-height: 29px;
+        margin-top: 2px;
+        white-space: nowrap;
+        /deep/small {
+            font-size: 12px;
+        }
+    }
+
+    &:nth-child(2) {
+        .text2 {
+            color: #4dcc6f;
+        }
+    }
+}
+</style>

+ 89 - 0
src/test/java/com/izouma/nineth/ContentAuditTest.java

@@ -0,0 +1,89 @@
+package com.izouma.nineth;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.exceptions.ServerException;
+import com.aliyuncs.green.model.v20180509.TextScanRequest;
+import com.aliyuncs.http.FormatType;
+import com.aliyuncs.http.HttpResponse;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import org.junit.Test;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class ContentAuditTest {
+
+    @Test
+    public void testText() {
+        IClientProfile profile = DefaultProfile
+                .getProfile("cn-shenzhen", "LTAI5tPoBCiEMSDaS1Q4HKr9", "F8ZNiqdH35T7gikBkn6Fq8tgbvdY88");
+        DefaultProfile
+                .addEndpoint("cn-shenzhen", "Green", "green.cn-shanghai.aliyuncs.com");
+        IAcsClient client = new DefaultAcsClient(profile);
+        TextScanRequest textScanRequest = new TextScanRequest();
+        textScanRequest.setAcceptFormat(FormatType.JSON); // 指定API返回格式。
+        textScanRequest.setHttpContentType(FormatType.JSON);
+        textScanRequest.setMethod(com.aliyuncs.http.MethodType.POST); // 指定请求方法。
+        textScanRequest.setEncoding("UTF-8");
+        textScanRequest.setRegionId("cn-shenzhen");
+        List<Map<String, Object>> tasks = new ArrayList<>();
+        Map<String, Object> task1 = new LinkedHashMap<>();
+        task1.put("dataId", UUID.randomUUID().toString());
+        /**
+         * 待检测的文本,长度不超过10000个字符。
+         */
+        task1.put("content", "我操你妈逼");
+        tasks.add(task1);
+        JSONObject data = new JSONObject();
+
+        /**
+         * 检测场景。文本垃圾检测请传递antispam。
+         **/
+        data.put("scenes", Arrays.asList("antispam"));
+        data.put("tasks", tasks);
+        data.put("bizType", "recommend_standard_template_01");
+        System.out.println(JSON.toJSONString(data, true));
+        textScanRequest.setHttpContent(data.toJSONString().getBytes(StandardCharsets.UTF_8), "UTF-8", FormatType.JSON);
+        // 请务必设置超时时间。
+        textScanRequest.setConnectTimeout(3000);
+        textScanRequest.setReadTimeout(6000);
+        try {
+            HttpResponse httpResponse = client.doAction(textScanRequest);
+            if (httpResponse.isSuccess()) {
+                JSONObject scrResponse = JSON.parseObject(new String(httpResponse.getHttpContent(), StandardCharsets.UTF_8));
+                System.out.println(JSON.toJSONString(scrResponse, true));
+                if (200 == scrResponse.getInteger("code")) {
+                    JSONArray taskResults = scrResponse.getJSONArray("data");
+                    for (Object taskResult : taskResults) {
+                        if (200 == ((JSONObject) taskResult).getInteger("code")) {
+                            JSONArray sceneResults = ((JSONObject) taskResult).getJSONArray("results");
+                            for (Object sceneResult : sceneResults) {
+                                String scene = ((JSONObject) sceneResult).getString("scene");
+                                String suggestion = ((JSONObject) sceneResult).getString("suggestion");
+                                // 根据scene和suggetion做相关处理。
+                                // suggestion为pass表示未命中垃圾。suggestion为block表示命中了垃圾,可以通过label字段查看命中的垃圾分类。
+                                System.out.println("args = [" + scene + "]");
+                                System.out.println("args = [" + suggestion + "]");
+                            }
+                        } else {
+                            System.out.println("task process fail:" + ((JSONObject) taskResult).getInteger("code"));
+                        }
+                    }
+                } else {
+                    System.out.println("detect not success. code:" + scrResponse.getInteger("code"));
+                }
+            } else {
+                System.out.println("response not success. status:" + httpResponse.getStatus());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}