基于Github Action 配置Java Python Go. Rust Nodejs C++ 实现自动发布功能(不基于Jenkins的快速敏捷发布体系)

  • 需求:基于Github Action 配置Java Python Go. Rust Nodejs  C++ 实现自动发布功能。
  • 目标:基于Github+Argocd实现不基于Jenkins的快速敏捷发布体系,覆盖服务为:容器+非容器服务。
  • 实现思路:容器基于github action cicd后把最新镜像版本更新到Kustomize代码仓库,非容器的基于github action 调用ansible 进行服务的分发。
  • Java(以此为举例重点,其他语言可以参考做适配即可)
  •  1 # set Amazon Corretto 17 as base image
     2 FROM amazoncorretto:17-al2023-jdk
     3 # set working directory
     4 WORKDIR /data
     5 # set environment variables
     6 ARG JAR_FILE=target/pb-trading-monitor-*.jar
     7 # copy jar file to the container
     8 COPY ${JAR_FILE} api/pb-trading-monitor.jar
     9 # set environment variables
    10 ENV SERVICE_NAME=pb-trading-monitor
    11 
    12 RUN mkdir -p /data/logs
    13 
    14 ENTRYPOINT [ \
    15   "/bin/bash", \
    16   "-c", \
    17   "java \
    18     --enable-preview \
    19     -Xms${JAVA_OPT_XMS} \
    20     -Xmx${JAVA_OPT_XMX} \
    21     -XX:+UseG1GC \
    22     -XX:MaxGCPauseMillis=100 \
    23     -Dfile.encoding=UTF-8 \
    24     -Dspring.profiles.active=${JAVA_OPT_ENV} \
    25     -Dlogging.file.path=/data/logs \
    26     -jar /data/api/pb-trading-monitor.jar" ]
    Java-Dockerfile
     1 name: nonprod-cicd
     2 
     3 on:
     4   push:
     5     branches:
     6       - dev
     7       - sit
     8       - uat
     9       - qa
    10       - fat
    11 
    12 jobs:
    13   maven_build:
    14     uses: Tech/devops-cicd/.github/workflows/javaci.yaml@main
    15     with:
    16       K8S: true
    17     secrets:
    18       SRE_LTP_TOKEN: ${{ secrets.SRE_TOKEN }}
    19   deploy:
    20     needs: maven_build
    21     uses: Tech/devops-cicd/.github/workflows/javacd.yaml@main
    22     with:
    23       K8S: true
    24     secrets:
    25       ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
    .github/workflows/nonpro-cicd.yaml

     

  •   1 name: Reusable Build Template
      2 
      3 env:
      4   PROJECT_NAME: ${{ github.repository }}
      5   COMMIT_ID: ${{ github.sha }}
      6   BASE_DIR: /data/mnt
      7   ref: ${{ github.ref_name }}
      8 on:
      9   workflow_call:  # 允许被其他工作流调用
     10     inputs:
     11       JAR:
     12         required: false
     13         type: string
     14       K8S:
     15         required: false
     16         type: boolean
     17         default: false
     18       REPO_NAME:
     19         required: false
     20         type: string
     21     secrets:
     22       SRE_LTP_TOKEN:
     23         description: "Token for SRE"
     24         required: true
     25 
     26 jobs:
     27   git_checkout:
     28     name: Git_checkout
     29     runs-on: [ self-hosted,ltp ]
     30     if: ${{ !contains('qa,fat', github.ref_name) || !contains(fromJSON('["pb-trading-engine", "backend-ltp-config-project", "ltp-exchange-data-server", "pb-trading-dump", "pb-trading-engine","pb-trading-market","pb-trading-monitor","pb-trading-push","pb-trading-query","pb-trading-statistics","pb-trading-transfer","rapidtrade-data-push","rapidx-clearing-accounting","rapidx-trading-algo-server","rapidx-trading-clearing","rapidx-trading-onezero-maker","rapidx-trading-query-persistent","rapidx-trading-query-realtime"]'), github.event.repository.name) }}
     31 
     32     steps:
     33       - name: Checkout
     34         uses: actions/checkout@v3
     35         # with:
     36         #   ref: ${ref}
     37       # - name: diff
     38       #   run: |
     39       #     git diff remotes/origin/main qa >diff.txt
     40 
     41       - name: Instead of setting.xml's token
     42         run: |
     43           sed -i "s|SRE_LTP_TOKEN|${{secrets.SRE_TOKEN}}|g" .github/workflows/settings.xml
     44 
     45       - name: Set Dockerfile stage
     46         if: ${{ inputs.k8s == true }}
     47         #后续调整为deployment内的env控制
     48         run: |
     49           if [[ "${{ github.ref_name }}" == "release" ]]; then
     50           ref="prod"
     51           elif [[ "${{ github.ref_name }}" == Uat* ]]; then
     52           ref="uat"
     53           elif [[ "${{ github.ref_name }}" == Prod* ]]; then
     54           ref="prod"
     55           elif [[ "${{ github.ref_name }}" == "feat/release_uat" ]]; then
     56           ref="uat"
     57           elif [[ "${{ github.ref_name }}" == "feat/shard_release_uat" ]]; then
     58           ref="uat"
     59           elif [[ "${{ github.ref_name }}" == *"qa"* ]]; then
     60           ref="qa"
     61           elif [[ "${{ github.ref_name }}" == *"sit"* ]]; then
     62           ref="sit"
     63           elif [[ "${{ github.ref_name }}" == *"dev"* ]]; then
     64           ref="dev"
     65 
     66           fi
     67           sed -i "s/stage/${ref}/g" Dockerfile
     68 
     69       - name: Build with Maven
     70         run: mvn -B -U clean  package -s .github/workflows/settings.xml
     71 
     72       # - name: Build and Save to NFS
     73       #   if: ${{ inputs.k8s == false }}
     74       #   run: |
     75       #     echo $BASE_DIR/$PROJECT_NAME/$COMMIT_ID
     76       #     mkdir -p $BASE_DIR/$PROJECT_NAME/$COMMIT_ID
     77           
     78       #     echo "Artifact copied to $BASE_DIR/$PROJECT_NAME/$COMMIT_ID"
     79       - name: Build and Save to NFS
     80         run: |
     81           git fetch origin release
     82           git diff origin/release HEAD > diff.txt
     83           # rm -rf $BASE_DIR/$PROJECT_NAME/$COMMIT_ID/
     84           mkdir -p $BASE_DIR/$PROJECT_NAME/$COMMIT_ID
     85           cp -rf target/${{ inputs.REPO_NAME }}*.jar $BASE_DIR/$PROJECT_NAME/$COMMIT_ID/ || true
     86           cp -rf .* $BASE_DIR/$PROJECT_NAME/$COMMIT_ID/ || true
     87 
     88 
     89       - name: Configure AWS credentials
     90         if: ${{ inputs.k8s == true }}
     91         uses: aws-actions/configure-aws-credentials@v1
     92         with:
     93           aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
     94           aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
     95           aws-region: ap-northeast-1
     96       - name: Login to Amazon ECR
     97         if: ${{ inputs.k8s == true }}
     98         id: login-ecr
     99         uses: aws-actions/amazon-ecr-login@v1
    100       - name: Build, tag, and push image to Amazon ECR
    101         if: ${{ inputs.k8s == true }}
    102         env:
    103           ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
    104           # ECR_REPOSITORY: ${REPO_NAME}
    105           IMAGE_TAG: ${{ github.sha }}
    106         run: |
    107           ECR_REPOSITORY=$(echo ${{ github.repository }}|cut -d'/' -f2)
    108           aws ecr create-repository --repository-name  ${ECR_REPOSITORY} > /dev/null 2>&1 || echo "仓库已存在,继续执行构建镜像操作"
    109           docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
    110           docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG 
    111       - name: custom script
    112         if: github.ref_name == 'fat' || github.ref_name == 'qa'
    113         run: |
    114           app_name=$(echo ${{ github.repository }}|cut -d'/' -f2)
    115           env="${{ github.ref_name }}"
    116           curl -s "http://10.18.2.11/cov/cicd?app_name=$app_name&env=${env}" > /dev/null 2>&1 &
    117           
    javaci.yaml
      1 name: Reusable Build Template
      2 
      3 env:
      4   ref: ${{ github.ref_name }}
      5   SRC_DIR: /data/mnt/${{ github.repository }}/${{ github.sha }}/
      6   # DIR: /data/jar/${{ github.repository }}/${{ github.sha }}
      7 on:
      8   workflow_call:  # 允许被其他工作流调用
      9     inputs:
     10       IP:
     11         required: false
     12         type: string
     13       JAR:
     14         required: false
     15         type: string
     16       DIRECTORY:
     17         required: false
     18         type: string
     19         default: "/data/www"
     20       SCRIPT:
     21         required: false
     22         type: string
     23         default: "start.sh"
     24       K8S:
     25         required: false
     26         type: boolean
     27         default: false
     28       REPO_NAME:
     29         required: false
     30         type: string
     31       JVM_OPTIONS:
     32         required: false
     33         type: string
     34         default: "-Xmx2G -Xms2G -XX:+UseG1GC -XX:+UseStringDeduplication"
     35     secrets:
     36       ACCESS_TOKEN:
     37         required: false
     38         description: "Access token for Kustomize repository"
     39 jobs:
     40   deploy:
     41     runs-on: [ self-hosted,ltp]
     42     #if: (!contains('qa,fat', github.ref_name) && !contains(fromJSON('["pb-trading-engine", "backend-ltp-config-project", "ltp-exchange-data-server", "pb-trading-dump", "pb-trading-engine","pb-trading-market","pb-trading-monitor","pb-trading-push","pb-trading-query","pb-trading-statistics","pb-trading-transfer","rapidtrade-data-push","rapidx-clearing-accounting","rapidx-trading-algo-server","rapidx-trading-clearing","rapidx-trading-onezero-maker","rapidx-trading-query-persistent","rapidx-trading-query-realtime"]'), github.event.repository.name))
     43     steps:
     44       - name: Copy artifact to server
     45         if: ${{ inputs.k8s == false }}
     46         env:
     47           JVM_OPTIONS: ${{ inputs.JVM_OPTIONS }}
     48         run: |
     49           repo_name=$(echo ${{ github.repository }}|cut -d'/' -f2)
     50           echo $repo_name
     51           # 替换生产环境的env
     52           if [[ "${{ github.ref_name }}" == "release" ]]; then
     53           ref="prod"
     54           elif [[ "${{ github.ref_name }}" == Uat* ]]; then
     55           ref="uat"
     56           elif [[ "${{ github.ref_name }}" == UAT* ]]; then
     57           ref="uat"
     58           elif [[ "${{ github.ref_name }}" == ProdJP* ]]; then
     59           ref="prod"
     60           elif [[ "${{ github.ref_name }}" == Prod* ]]; then
     61           ref="prod"
     62           elif [[ "${{ github.ref_name }}" == Mirror1* ]]; then
     63           ref="mirror1"
     64           elif [[ "${{ github.ref_name }}" == Mirror2* ]]; then
     65           ref="mirror2"
     66           elif [[ "${{ github.ref_name }}" == Mirror3* ]]; then
     67           ref="mirror3"
     68           elif [[ "${{ github.ref_name }}" == "feat/release_uat" ]]; then
     69           ref="uat"
     70           elif [[ "${{ github.ref_name }}" == "feat/shard_release_uat" ]]; then
     71           ref="uat"
     72           elif [[ "${{ github.ref_name }}" == *"qa"* ]]; then
     73           ref="qa"
     74           elif [[ "${{ github.ref_name }}" == *"sit"* ]]; then
     75           ref="sit"
     76           elif [[ "${{ github.ref_name }}" == *"dev"* ]]; then
     77           ref="dev"
     78 
     79           
     80           fi
     81 
     82           cd /etc/ansible && ~/ansible-playbook -i inventory/$ref deploy.yml \
     83             --extra-vars "host=${repo_name} SRC_DIR=${{ env.SRC_DIR }} directory=/data/jar/${repo_name}/${{ github.sha }} script=${{ inputs.SCRIPT }} commit_id=${{ github.sha }} start_script=/data/scripts/${repo_name}.sh  jvm_args='${JVM_OPTIONS}'"
     84 
     85       - name: Checkout kustomize repo
     86         if: ${{ inputs.K8S == true }}
     87         uses: actions/checkout@v3
     88         with:
     89           repository: Tech/Kustomize
     90           ref: main
     91           token: ${{ secrets.ACCESS_TOKEN }}
     92       - name: modify kustomize file
     93         if: ${{ inputs.k8s == true }}
     94         run: |
     95           # REPO_NAME=$(echo ${{ github.repository }}|cut -d'/' -f2)
     96           if [[ "${{ github.ref_name }}" == Uat* ]]; then
     97           ref="uat"
     98           elif [[ "${{ github.ref_name }}" == ProdJP* ]]; then
     99           ref="prodjp"
    100           elif [[ "${{ github.ref_name }}" == Prod* ]]; then
    101           ref="prod"
    102           elif [[ "${{ github.ref_name }}" == Mirror1* ]]; then
    103           ref="mirror1"
    104           elif [[ "${{ github.ref_name }}" == Mirror2* ]]; then
    105           ref="mirror2"
    106           elif [[ "${{ github.ref_name }}" == Mirror3* ]]; then
    107           ref="mirror3"
    108           elif [[ "${{ github.ref_name }}" == "feat/release_uat" ]]; then
    109           ref="uat"
    110           elif [[ "${{ github.ref_name }}" == "feat/shard_release_uat" ]]; then
    111           ref="uat"
    112           elif [[ "${{ github.ref_name }}" == *"qa"* ]]; then
    113           ref="qa"
    114           elif [[ "${{ github.ref_name }}" == *"sit"* ]]; then
    115           ref="sit"
    116           elif [[ "${{ github.ref_name }}" == *"dev"* ]]; then
    117           ref="dev"
    118           elif [[ "${{ github.ref_name }}" == "release" ]]; then
    119           ref="prod"
    120 
    121           fi
    122           echo "Using ref: $ref"
    123           if ${{ inputs.REPO_NAME != '' }} ; then
    124             REPO_NAME=${{ inputs.REPO_NAME }}
    125             echo  $REPO_NAME
    126             sed -i "s/\(newTag:\ \).*/\1\"${{ github.sha }}\"/g" backend/${REPO_NAME}/overlays/${ref}/kustomization.yaml
    127             else
    128             REPO_NAME=$(echo ${{ github.repository }}|cut -d'/' -f2)
    129           fi
    130 
    131           sed -i "s/\(newTag:\ \).*/\1\"${{ github.sha }}\"/g" backend/${REPO_NAME}/overlays/${ref}/kustomization.yaml
    132           if [[ "$REPO_NAME" == "pb-trading-dump" && ( "$ref" == "dev" || "$ref" == "sit" ) ]] ; then
    133             sed -i "s/\(newTag:\ \).*/\1\"${{ github.sha }}\"/g" backend/${REPO_NAME}/overlays/${ref}*/kustomization.yaml
    134           fi
    135           git config --global --add safe.directory /__w/${REPO_NAME}/${REPO_NAME}
    136           git config --global user.name "${{ github.event.head_commit.author.name }}"
    137           git config --global user.email ${{ github.event.head_commit.author.email }}
    138           # 仅在检测到更改时提交和推送
    139           git add .
    140           if git diff --quiet && git diff --cached --quiet; then
    141             echo "✅ 没有检测到文件更改,跳过提交步骤"
    142           else
    143             echo "✅ 检测到文件更改,准备提交......"
    144             git commit -m "${{ github.event.head_commit.message }}"
    145             git push
    146           fi
    147   wiz_scan:
    148     name: wiz_scan
    149     runs-on: [ self-hosted,ltp ]
    150     if: startsWith(github.ref, 'refs/tags/Prod')
    151     steps:
    152       - name: wiz_scan
    153         env:
    154           APIIRO_API_KEY: ${{ secrets.APIIRO_API_KEY }}
    155           APIIRO_API_URL: "https://app.test.com"
    156         run: |
    157           echo ${{ github.server_url }}/${{ github.repository }} ${{ github.ref_name }}
    158           cd /data/security-scan/
    159           git pull
    160           python3 securityscan.py sast --repositoryurl ${{ github.server_url }}/${{ github.repository }}  --commitsha ${{ github.sha }} --branch release || true
    161           # python3 securityscan.py sast --repositoryurl ${{ github.server_url }}/${{ github.repository }}.git  --commitsha ${{ github.sha }} --branch ${{ github.ref_name }} --debug &
    javacd.yaml
     1 - name: Validate host group and deploy
     2   hosts: localhost
     3   gather_facts: false
     4   vars:
     5     valid_host_groups: "{{ groups.keys() | list }}"
     6   tasks:
     7     - name: Check if 'host' is defined
     8       fail:
     9         msg: |
    10           ❌ 错误: 必须提供 host 参数,联系运维人员添加,使用方式:
    11           -e "host=your_group"
    12       when: host is not defined
    13 
    14     - name: Check if host group exists
    15       fail:
    16         msg: |
    17           ❌ 错误: 主机组 '{{ host }}' 不存在!,联系运维人员添加
    18 
    19           请检查 inventory 文件: /etc/ansible/inventory/dev
    20       when: host not in valid_host_groups
    21 
    22     - name: Check if host group has enough members
    23       fail:
    24         msg: |
    25           ❌ 错误: 主机组 '{{ host }}' 的成员不足(至少1个),联系运维人员添加
    26           当前主机组 '{{ host }}' 包含 {{ groups[host] | length }} 个主机
    27       when: groups[host] | length < 1
    28 
    29 
    30 - import_playbook: javacd.yml
    deploy.yaml

    以上通过在代码仓库定义Dockerfile+nonpro-cicd.yaml 进行自动触发到javaci.yaml进行编译后,再通过javacd.yaml进行镜像或者jar包的分发实现2种部署方式共存。仅供参考,请勿照搬。

  • Python
  •  1 # 使用官方 Python 3.10 镜像作为基础镜像
     2 FROM python:3.10-slim
     3 
     4 # 设置工作目录
     5 WORKDIR /app
     6 
     7 # 设置环境变量
     8 ENV PYTHONUNBUFFERED=1 \
     9     PYTHONDONTWRITEBYTECODE=1
    10 
    11 # 安装系统依赖
    12 RUN apt-get update && \
    13     apt-get install -y --no-install-recommends \
    14     gcc \
    15     && rm -rf /var/lib/apt/lists/*
    16 
    17 # 复制依赖文件
    18 COPY requirements.txt .
    19 
    20 # 安装 Python 依赖
    21 RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
    22 
    23 # 复制项目文件
    24 COPY . .
    25 
    26 # 创建日志目录
    27 RUN mkdir -p /app/logs
    28 
    29 # 设置启动命令
    30 CMD ["python", "run_all.py"]
    Python-Dockerfile

     

  • Go
  • # 第一阶段:编译Go程序(builder)
    FROM golang:1.25 AS builder
    
    WORKDIR /src
    
    # 复制依赖文件并下载
    COPY ./go.mod ./go.sum ./
    RUN go mod download
    
    # 复制源代码
    COPY ./ .
    
    RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/app .
    
    FROM alpine:3.20
    
    RUN apk add --no-cache ca-certificates tzdata \
      && adduser -D -H -u 10001 appuser
    
    WORKDIR /
    
    COPY --from=builder /out/app /app
    COPY --from=builder /src/config.json /config.json
    
    RUN chown appuser:appuser /config.json && chmod 644 /config.json
    
    USER appuser
    
    EXPOSE 8080
    
    # Default config path expected by the app is /opt/xidp/conf/config.yml
    ENTRYPOINT ["/app","-env","pro"]
    Go-dockerfile

     

  • Rust
  •  1 # 使用指定版本的 bookworm 基础镜像
     2 FROM rust:1.88.0-bookworm AS chef
     3 RUN cargo install cargo-chef
     4 
     5 FROM chef AS planner
     6 WORKDIR /app
     7 COPY . .
     8 RUN cargo chef prepare --recipe-path recipe.json
     9 
    10 FROM chef AS builder
    11 WORKDIR /app
    12 
    13 # 安装系统依赖
    14 RUN apt-get update && apt-get install -y \
    15     pkg-config \
    16     libssl-dev \
    17     libsasl2-dev \
    18     ca-certificates \
    19     && rm -rf /var/lib/apt/lists/*
    20 
    21 # 构建依赖
    22 COPY --from=planner /app/recipe.json recipe.json
    23 RUN cargo chef cook --release --recipe-path recipe.json
    24 
    25 # 构建应用
    26 COPY . .
    27 RUN cargo build --release --bin rust_risk_manager
    28 
    29 # 运行时镜像 - 使用相同的 bookworm 版本
    30 FROM debian:bookworm-slim
    31 WORKDIR /app
    32 
    33 # 安装运行时依赖
    34 RUN apt-get update && apt-get install -y \
    35     libssl3 \
    36     libsasl2-2 \
    37     ca-certificates \
    38     curl \
    39     && rm -rf /var/lib/apt/lists/*
    40 
    41 # 复制二进制文件和配置文件
    42 COPY --from=builder /app/target/release/rust_risk_manager /app/rust_risk_manager
    43 COPY config/ ./config/
    44 
    45 # 创建必要的目录
    46 RUN mkdir -p logs
    47 
    48 # 暴露端口(根据您的应用需要调整)
    49 EXPOSE 8080
    50 
    51 # 设置环境变量
    52 ENV RUST_LOG=info
    53 ENV RUST_BACKTRACE=1
    54 
    55 # 设置容器启动时运行的命令
    56 CMD ["./rust_risk_manager"]
    Rust-Dockerfile

     

  • C++
  •  1 # 运行阶段 - 使用轻量级基础镜像
     2 FROM ubuntu:22.04
     3 
     4 # 安装运行时依赖
     5 RUN apt-get update && apt-get install -y \
     6     libssl3 \
     7     ca-certificates \
     8     && rm -rf /var/lib/apt/lists/*
     9 
    10 # 创建应用用户
    11 RUN useradd -r -s /bin/false appuser
    12 
    13 # 设置工作目录
    14 WORKDIR /data/app/
    15 
    16 # 从构建上下文复制可执行文件
    17 COPY .  .
    18 
    19 # 确保可执行文件有执行权限
    20 RUN chmod +x *
    21 
    22 # 切换到非特权用户
    23 USER appuser
    24 
    25 #RUN cp yml/stage/rapidtrade_order.yml  .
    26 
    27 # 设置启动命令,其中stage通过ci脚本去动态替换为不同环境的变量
    28 CMD ["nohup", "./rapidtrade_order", "rapidtrade_order.yml"]
    C++-Dockerfile

     

  • NodeJs
  •  1 FROM node:20-alpine AS base
     2 WORKDIR /app
     3 
     4 FROM base AS deps
     5 COPY package.json yarn.lock ./
     6 RUN yarn install --frozen-lockfile
     7 
     8 FROM base AS builder
     9 COPY --from=deps /app/node_modules ./node_modules
    10 COPY . .
    11 ENV NEXT_TELEMETRY_DISABLED=1
    12 RUN yarn build
    13 
    14 FROM base AS runner
    15 ENV NODE_ENV=production
    16 ENV NEXT_TELEMETRY_DISABLED=1
    17 
    18 # 运行时也可以覆盖 APP_ENV
    19 ARG APP_ENV=qa
    20 ENV APP_ENV=${APP_ENV}
    21 
    22 WORKDIR /app
    23 
    24 # 仅安装生产依赖,减小体积
    25 COPY package.json yarn.lock ./
    26 RUN yarn install --frozen-lockfile --production
    27 
    28 # 拷贝运行所需产物
    29 COPY --from=builder /app/public ./public
    30 COPY --from=builder /app/.next .//.next
    31 COPY --from=builder /app/next.config.mjs ./next.config.mjs
    32 
    33 COPY --from=builder /app/config ./config
    34 
    35 # 非 root 运行
    36 RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
    37 RUN mkdir -p /app/.next/cache/fetch-cache && \
    38     chown -R nextjs:nodejs /app/.next
    39 USER nextjs
    40 
    41 CMD ["yarn", "start"]
    Node-Dockerfile

     

 

posted @ 2025-11-11 00:00  meijinmeng  阅读(9)  评论(0)    收藏  举报