本文最后更新于 2024-03-17,文章内容可能已经过时。

简历文档v1.0

为自己的简历制作的文档.供参考和做面试提示词使用.

简历

1.ERP:

ERP 系统是一种集成的应用软件包,可以用于平衡制造、分销和财务功能。

公司那边有开发人员和较为完整的系统,实验室人员在原有基础上面做优化.

配合公司的后端人员,他们给我们任务,我们找人做.

spring mvc 的项目.

1.1 工作流程:

两个系统

国内系统入驻很多个国内平台商家.

首先进行商品采集 把国内平台上的商品信息采集到自己的数据库,然后再经过处理后,上架到韩国的电商平台.

下单. 韩国客户下单后,中国操作人员同步下单,并定期进行手动的订单同步. 中国下单到指定的物流中心,然后发送过去.

1.2 对接支付宝:

应用场景:商户购买图片翻译,文字翻译等服务.

首先获取appid,appkey,appsecret.我是一开始是使用沙盒的参数,测试完成后换成公司正式的. 把这些通过一个配置类存起来.

然后编写接口,主要有两个方法

一个是发起支付的方法,一个是支付完成后,支付宝平台设置回调的方法.

因为都是自己调用的,所以url可以随便写.

传入订单号,返回一个HTML页面 用户扫码支付后,支付宝异步调用系统给出的接口. 编写完成后,在本地测试回调的时候,还涉及到了内网穿透问题.

内网穿透的核心原理在于将外网 IP 地址与内网 IP 地址建立联系

我当时是使用的ngrok.通过反向代理的方式实现的.

正向代理是代理客户端,反向代理是代理服务器.

ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

然后就是退款,调用支付宝的退款接口,然后等待返回结果.这也是异步的.

package com.samton.ops.common.util.alipay;
​
​
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
​
/**
 * @author: HuGoldWater
 * @description:
 */
@Slf4j
@RestController
@RequestMapping("payment")
public class AlipayController {
​
    @Autowired
    private AliPayResource aliPayResource;
​
    /**
     * 前往支付宝进行支付
     */
    @RequestMapping (value="/goAlipay")
    public String goAlipay(String merchantUserId, String merchantOrderId) throws Exception{
        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(aliPayResource.getGatewayUrl(),
                aliPayResource.getAppId(),
                aliPayResource.getMerchantPrivateKey(),
                "json",
                aliPayResource.getCharset(),
                aliPayResource.getAlipayPublicKey(),
                aliPayResource.getSignType());
​
        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(aliPayResource.getReturnUrl());
        alipayRequest.setNotifyUrl(aliPayResource.getNotifyUrl());
​
        // 商户订单号, 商户网站订单系统中唯一订单号, 必填
        String out_trade_no = merchantOrderId;
        // 付款金额, 必填 单位元
        String total_amount = "0.01";  // 测试用 1分钱
        // 订单名称, 必填
        String subject = "抒情熊-付款用户[" + merchantUserId + "]";
        // 商品描述, 可空, 目前先用订单名称
        String body = subject;
​
        // 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
        String timeout_express = "1h";
        alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
                + "\"total_amount\":\""+ total_amount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"body\":\""+ body +"\","
                + "\"timeout_express\":\""+ timeout_express +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
​
        //请求
        String alipayForm = "";
        try {
            alipayForm = alipayClient.pageExecute(alipayRequest).getBody();
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        log.info("支付宝支付 - 前往支付页面, alipayForm: \n{}", alipayForm);
//        return JsonResult.ok(alipayForm);
        return  alipayForm;
    }
​
​
    /**
     * 支付成功后的支付宝异步通知
     */
    @RequestMapping(value="/alipay")
    public String alipay(HttpServletRequest request, HttpServletResponse response) throws Exception {
​
        log.info("支付成功后的支付宝异步通知");
​
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map<String,String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用
//       valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
​
        boolean signVerified = AlipaySignature.rsaCheckV1(params,
                aliPayResource.getAlipayPublicKey(),
                aliPayResource.getCharset(),
                aliPayResource.getSignType()); //调用SDK验证签名
​
        if(signVerified) {//验证成功
            // 商户订单号
            String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
            // 支付宝交易号
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
            // 交易状态
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
            // 付款金额
            String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
​
            if (trade_status.equals("TRADE_SUCCESS")){
//                String merchantReturnUrl = paymentOrderService.updateOrderPaid(out_trade_no, CurrencyUtils.getYuan2Fen(total_amount));
//                notifyFoodieShop(out_trade_no,merchantReturnUrl);
            }
​
            Date date = new Date();
            SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
            Object date1 = dateFormat.format(date);
​
            log.info("************* 支付成功(支付宝异步通知) - 时间: {} *************", date1);
            log.info("* 订单号: {}", out_trade_no);
            log.info("* 支付宝交易号: {}", trade_no);
            log.info("* 实付金额: {}", total_amount);
            log.info("* 交易状态: {}", trade_status);
            log.info("*****************************************************************************");
​
            return "success";
        }else {
            Date date = new Date();
            SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
            Object date1 = dateFormat.format(date);
            //验证失败
            log.info("验签失败, 时间: {}", date1);
            return "fail";
        }
    }
​
}
​
​
package com.samton.ops.common.util.alipay;
​
import lombok.Data;
import org.springframework.stereotype.Component;
​
@Data
@Component
​
public class AliPayResource {
    //之前测试的
    //private String appId="2021000122690584";
    //private String merchantPrivateKey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0vuB3EqOvJD8E4/AQYCY7Vph5aVbfMKJxc1lceE+es1dk0EQ8VWmYXom1P2zeBDA5MfHl+cIZx7l15J+FkEkc6hP6vMTuDtS0VDxJ36P/Frp2V3St42MTw2aHVvWgbloOGJeNY4R5zSGpN+Louhjwi5GZjWHkDluSNxI7Y2orCPk3NwqSnxnRjTSYzK19A1FYELFyzTIek2ncNRe2snT4AyDym/vl1WxDSYx0MhFhuJD4DWo6viX4tDQYPRz3TTqo20N82McYBzBEEYgJYhbe9/FBve7Vydd3QeiWvd9EkGaiPovJCg+MxCUYn9dZwZ315C3G1/ctwOceKMi5t+krAgMBAAECggEAK6nQ3/MIx86hyrSl0c7obX1F6E6iRdih5XZQKB6IXXZFrn0BfvHDSKPN8JMZ4ahxXd/K6Bul4ER3cRuBzepFP07s9K2VhUzf5ZBT4CS+oWkEMoJ+FWPRE30oz5kaTV9bMfyO4AEih6oeb5qonkAWtkWBLu8Qrt8pD/Ft3hruEub3tKQQMKM+g00PISyq7QuKLgM335EZOSZIKguKIErCSqYtms0qRB67uyfnLnYqnGtOMwv0Crs6ZMtgtT8IMwqgqjpMBWM5ui5C5iFkPVq4yCePPBA5HvAjIUHlCCv96fLbXkmqeVEhgv6HB/ZSqKscpYoFnHVvCg+lDAsljy1zQQKBgQDrQ9S2+1uZl0K84xYepeTIGCk7kA8TXoAFK3WwYQeIfqsRvetQIYT1NX0/OYE9Sx9+8uygRtJRj4XhdiRSw58gqyGWJYsPnycDbPQ5CcvqEVsuDpW2ZrYZm1Nm424gUn7pSyC8XBCVgOh8As5Ni7BqNvy7oXyAN1XXuu8Z5IdhhQKBgQDErPPGxegO7vJV2LUytaPqwro4IwSbi29YhoRZTU9L2lrms2qbMC7VlZDCUy6tPS4tmZi3/kgb0PZZ3f5xK3wb+3pzAWwANqT57nV+GuXjzHFO2SHta29LOaKZ22VhMbM7FyHO3CoR2KGs3G0dKQrHEwIsDu3XeNUl9zVCQ3vG7wKBgQDAQ0C1ARnMnRbHMnXDOiOLemNH7+TCGXpZvziAmesEGzBGYYTKiXoUwk/GuYHqy1fD8VZ7bSU3zijFJj1s/b0vf2sFP00zyQajAAleC6l/cgunyfeDhtDOgGdaMAaxl3lrwh/QjxRmeWCE5+4c5UmYo7NKyx/p0E7w7C22ZVJV2QKBgCVB2kBad1Z034VxswmzLSUo1FwUDihlJqeve9zq702gRL4VWOmjHAwr9CtL1LjOsTPEOBEK46AZWsG1cyD/KtimMBEfQNVdhK0wBiPodopLzV8xdOLkCkZG2c2pqS/bWWelPytPu8x7rEzxyN3QS5FgwXWVMmsyIba6eOVfoVATAoGAZXY7UKsEIYJZ4bKxXC2+aa7P2Igs8mXfhns+LJYH+tcl2amcfjTfwfGF+dnQz6k2ZrZOx6xzbm7V0/VgHb9K28vZIJ4AUSMP2l1PYuNi3WvMb4EjftASv7zbhmjIKRwRZ05KWDXBG/FUn/Q/ZQsVPi/wn2apy6c8wWmA9p9LPDw=";
    //private String alipayPublicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiqQtyKGK0T9eu28Qv/qvcpro7zrnv0Uzplc/rTVozMPDHpdpoWITcWivBVJzNFBQMXW7pV8tJqRzQYATMApcDYnMWa6WrK6YfgqZuVoyvMUSNitajTHsxEmVWQTXqnW95QsiZN+22y3TtFQ0F9C4C/KwwaM4oibTsNtlzI2qqzgFtNbsvvq8lsa3FVZO0gC04BMqiemTfc1dCTgQ7kihl4MJNit85t4tmSCPlIOu7Or1BbVRZ3E1RXeXw39EsggCkdsOp+1ljVxOBfpkne99aodfXBNzJrrZboI5BuTY4j5zTFWFZr3mHx0r+htBbKQ4gyquIakKXigZx1Vir6RQWQIDAQAB";
​
​
    //新生成的
    private String appId="2021003196629056";
    private String merchantPrivateKey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+MobPAIduM31gH7RcK7zeLcQrtiOJ5k0lqdPIyxqZCJpw4xkyFknETfpu29VgBsXYVZAj5UpdRAwIN0vBQk89PL83ryb6oKTmXL9955hyFkvXA8KllS2wlvZw+blhQKec1vABRHgAdMqb2g4ZVSpeADecudQ3zimQ+/JmXqJ/dFcz353VKP2MsQO6XelB0V+sYQ/eOVymlgq61rPf7mnbuqB42xXcAKyoHKsdjs++1Xc0jnAPwFNWoK79YYmBfLNJrUGIqP3Mk/J/D7AX75S94nOTSETUgGS4V64rVgh7SLPWHm8UPpp9a5E2Gy6eCoviFASmSnPjQshh/Z5XVwV7AgMBAAECggEBAIRjwzZ/lcV9jb3FE9Q8laDJlo0eoSox5m1bAxH3XjI7rNT7HuSgYwSDithzqNjInhpxpH138wVsgjuN0etZ7rIfgLKP9r/p5h57XMeU16ZCItQtx+VeK4mJ60zEZudtC76+Vh1rvWQD56wIYlv7zhvUZuFu8GtP09sZpKbhJJc1/vPt9j3QKrgLFLTlI/NgkJhKGVBiKZXsYfZXEaJg7REQqRkvvhbIpBMTgDT5U3la3aNuEZ/IFkMVYGoxXnuRMRxJn/Ws4m8g20n8zMEV3id4Ajg0h6huex826YLbqRjH1bZV8JqwxeE4LPGMRJdNxSStLi3w7cdlZqGaD65AW/ECgYEA3Tyt8dNe6mxXBU+8k/2ASs1yOM4RV51bi3tPd13ba0yBY5sumLAX8yyShkHBUpWZYQ9MKW3D2iyI7V08ETQei2Xg8pYQAM50KzEBFFoqLfCdC9wb7JWPfT2thzrgrwIYfLlf+2QxO+rrDijGDJDNVM4BcyitctjtNWLviZ6r8F8CgYEA3BVErC02GWkSFGo8/S5LBVrfKbM3X7BcIoxNq6LvEhPSOe0aAIexIQiRKau+5UpJUyfUq9CQ4hwwUQVuASh0f4+EARq6hhyfbDKo34WA5ZC62d61u+roqydfs54gXUaWQgJtLR8DOzTlgzX0kGTK7vyI5PRgKYtu5QNWqxJM0GUCgYATjIwWRUYq0r3xwzT3orvWYEcKi/LuWgI/1fqUop+D4LPCOHEqnszO+Q5NfLv3by6pa++f7YoT2kGTL9zh7EgSq0LwTKBHYfbT5jWhNcJqYsuNw7pX8nNGbs/JlkNKU5YUV1EK5rSPBdgVXTb630S1jKqGIX8KGe8D+6UM9Q9eYwKBgF8i6n6rRJmTa/dbPWYMnu1/rLxv9l3s9McSc3jghAwCeXwE3JqiDZXECExFK84eYLgLnclv7VFw8gn0GOtzO3jw5xU7IqpasSeqdom5QlD6UWtg9Jp5H37tFFem4UKxAr8iIWPB5jmv0g74QfIxP/AzRlICuZb76UIiQVLOJFppAoGATIx7fd8nEzDclAdbR0X+D1iuxXp6nBHYn1x07kqe/ugCDe+M7awNE+IK6SjC0NYwqq1uUr3KpkNrFChLMzBpb6pZGyNtnBQLpYZvPus+o7qH4itVgbZOazYwfX9bK6JucUNAlv91I4mrtZ4GtVPm7+rSl1j6QugjnlCUGG4V8GM=";
    private String alipayPublicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvjKGzwCHbjN9YB+0XCu83i3EK7YjieZNJanTyMsamQiacOMZMhZJxE36btvVYAbF2FWQI+VKXUQMCDdLwUJPPTy/N68m+qCk5ly/feeYchZL1wPCpZUtsJb2cPm5YUCnnNbwAUR4AHTKm9oOGVUqXgA3nLnUN84pkPvyZl6if3RXM9+d1Sj9jLEDul3pQdFfrGEP3jlcppYKutaz3+5p27qgeNsV3ACsqByrHY7PvtV3NI5wD8BTVqCu/WGJgXyzSa1BiKj9zJPyfw+wF++UveJzk0hE1IBkuFeuK1YIe0iz1h5vFD6afWuRNhsungqL4hQEpkpz40LIYf2eV1cFewIDAQAB";
​
    //以前别的平台用的
    //    private String appId="2017071307739440";
//    private String merchantPrivateKey="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyEIH5VqdTZyUqKm+htChbSEsbsK5wS9vnnZjjuQDOmr8B/GhlVHeAkl+nxB8tM84OOWevD7tukieE4b48mrkmnnNSNC1EOBreIFFFCNeIcDs5JZFV/O1BH/7PNG+lSDkgHyTXoNrJKjlWbSWHuxGYFO2Gv3fSdc0p3hSXhlqPLM0b5IJwBZx6/Qkiy0KYAgbRfhc/0EdXaXlyq4cPXJaJiYTzQB9y8ImCPXGfjA0EjU4kr7f6ZuAvb8FI5Qmpbh/KyW4VB+BAcV2g4AhXofp92UDggq/zq+M/aWvFFgstYHpDebj0O6KE0fnv0iHSOJRc5ZAxpfpCE5R1x8Ff51rFAgMBAAECggEBAJW79+/6BD7IL6JjiPfLjVwlULN6QVXBFKySA+0KtzkFO7Wp0QfUnaEKdVGYRDc4pv+jGiNF8XErifvd8KD54tQszgDES4RgQYekWXLZ2pSq+8I0ayCJzeDDzPvktjWgpBj014BTjWc4EHsy0SpwDn16q7px64qY8OtSCrLYkfJubOxIC0ly2/vwRsbodQW2KPdL5+hBznP719EWMLRecrTezeguwHygOe3OvsJFwWyt/5EBQr7ompFYZKEMqanM2Ccqh6Ttmr+LOm52yhBP5YhNH2zCCiyU7LoVTwG3K0E6G1daa3TYjcmpCbNNfECVadeoS8stuqHw9bcpumKvLAkCgYEA2fq2znTIDqPz+3AWPqjnEbiO2xOri+0GM2aaT/szQ0+VOsHzICN3HZ9PEiz21ceFwUdPguwsvG8pzUm0sQkTlKcO9EPHMFR0ZRFkF++YePWqbP9kD7HOmNVIyS109u4M2NQRqt281GbMS0cp0FZBE9haXxkaJERHgAALfR43B0MCgYEA0R+AckNAIVK3/ZByfuJCpLDIqh/3B13Q/HZEXEG8PX/Bkb5WQb/xIIftbo77SGCbBHKRedncCdvQrdsnfTER0U+McAcYPOGjfoDRayqD+8aMDMZnEa6ms+52Gk4YEUNmHbeC33sVaSDhPRJLXAm2suBqu7jWbickUEDIN2DG4VcCgYEAnbwPDNb07aM2qnwxnKYcj3Y96coSGO8rzYYxpC7iqZKtKhevF1KSn5zoWv6un4QCBhrULqk4tiK63RK47mLjCG7bI2bofNCgaYJsK+X1L5KWAMnOXo0MMwwj33BFc2pPYZgUMNDEE+9PZinY2CmSbgnhW2+Ouy+tjbJ6nc9/goECgYBRTST7x0d8bRNZAjpxN/fe3Vf2RB0fAQtJy5UCJRBQ/IU96zjPsRbGpfHaBL6Owfgif4QtUlSohIwZu1Ub5+LcdEfOGgQDT1nnyZ8hQdM0JFm4cb4KctMeqvvBeFEFDSX7MagwyEJnr7/BpYYkzyX3XyY/uzmqc487oCP226oWWwKBgEJMmIlVP8UuUj3kEPRrY4zX9lLFFPE91fKb9xjy4+qZNzN8ecqQ79is9HcMqQLNHWLhQRedHhCg6pK/dUjm5QcNhg5VifvGg+coGHIYKfqbmdFF92gH834DummL1cdflTZ4M+f/n4xs/HJk1vKl8jwYXvoAyy+JfvUVX2ovtBrZ";
//    private String alipayPublicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi7ttZ5erVZrV5i6EzQq5iLlpoYYBCs/7gxMChokDeXPpp+TV2HoZx8Ftlcit0M4GPssTFK4NOg1xuH/NJEROYzxFFrvIup/DG3KmprCM2bB42VwGi43W0r969EgUVO45Pipguk01lqYRCVbjsYA09Zh91DUHEETbVGbQMfYP/3CEjbICIMgUJpqWWi7gtGJmPne0eXIELpp1enkHwvbgMAbL9IMekn0omUTxrDJpbRUBsDFCvSusLv3m4ihYUT4UZtlqXin9wKA1fSoLiMojYGFvGgK8vuUl7d40B0hwEpGlKb9KKsVF7vj7trAnUj4KYg98yAQ8ArynT5imS6uRFwIDAQAB";
​
​
    private String notifyUrl="https://dbe1-112-6-224-58.ngrok-free.app/ST_ERP_Web_exploded/payment/alipay";
​
    private String returnUrl;
​
    private String signType="RSA2";
    private String charset="UTF-8";
    //private String gatewayUrl="https://openapi.alipaydev.com/gateway.do";
    private String gatewayUrl="https://openapi.alipay.com/gateway.do";
}
​

1.3 对接阿里机器翻译:

和对接支付宝差不多

都是使用appkey和appsecret的方式去获取支付宝的翻译api的client.

然后调用翻译接口进行翻译.

但是有个大坑.

就是如果不使用maven等方式引入依赖,(会有很多个依赖)会出现版本不匹配问题,看官网的文档也解决不了.mavenrepository上寻找也找不到.

最后联系的阿里的工作人员来解决的.

    public static   com.aliyun.alimt20181012.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 您的 AccessKey ID
                .setAccessKeyId(accessKeyId)
                // 您的 AccessKey Secret
                .setAccessKeySecret(accessKeySecret);
        // 访问的域名
        config.endpoint = "mt.cn-hangzhou.aliyuncs.com";
        return new com.aliyun.alimt20181012.Client(config);
    }
​
​
    public static Result translate(String text) throws Exception {
        try {
//            System.out.println("翻译前为:"+text);
            com.aliyun.alimt20181012.Client client = createClient("LTAI5tD61xHVx87uUVnWi26B", "hIRefUPwQnKJqf4Wk5bAarhfC8CGYZ");
            com.aliyun.alimt20181012.models.TranslateGeneralRequest translateGeneralRequest = new com.aliyun.alimt20181012.models.TranslateGeneralRequest()
                    .setFormatType("text")
                    .setSourceLanguage("zh")
                    .setTargetLanguage("ko")
                    .setSourceText(text);
//                .setScene("general");
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            // 复制代码运行请自行打印 API 的返回值
            TranslateGeneralResponse response =  client.translateGeneralWithOptions(translateGeneralRequest, runtime);
            String text1 = com.aliyun.teautil.Common.toJSONString(response.getBody().data.getTranslated());
            String number = com.aliyun.teautil.Common.toJSONString(response.getBody().data.getWordCount());
//            System.out.println("翻译后为:"+text1);
//            System.out.println(number);
            return new Result(text1,number);
        } catch (TeaException error) {
            error.printStackTrace();
        } catch (Exception _error) {
            _error.printStackTrace();
        } catch (Throwable t){
            t.printStackTrace();
        }
        return null;
    }

出了翻译本身,还有业务问题.

业务场景主要出现在商品翻译和客服对话的时候.

对于商品翻译,因为他是要计算用量的,所以翻译之后必须储存下来.

package com.samton.platform.translate.service.impl;//package com.samton.platform.translate.service.impl;
​
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.samton.erp.api.setMeal.service.TErpUserDataStatisticsService;
import com.samton.listing.api.coupang.entity.LisCoupangProduct;
import com.samton.listing.api.coupang.dao.LisCoupangProductMapper;
​
import com.samton.platform.framework.base.BaseResult;
import com.samton.platform.translate.bean.CoupangProduct;
import com.samton.platform.translate.bean.LisTranslate;
import com.samton.platform.translate.dao.LisTranslateMapper;
import com.samton.platform.translate.service.TranslateService;
import com.samton.platform.translate.util.Result;
import com.samton.platform.translate.util.TranslateUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
​
​
@Service("translateservice")
public class TranslateServiceImpl implements TranslateService {
​
​
​
    @Resource
    private  LisCoupangProductMapper lisCoupangProductMapper;
​
    @Resource
    private LisTranslateMapper lisTranslateMapper;
    @Resource
    private TErpUserDataStatisticsService tErpUserDataStatisticsService;
​
//
//    //1.传入id,查找这一行,得到需要的属性
//    //2.解析json,并进行翻译
//    //4.把数据返回给前端
//    //3.在数据库中替换翻译结果
    public LisCoupangProduct translateAll(Long id) throws Exception {
        //初始化翻译所需字符数量
        Integer wordCountAll = 0;
//        //获取需要翻译的行
        LisCoupangProduct coupangProduct = lisCoupangProductMapper.selectById(id);
        //翻译名字
        Result result = TranslateUtil.translate(coupangProduct.getCoupangProName());
        assert result != null;
        String translatedName = result.getText();
        coupangProduct.setCoupangProName(translatedName);
        wordCountAll +=  Integer.parseInt(result.getNumber());
        //翻译sku_pro
​
        JSONArray jsonArray = JSON.parseArray(coupangProduct.getSkuProperty());
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            jsonArray.remove(i);
            String attributeTypeName = (String) jsonObject.get("attributeTypeName");
            if (!StringUtils.isEmpty(attributeTypeName)) {
                Result result1 = TranslateUtil.translate(attributeTypeName);
                assert result1 != null;
                wordCountAll += Integer.parseInt(result1.getNumber());
                String translatedKey = result1.getText();
                jsonObject.put("attributeTypeName", translatedKey);
            }
            JSONArray jsonArray1 = (JSONArray) jsonObject.get("attributeValueName");
            for(int j = 0;j<jsonArray1.size();j++){
                //获取
                String attributeValueName = jsonArray1.getString(j);
                jsonArray1.remove(j);
​
                //翻译
                if (!StringUtils.isEmpty(attributeValueName)) {
                    Result result2 = TranslateUtil.translate(attributeValueName);
                    assert result2 != null;
                    wordCountAll += Integer.parseInt(result2.getNumber());
                    String translatedValue = result2.getText();
                    //添加
                    jsonArray1.add(j,translatedValue);
                }
            }
            jsonObject.put("attributeValueName", jsonArray1);
            jsonArray.add(i, jsonObject);
        }
​
        coupangProduct.setSkuProperty(jsonArray.toJSONString());
​
        //翻译product_propertys
        JSONArray jsonArray1 = JSON.parseArray(coupangProduct.getProductProperties());
        for (int i = 0; i < jsonArray1.size(); i++) {
            JSONObject jsonObject = jsonArray1.getJSONObject(i);
            jsonArray1.remove(i);
​
            String itemName = (String) jsonObject.get("itemName");
            if (!StringUtils.isEmpty(itemName)) {
                Result result2 = TranslateUtil.translate(itemName);
                wordCountAll += Integer.parseInt(result2.getNumber());
                String translatedItem = result2.getText();
                jsonObject.put("itemName", translatedItem);
            }
​
            String emptyBarcodeReason = (String) jsonObject.get("emptyBarcodeReason");
            if (!StringUtils.isEmpty(emptyBarcodeReason)) {
                Result result2 = TranslateUtil.translate(emptyBarcodeReason);
                wordCountAll += Integer.parseInt(result2.getNumber());
                String translatedEmptyBarcodeReason = result2.getText();
                jsonObject.put("emptyBarcodeReason", translatedEmptyBarcodeReason);
            }
​
            JSONArray jsonArray2 = jsonObject.getJSONArray("searchTags");
            if (jsonArray2.size() != 0) {
                for (int j = 0; j < jsonArray2.size(); j++) {
                    Result result2 = TranslateUtil.translate((String) jsonArray2.get(j));
                    wordCountAll += Integer.parseInt(result2.getNumber());
                    jsonArray2.remove(j);
                    jsonArray2.add(j, result2.getText());
                }
                jsonObject.put("searchTags", jsonArray2);
            }
​
            JSONArray jsonArray3 = jsonObject.getJSONArray("attributes");
            System.out.println(jsonArray3);
            if (jsonArray3.size() != 0) {
                for (int m = 0; m < jsonArray3.size(); m++) {
                    JSONObject jsonObject1 = jsonArray3.getJSONObject(m);
                    System.out.println(jsonObject1);
                    jsonArray3.remove(m);
                    String attributeTypeName = (String) jsonObject1.get("attributeTypeName");
                    if (!StringUtils.isEmpty(attributeTypeName)) {
                        Result result1 = TranslateUtil.translate(attributeTypeName);
                        wordCountAll += Integer.parseInt(result1.getNumber());
                        String translatedKey = result1.getText();
                        jsonObject1.put("attributeTypeName", translatedKey);
                    }
​
                    String attributeValueName = (String) jsonObject1.get("attributeValueName");
                    if (!StringUtils.isEmpty(attributeValueName)) {
                        Result result2 = TranslateUtil.translate(attributeValueName);
                        wordCountAll += Integer.parseInt(result2.getNumber());
                        String translatedValue = result2.getText();
                        jsonObject1.put("attributeValueName", translatedValue);
                    }
                    jsonArray3.add(m, jsonObject1);
                }
                jsonObject.put("attributes", jsonArray3);
            }
​
​
            String offerDescription = (String) jsonObject.get("offerDescription");
            if (!StringUtils.isEmpty(offerDescription)) {
                Result result2 = TranslateUtil.translate(offerDescription);
                wordCountAll += Integer.parseInt(result2.getNumber());
                String translatedOfferDescription = result2.getText();
                jsonObject.put("offerDescription", translatedOfferDescription);
            }
​
            String extraInfoMessage = (String) jsonObject.get("extraInfoMessage");
            if (!StringUtils.isEmpty(extraInfoMessage)) {
                Result result2 = TranslateUtil.translate(extraInfoMessage);
                wordCountAll += Integer.parseInt(result2.getNumber());
                String translatedExtraInfoMessage = result2.getText();
                jsonObject.put("extraInfoMessage", translatedExtraInfoMessage);
            }
​
            String manufacture = (String) jsonObject.get("manufacture");
            if (!StringUtils.isEmpty(manufacture)) {
                Result result2 = TranslateUtil.translate(manufacture);
                wordCountAll += Integer.parseInt(result2.getNumber());
                String translateManufacture = result2.getText();
                jsonObject.put("manufacture", translateManufacture);
            }
            jsonArray1.add(i,jsonObject);
        }
        coupangProduct.setProductProperties(jsonArray1.toJSONString());
        //更新coupangProduct进入数据库.
//        lisTranslateMapper.updateTranslated1(id,coupangProduct.getCoupangProName());
//        lisTranslateMapper.updateTranslated2(id, coupangProduct.getSkuProperty());
//        lisTranslateMapper.updateTranslated3(id, coupangProduct.getProductPropertys());
        lisCoupangProductMapper.insertByProduct(coupangProduct);
        //把username和wordcount更新进入数据库.
        //若不存在则先创建用户
//        LisTranslate lisTranslate = lisTranslateMapper.selectUser(username);
//        if(lisTranslate==null){
//            lisTranslateMapper.creatUser(username, String.valueOf(wordCountAll));
//            return coupangProduct ;
//        }else {
//            Integer  word = Integer.getInteger(lisTranslate.getWordcount())+wordCountAll;
//            lisTranslateMapper.updateUser(username,String.valueOf(word));
//        }
        return coupangProduct;
    }
//
​
    @Override
    public void translate(CoupangProduct product, BaseResult<CoupangProduct> result) {
        try {
            StringBuilder searchTags = new StringBuilder();
            StringBuilder notices = new StringBuilder();
            StringBuilder itemName = new StringBuilder();
            product.getSearchTags().forEach(s -> {
                searchTags.append(s+"#");
            });
            product.getNotices().forEach(a ->{
                notices.append(a+"#");
            });
            product.getItemName().forEach(n ->{
                itemName.append(n+"#");
            });
​
            List<String> attributeTypeNameList = product.getAttributeTypeNameList();
            List<String> attributeValueNameList = product.getAttributeValueNameList();
            StringBuilder attributeTypeName = new StringBuilder();
            StringBuilder attributeValueName = new StringBuilder();
            attributeTypeNameList.forEach(attributes -> {
                attributeTypeName.append(attributes+"#");
            });
​
            attributeValueNameList.forEach(attributes -> {
                attributeValueName.append(attributes+"#");
            });
            Result coupangProName = TranslateUtil.translate(product.getCoupangProName());
            Result brand = product.getBrand()==null?null:TranslateUtil.translate(product.getBrand());
            Result searchTag = searchTags.toString().isEmpty()?null:TranslateUtil.translate(searchTags.toString());
            Result itemNames = itemName.toString().isEmpty()?null:TranslateUtil.translate(itemName.toString());
            Result barcodeReason = product.getEmptyBarcodeReason()==null?null:TranslateUtil.translate(product.getEmptyBarcodeReason());
            Result notice = notices.toString().isEmpty()?null:TranslateUtil.translate(notices.toString());
            Result typeName = attributeTypeName.toString().isEmpty()?null:TranslateUtil.translate(attributeTypeName.toString());
            Result valueName = attributeValueName.toString().isEmpty()?null:TranslateUtil.translate(attributeValueName.toString());
​
            Integer coupangProNameNum = Integer.parseInt(coupangProName.getNumber());
            Integer brandNum = Integer.parseInt(brand==null?"0":brand.getNumber());
            Integer searchTagNum = Integer.parseInt(searchTag==null?"0":searchTag.getNumber());
            Integer itemNamesNum = Integer.parseInt(itemNames==null?"0":itemNames.getNumber());
            Integer barcodeReasonNum = Integer.parseInt(barcodeReason==null?"0":barcodeReason.getNumber());
            Integer noticeNum = Integer.parseInt(notice==null?"0":notice.getNumber());
            Integer typeNameNum = Integer.parseInt(typeName==null?"0":typeName.getNumber());
            Integer valueNameNum = Integer.parseInt(valueName==null?"0":valueName.getNumber());
            //文字用量统计总和(后面需要用)
            Integer sum = coupangProNameNum+brandNum+searchTagNum+itemNamesNum+barcodeReasonNum+noticeNum+typeNameNum+valueNameNum;
            BaseResult<Boolean> dataResult = new BaseResult<>();
            tErpUserDataStatisticsService.billingStatistics(2,sum,dataResult);
            if(!dataResult.isSuccess()){
                result.construct(dataResult.getMessage(),false);
                return;
            }
​
            CoupangProduct coupangProduct = new CoupangProduct();
            coupangProduct.setCoupangProName(coupangProName.getText());
            coupangProduct.setBrand(brand==null?null:brand.getText());
            coupangProduct.setSearchTags(searchTag==null?null:Arrays.asList((searchTag.getText()).split("#")));
            coupangProduct.setItemName(itemNames==null?null:Arrays.asList((itemNames.getText()).split("#")));
            coupangProduct.setEmptyBarcodeReason(barcodeReason==null?null:barcodeReason.getText());
            coupangProduct.setNotices(notice==null?null:Arrays.asList((notice.getText()).split("#")));
            coupangProduct.setAttributeTypeNameList(typeName==null?null:Arrays.asList((typeName.getText()).split("#")));
            coupangProduct.setAttributeValueNameList(valueName==null?null:Arrays.asList((valueName.getText()).split("#")));
​
​
//            CoupangProduct coupangProduct = new CoupangProduct();
//            coupangProduct.setCoupangProName(TranslateUtil.translate(product.getCoupangProName()).getText());
//            coupangProduct.setBrand(product.getBrand()==null?null:TranslateUtil.translate(product.getBrand()).getText());
//            coupangProduct.setSearchTags(searchTags.toString().isEmpty()?null:Arrays.asList((TranslateUtil.translate(searchTags.toString()).getText()).split(",")));
//            coupangProduct.setItemName(itemName.toString().isEmpty()?null:Arrays.asList((TranslateUtil.translate(itemName.toString()).getText()).split(",")));
//            coupangProduct.setEmptyBarcodeReason(product.getEmptyBarcodeReason()==null?null:TranslateUtil.translate(product.getEmptyBarcodeReason()).getText());
//            coupangProduct.setNotices(notices.toString().isEmpty()?null:Arrays.asList((TranslateUtil.translate(notices.toString()).getText()).split("#")));
//            coupangProduct.setAttributeTypeNameList(attributeTypeName.toString().isEmpty()?null:Arrays.asList((TranslateUtil.translate(attributeTypeName.toString()).getText()).split(",")));
//            coupangProduct.setAttributeValueNameList(attributeValueName.toString().isEmpty()?null:Arrays.asList((TranslateUtil.translate(attributeValueName.toString()).getText()).split(",")));
            result.construct("成功",true,coupangProduct);
        }catch (Exception e){
            e.printStackTrace();
        }catch (Throwable t){
            t.printStackTrace();
        }
    }
​
}
​

涉及到json疯狂嵌套的问题,又不能直接全翻译了 那样会导致浪费.所以就解析着翻译.

然后存入新的表中.

通过原表中的字段表征是否被翻译过,避免重复翻译.

通用版和专业版都对接了.基本没啥区别,就是调接口的时候参数不一样.

1.4 aop搭建全局异常处理与日志系统:

使用自定义注解.

package com.elevator.common.annotation;
​
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
/**
 * 记录接口调用情况注解
 * @Author A1yCE
 * @Date 2023-11-26 15:22
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiCallLog {
}

然后在每一模块内定义逻辑.

package com.elevator.unit.aop;
​
import com.elevator.common.utils.UserUtils;
​
import com.elevator.system.client.SystemClient;
import com.elevator.system.dto.BaseResponseDTO;
import com.elevator.system.dto.UserDto;
import com.elevator.unit.entity.ApiCallLog;
import com.elevator.unit.repository.ApiCallLogRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
​
import java.util.List;
import java.util.stream.Collectors;
​
/**
 * @Author A1yCE
 * @Date 2023-11-26 15:23
 **/
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class ApiAdvice {
    private final SystemClient systemClient;
    private final ApiCallLogRepository apiCallLogRepository;
​
    @Pointcut("@annotation(com.elevator.common.annotation.ApiCallLog)")
    private void apiAdvicePointCut(){}
​
    @Before("apiAdvicePointCut()")
    public void apiAdvice(){
        try {
            ApiCallLog apiCallLog = new ApiCallLog();
            Long userId = UserUtils.getUserId(); // 用户id
            String unitType = UserUtils.getUnitType(); // 单位名称
            BaseResponseDTO<UserDto> userDto = systemClient.findUserById(userId);
            String username = userDto.getData().getUsername(); // 账号
            String nickName = userDto.getData().getNickName(); // 真名
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String operation = attributes.getRequest().getRequestURI(); // 操作的接口
            List<String> roleIds = UserUtils.getRoleIds();
            List<Long> idList = roleIds.stream().map(Long::new).collect(Collectors.toList());
            BaseResponseDTO<List<String>> roleListDto = systemClient.findNameByIdList(idList);
            List<String> roleList = roleListDto.getData();
            String role = String.join(",",roleList); // 角色
            apiCallLog.setUserId(userId);
            apiCallLog.setUserName(username);
            apiCallLog.setNickName(nickName);
            apiCallLog.setRole(role);
            apiCallLog.setOperation(operation);
            apiCallLog.setUnitType(unitType);
            apiCallLogRepository.save(apiCallLog);
        } catch (Exception e) {
            log.error("服务器内部错误");
        }
    }
}
  1. 切面(Aspect): 切面是横切关注点的模块化单元。它定义了在何处以及如何应用横切关注点。切面包含了通知(Advice)和切点(Pointcut)。

  2. 通知(Advice): 通知是切面的具体行为。它定义了在切点处执行的代码。AOP有几种类型的通知,包括前置通知(Before Advice)、后置通知(After Advice)、返回通知(After Returning Advice)、异常通知(After Throwing Advice)和环绕通知(Around Advice)。

    • 前置通知(Before Advice): 在切点之前执行的通知。

    • 后置通知(After Advice): 在切点之后执行的通知,不考虑方法的成功或失败。

    • 返回通知(After Returning Advice): 在切点方法成功返回后执行的通知。

    • 异常通知(After Throwing Advice): 在切点方法抛出异常后执行的通知。

    • 环绕通知(Around Advice): 包围切点方法的通知,可以在方法调用前后执行自定义的行为。

  3. 切点(Pointcut): 切点定义了在应用通知的位置。它是一个表达式,描述了哪些连接点(Join Points)会触发通知。连接点是程序执行的点,例如方法调用、异常抛出等。

  4. 连接点(Join Point): 连接点是在应用程序执行过程中可以插入切面的点。例如,方法的调用是一个连接点。

  5. 织入(Weaving): 织入是将切面应用到目标对象并创建一个新的代理对象的过程。有三种织入方式:编译时织入(Compile-Time Weaving)、类加载时织入(Load-Time Weaving)和运行时织入(Runtime Weaving)。

    • 编译时织入: 在编译时将切面织入目标代码,生成最终的字节码文件。

    • 类加载时织入: 在类加载到JVM时,通过类加载器将切面织入目标类。

    • 运行时织入: 在应用程序运行时,通过特定的代理机制将切面织入目标对象

使用注解定义切点的位置.

然后执行一个前置通知处理日志系统.通过解析jwt获取id,然后把相关参数存入数据库.

然后执行一个环绕通知处理异常,发生异常时,直接返回前端相应的信息,然后同样执行日志存储的逻辑.

1.5 线程池订单数据:

1.业务场景:订单写入

主要还涉及到一些数据处理和翻译.

2.线程池:

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;

只要有并发的地方、任务数量大或小、每个任务执行时间长或短的都可以使用线程池;

总体来说,线程池有如下的优势:

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 image-20231202151848985

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
​
public class ThreadPoolExample {
​
    public static void main(String[] args) {
        // 创建固定大小的线程池,线程数为5
        ExecutorService executorService = Executors.newFixedThreadPool(5);
​
        // 提交任务给线程池
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executorService.submit(() -> {
                System.out.println("Executing task: " + taskId + " on thread " + Thread.currentThread().getName());
            });
        }
        // 关闭线程池
        executorService.shutdown();
    }
}

1.6 分布式定时任务:

刷本系统内的库存信息:就是把国内平台采集的库存数同步到韩国数据表中.

目前是单机,但是后续有分布式需求.

分布式定时任务是指在一个分布式系统中,多个节点(服务器或服务实例)协同合作执行定时任务的一种机制。在传统的单体应用中,定时任务通常是在单一的应用实例中运行的,但在分布式系统中,存在多个独立的节点,每个节点都可能需要执行定时任务。

使用redis分布式锁实现.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
​
@Component
public class DistributedLock {
​
    private static final String LOCK_KEY = "my_task_lock";
​
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
​
    public boolean tryLock() {
        return redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, "locked");
    }
​
    public void releaseLock() {
        redisTemplate.delete(LOCK_KEY);
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyScheduledTask {

    @Autowired
    private DistributedLock distributedLock;

    @Scheduled(cron = "0/30 * * * * *") // 每30秒执行一次
    public void myTask() {
        // 尝试获取分布式锁
        if (distributedLock.tryLock()) {
            try {
                // 执行定时任务的逻辑
                System.out.println("定时任务执行时间:" + System.currentTimeMillis());
            } finally {
                // 释放分布式锁
                distributedLock.releaseLock();
            }
        } else {
            // 未获取到锁,说明有其他实例在执行任务
            System.out.println("未获取到分布式锁,任务未执行");
        }
    }
}

1.7 统计报表:

选择日期.

统计订单信息,金额信息,运费信息,物流信息等等.

这些信息都是已经在数据库中存好了的.只是需要进一步处理.

使用了mybatis的动态sql技术.

demo:

public class User {
    private Long id;
    private String username;
    private String email;
    // 省略其他属性、构造方法和 getter/setter
}
import java.util.List;

public interface UserMapper {
    
    List<User> getUserByCondition(User user);
}
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">

    <select id="getUserByCondition" resultType="com.example.model.User">
        SELECT * FROM user
        <where>
            <if test="id != null">
                AND id = #{id}
            </if>
            <if test="username != null">
                AND username = #{username}
            </if>
            <if test="email != null">
                AND email = #{email}
            </if>
        </where>
    </select>
</mapper>
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.List;

@Configuration
public class MyBatisConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }

    @Autowired
    private UserMapper userMapper;

    public void exampleMethod() {
        User queryUser = new User();
        queryUser.setUsername("testUser");

        List<User> users = userMapper.getUserByCondition(queryUser);
        // 处理查询结果
    }
}

持久化的优势:

  1. 历史数据分析: 如果你需要对历史数据进行分析或者生成历史趋势报表,持久化数据是很有帮助的。

  2. 性能优化: 对于一些复杂的统计计算,将结果持久化到数据库中可以减少重复计算的成本,提高查询性能。

  3. 报表的长期保存: 如果需要长期保存报表数据,而不仅仅是即时查询,那么将统计结果持久化到数据库中是比较常见的做法。

不持久化的优势:

  1. 实时性: 如果你的报表需要实时展示最新数据,而不是历史数据,那么不持久化可能更适合。

  2. 减少存储空间: 持久化数据会占用存储空间,如果数据的增长速度较快,而且不需要长期保存历史数据,可以考虑不持久化,只在需要时计算。

  3. 计算成本: 有时候,统计计算的成本可能较高,但是如果数据更新频繁,而报表查询并不频繁,那么实时计算可能更经济。

综合考虑:

  1. 业务需求: 确保你了解业务需求,如果业务需要展示历史趋势或者需要在报表中提供历史数据分析,那么持久化可能是有必要的。

  2. 性能需求: 如果数据量大、查询频繁,而且性能是关键因素,可能需要考虑持久化。

  3. 存储成本: 考虑存储成本和数据增长的速度,以及是否有必要长期保存历史数据。

  4. 实时性需求: 如果实时性是关键需求,而且计算成本可以接受,可以选择不持久化。

对于订单的金额相关信息,以整月的形式持久化.

对于其他的乱七八糟的信息,则现场查.

根据日期的查询 能查出来说明是持久化条件. 查不出来说明是不持久化的日期,那么就现场查,可能需要等一会.

1.8 项目工期把控与进度管理:

1.评估:

老师给出项目文档,分享需求,评估一下需要找多少人做,几个人大三的,几个大二的这样.

2.需求会:

我们和甲方都去实验室线下.跟甲方开需求会.明确需求,讲解需求.共同指定工期.

3.文档把控:

原先的方式是每个人每天在群里汇报:

昨日的总结

今日的工作

但是这种方式存在着很多的问题,因为学校这个情况,大部分人都是开发时间不均衡的.

所以变成文档的形式.

编写文档,包括谁 什么时间 什么内容 开发情况 对接情况 遇到的问题等 每个人每周一般是填一次.

然后负责人通过看文档,找到问题并联系相关同学解决.

2.电梯:

2.1 项目来源:

层层外包.

威海市政府原有电梯维保项目,但是不能满足日渐增加的需求,所以招标重构项目.

威海市广日电梯有限公司接到项目,然后外包给若维,然后若维的leader是我们学校的毕业生,再跟我们实验室共同开发.

负责管理电梯的方方面面,核心是三大工单,维保,救援,报修的业务逻辑,还有各种单位的互相交互,使用单位,维保单位,救援单位,应急处置单位,等.还有大屏展示等功能.

2.2 项目架构:

确定技术栈.

springcloud nacos gateway openfegin

确定用户认证体系.

springsecruity oauth2 :集成度高.方便扩展.

确定微服务模块划分.

微服务模块划分是设计和组织一个微服务架构时的关键步骤之一。

以下是一些通用的微服务模块划分原则:

  1. 单一职责原则(Single Responsibility Principle):

    • 每个微服务应该专注于一个特定的业务功能或业务流程,执行特定的业务操作。这有助于确保服务的职责清晰,易于维护和理解。

  2. 松耦合(Loose Coupling):

    • 微服务之间应该尽量减少相互依赖,避免紧密耦合。松耦合使得单个服务的修改不会对其他服务产生过多的影响,提高了系统的灵活性和可维护性。

  3. 高内聚(High Cohesion):

    • 一个微服务内的组件应该紧密相关,共同实现特定的业务功能。高内聚有助于确保服务的功能单一且容易维护。

  4. 界限上下文(Bounded Context):

    • 根据领域驱动设计(Domain-Driven Design,DDD)的原则,微服务的划分应该基于界限上下文,即每个微服务应该有明确定义的业务边界,服务内的模型和术语应该在该边界内保持一致。

  5. 可独立部署(Independent Deployment):

    • 每个微服务应该是独立可部署的单元。这意味着修改一个服务不应该影响其他服务,使得团队能够独立地开发、测试、部署和升级各个服务。

  6. 可伸缩性(Scalability):

    • 考虑到系统可能的未来需求,微服务的划分应该有助于系统的横向扩展。这意味着可以根据需要增加特定服务的实例数量,而不影响整体系统的性能。

  7. 通信开销最小化:

    • 服务之间的通信会引入一定的开销,因此应该尽量减少服务之间的通信量。可以采用事件驱动、异步通信等方式来降低直接同步调用的频率。

  8. 数据管理独立性:

    • 每个微服务应该有自己的数据存储,并通过 API 提供对数据的访问。避免直接访问其他服务的数据库,以保持数据管理的独立性。

  9. 安全性和隔离性:

    • 考虑到微服务架构中的多个服务可能面临不同的安全需求,确保每个服务都有适当的安全措施。此外,服务之间应该有足够的隔离,以防止故障在整个系统中传播。

尽量减少模块之间的数据库调用.

尽量减少各个业务跨模块的调用.

确定人员分配.

2.3 CI/CD:

持续集成(Continuous Integration) 定义:持续频繁的(每天多次)将本地代码“集成”到主干分支,并保证主干分支可用

如果只做到持续集成的话其实没啥用,这个是通过规范实现的.

1.从git上拉取项目 git clone http://ruoweiedu.com:7000/elevator/elevator-backend.git;
2.创建本地开发分支:git checkout -b xxx_dev (xxx一般为姓名首字母小写,如hjm_dev);
3.每次写代码前,都要更新本地代码和master保持一直:git pull origin master;
4.代码提交需先push到自己的开发分支上:git add --all && git commit -m '日志信息' && git push origin xxx_dev;
5.手动将xxx_dev 合并到master分支,点击左侧合并请求合并分支到master

每个人在gitlab上有一个自己的分支.

进行代码开发完成后, add commit 到自己仓库 然后同步到远程仓库,然后手动合并分支到自己的仓库.并进行本地测试后.

这样就实现了基本的ci/cd流程.

持续交付(Continuous Delivery) 定义:是持续集成的下一步,持续频繁地将软件的新版本交付到类生产环境(类似于预发),交付给测试、产品验收。

持续部署(Continuous Deployment) 定义:是持续交付的下一步,“自动”将代码部署到生产环境

在这个项目里,开发阶段的生产服务器和测试服务器都是同一个,所以这两个其实是同一个概念.

而这一步是通过jenkins实现的.

具体来说就是,当master被合并时,唤醒gitlab hook,启动jenkins.

jenkins配置了一个简单风格的项目(性能原因)

源码为gitlab地址

触发器为gitlab hook

构建其实是不做任何操作的.

构建后操作 使用ssh连接服务器 上传最新的文件

并运行服务器上的部署脚本

在服务器上进行mvn打包 进程关闭 进程重启操作.

这样会出现 删除文件问题 目前没啥办法

还有自动化测试的问题.

apifox这个软件可以管理接口信息和测试用例,适用于本地测试.

还可以使用idea插件自动的生成测试文档,避免了代码侵入性.

还有就是下面要讲的这个测试软件andulir.

2.4 自动化迁移:

原有的数据库是在postgres,新系统因为国产化需求需要进行数据库的迁移.

主要是一个shell 脚本 一个python脚本 一个java脚本实现的.

需求:按列迁移.

方案: 首先给到的是一整个数据库的备份

python脚本切分文件

找到想要的表

java改变文件格式

导入postgers

shell启动达梦 最后一步需要手动迁移.

2.5 外部对接方案:

电梯保险数据接收.

工单写入

电梯运行状态等

安全性:appkey secret

幂等性: redis自增函数获取唯一的操作id

携带id访问内部接口

分布式锁.保证幂等性.

2.6 部署:

Kubernetes(K8s)是一个开源的容器编排平台,用于自动化容器化应用程序的部署、扩展和运维。以下是使用Kubernetes部署项目的一般流程:

  1. 准备工作

    • 安装和配置kubectl:kubectl是与Kubernetes集群进行交互的命令行工具。你需要安装并配置kubectl,以便连接到你的Kubernetes集群。

    • 安装和配置Kubernetes集群:你可以选择使用各种工具,如Minikube(本地开发和测试)、kubeadm、或云服务提供商的托管Kubernetes集群。

  2. 容器化应用

    • 将你的应用程序容器化:使用Docker或其他容器化工具将你的应用程序打包为Docker镜像。确保Docker镜像包含应用程序的所有依赖项。

  3. 创建Kubernetes配置文件

    • 编写Kubernetes配置文件(YAML格式):定义Deployment、Service、ConfigMap等Kubernetes资源的配置。配置文件描述了你的应用程序的部署和服务配置。

  4. 部署应用程序

    • 使用kubectl apply命令应用你的Kubernetes配置文件:这将在Kubernetes集群中创建相应的资源(Deployment、Service等)来部署和运行你的应用程序。

bashCopy codekubectl apply -f your-deployment.yaml
kubectl apply -f your-service.yaml

2.7 websocket rocket mq:

工单完成通知 order模块通知 unit模块

大屏展示功能 通过socket实现工单监控数据的变化.

3.Andulir:

在项目实践中开发的开源项目.

用于本地测试和项目上线的自动化测试.

使用流程:

引入

注解

启动项目

修改xml配置

重新启动

架构:

数据解析器:通过注解搜索到方法 生成对应的xml结构 持久化为一个xml文件.

数据生成器:根据xml文件 随机的生成测试数据

测试工具:

CommandLineRunner 是 Spring 框架中用于构建命令行应用程序的接口。它属于 org.springframework.boot 包,提供了在 Spring Boot 应用程序启动时运行代码的方式。该接口只包含一个方法:run

@Autowired
private ApplicationContext applicationContext;

测试的时候通过反射生成的对象不能注入 所以要通过上面的方法获取bean.

然后还有 git diff 命令 openfegin 前端 自动化测试. 管理测试用例的概念.

计划为其设计golang版本.

4.低代码综测平台: