Skip to content

Latest commit

 

History

History
629 lines (500 loc) · 18.3 KB

CDDR-2407.md

File metadata and controls

629 lines (500 loc) · 18.3 KB

240729

API

You


根据以下 Python 代码,用 NextJS 实现相同功能,给我 TS 代码,给每个 TS 文件命名并告知放在哪里,给我实现需求的正确代码和必要说明。

import os
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 配置 SQLite 数据库路径
basedir = os.path.abspath(os.path.dirname(__file__))
db_path = os.path.join(basedir, '../../datas/db_planfit_res-20230719.db')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + db_path
db = SQLAlchemy(app)

# 定义 Resource 数据模型
class Resource(db.Model):
    __tablename__ = 'tb_res_zh'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    part_name = db.Column(db.String, nullable=False)
    model_id = db.Column(db.String, nullable=False)
    item_name = db.Column(db.String, nullable=False)
    tag_text = db.Column(db.String)
    video_url = db.Column(db.String)
    content = db.Column(db.String)

    def to_json(self):
        return {
            'id': self.id,
            'part_name': self.part_name,
            'model_id': self.model_id,
            'item_name': self.item_name,
            'tag_text': self.tag_text,
            'video_url': self.video_url,
            'content': self.content
        }

@app.route('/resources', methods=['GET'])
def get_resources():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)
    resources = Resource.query.limit(per_page).offset((page - 1) * per_page).all()
    return jsonify([resource.to_json() for resource in resources])

@app.route('/resources/<string:model_id>', methods=['GET'])
def get_resource(model_id):
    resource = Resource.query.filter_by(model_id=model_id).first()
    if resource is None:
        return jsonify({'message': 'Resource not found'})
    return jsonify(resource.to_json())

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9001, debug=True)

You


.env位于项目根目录,db_planfit_res-20230719.db应该位于哪里?


You


prisma.ts中的prisma编译标红提示:

Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.ts(7017)

什么意思?怎么解决?


You


resources/index.ts:

import { NextApiRequest, NextApiResponse } from 'next'
import prisma from '../../../lib/prisma'

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'GET') {
    console.log('---Call resources!')
    const page = Number(req.query.page) || 1
    const perPage = Number(req.query.per_page) || 10

    try {
      const resources = await prisma.resource.findMany({
        take: perPage,
        skip: (page - 1) * perPage,
      })

      console.log('---Database resources:', resources)
      res.status(200).json(resources)
    } catch (error) {
      console.error('---Database error:', error)
      res.status(500).json({ message: 'Error fetching resources' })
    }
  } else {
    res.setHeader('Allow', ['GET'])
    res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}

getResources.ts:

export interface ResourceRaw {
    content: string
    id: number
    item_name: string
    model_id: string
    part_name: string
    tag_text: string
    video_url: string
}

export interface Content {
    title: string
    text_list: TextList[]
}

interface TextList {
    description: string
    order_list: string[]
}


export type Resource = ReturnAsyncType<typeof getResourceList>[number]

export async function getResourceList({page, pageSize}: {page: number, pageSize: number}) {
    const res = await fetch(process.env.BASE_URL + "/resources" + `?page=${page}&perPage=${pageSize}`);
    console.log('---res:',res);

    const data = await res.json() as ResourceRaw[];
    return data.map((resource) => {
        let content = [] as Content[];
        try {
            console.log('---resource.content:',resource.content);
            content = JSON.parse(resource.content);
        } catch (e) {
            console.log('---JSON parse error:',e);
        }
        return {
            ...resource,
            content,
        }
    })
}

运行报错:

---res: Response {
  [Symbol(realm)]: null,
  [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: true,
    requestIncludesCredentials: true,
    type: 'default',
    status: 404,
    timingInfo: {
      startTime: 817032.7932090759,
      redirectStartTime: 0,
      redirectEndTime: 0,
      postRedirectStartTime: 817032.7932090759,
      finalServiceWorkerStartTime: 0,
      finalNetworkResponseStartTime: 0,
      finalNetworkRequestStartTime: 0,
      endTime: 0,
      encodedBodySize: 916,
      decodedBodySize: 0,
      finalConnectionTimingInfo: null
    },
    cacheState: '',
    statusText: 'Not Found',
    headersList: HeadersList {
      cookies: null,
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    },
    urlList: [ [URL] ],
    body: { stream: undefined, length: undefined, source: undefined }
  },
  [Symbol(headers)]: HeadersList {
    cookies: null,
    [Symbol(headers map)]: Map(10) {
      'cache-control' => [Object],
      'x-powered-by' => [Object],
      'etag' => [Object],
      'content-type' => [Object],
      'vary' => [Object],
      'date' => [Object],
      'keep-alive' => [Object],
      'content-encoding' => [Object],
      'connection' => [Object],
      'transfer-encoding' => [Object]
    },
    [Symbol(headers map sorted)]: null
  }
}
- error SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at AsyncResource.runInAsyncScope (node:async_hooks:203:9)
- error SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at AsyncResource.runInAsyncScope (node:async_hooks:203:9)

什么意思?怎么解决?


You


写一个 CURL 测试http://127.0.0.1:3001/api/resources


You


NextJS 项目如何新增 API?我在项目跟目录新增了/app/api/resources/index.ts,为什么curl -X GET "http://127.0.0.1:3001/api/resources?page=1&per_page=10"返回 404?

resources/index.ts

import { NextApiRequest, NextApiResponse } from 'next'
import prisma from '../../../lib/prisma'

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'GET') {
    console.log('---Call resources!')
    const page = Number(req.query.page) || 1
    const perPage = Number(req.query.per_page) || 10

    try {
      const resources = await prisma.resource.findMany({
        take: perPage,
        skip: (page - 1) * perPage,
      })

      console.log('---Database resources:', resources)
      res.status(200).json(resources)
    } catch (error) {
      console.error('---Database error:', error)
      res.status(500).json({ message: 'Error fetching resources' })
    }
  } else {
    res.setHeader('Allow', ['GET'])
    res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}

怎么排查和解决?给我具体的步骤和说明。


You


使用 NextJS App Router,给我写一个 helloworld API 示例代码。


You


import { respData, respErr } from "@/lib/resp";
import prisma from "../../../lib/prisma";

export async function POST(req: Request) {
  try {
    const body = await req.json();
    console.log(`---resources req=`, body);
    let { page, per_page } = body;
    if (page == 0) {
      page = 1;
    }
    if (per_page == 0) {
      per_page = 10;
    }
    const resources = await prisma.resource.findMany({
      take: per_page,
      skip: (page - 1) * per_page,
    });

    console.log("---resources:", resources);
    return respData(resources);
  } catch (e) {
    console.error("resources failed: ", e);
    return respErr("resources failed");
  }
}
curl -X POST http://localhost:3001/api/resources \
  -H "Content-Type: application/json" \
  -d '{"page": 1, "per_page": 10}'

运行报错:

---resources req= { page: 1, per_page: 10 }
resources failed:  PrismaClientInitializationError:
Invalid `prisma.resource.findMany()` invocation:


Error querying the database: unable to open database file: /Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/prisma/./datas/db_planfit_res-20230719.db
    at zr.handleRequestError (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:8581)
    at zr.handleAndLogRequestError (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:7697)
    at zr.request (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:7307)
    at async POST (webpack-internal:///(sc_server)/./app/api/resources/route.ts:20:27)
    at async eval (webpack-internal:///(sc_server)/./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/server/future/route-modules/app-route/module.js:249:37) {
  clientVersion: '5.0.0',
  errorCode: undefined
}

什么意思?怎么解决?


You


schema.prisma在项目根目录/prisma中, db_planfit_res-20230719.db在项目根目录下/datas/中, .env中的写的是DATABASE_URL="file:./datas/db_planfit_res-20230719.db"

schema.prisma:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

prisma.ts

import { PrismaClient } from '@prisma/client'

// 添加全局类型声明
declare global {
  var prisma: PrismaClient | undefined
}

let prisma: PrismaClient

if (process.env.NODE_ENV === 'production') {
  prisma = new PrismaClient()
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient()
  }
  prisma = global.prisma
}

export default prisma
> ls -alh
total 5624
drwxr-xr-x   3 kevin  staff    96B Jul 29 16:54 .
drwxr-xr-x  30 kevin  staff   960B Jul 29 16:54 ..
-rw-r--r--   1 kevin  staff   2.7M Jul 29 16:54 db_planfit_res-20230719.db

运行报错:

---resources req= { page: 1, per_page: 10 }
resources failed:  PrismaClientInitializationError:
Invalid `prisma.resource.findMany()` invocation:


Error querying the database: unable to open database file: /Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/prisma/./datas/db_planfit_res-20230719.db
    at zr.handleRequestError (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:8581)
    at zr.handleAndLogRequestError (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:7697)
    at zr.request (/Users/kevin/1-GR个人/16-XMDM项目代码/163-TruthAIOrg/truth-ai-fitness-frontend/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:7307)
    at async POST (webpack-internal:///(sc_server)/./app/api/resources/route.ts:20:27)
    at async eval (webpack-internal:///(sc_server)/./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/next/dist/server/future/route-modules/app-route/module.js:249:37) {
  clientVersion: '5.0.0',
  errorCode: undefined
}

什么意思?怎么解决?


You


不要使用绝对路径,如果部署在远程服务器或者 Vercel 云平台呢?


You


route.ts

import { respData, respErr } from "@/lib/resp";
import prisma from "../../../lib/prisma";

export async function POST(req: Request) {
  try {
    const body = await req.json();
    console.log(`---resources req=`, body);
    let { page, per_page } = body;
    if (page == 0) {
      page = 1;
    }
    if (per_page == 0) {
      per_page = 10;
    }
    const resources = await prisma.resource.findMany({
      take: per_page,
      skip: (page - 1) * per_page,
    });

    console.log("---resources:", resources);
    return respData(resources);
  } catch (e) {
    console.error("resources failed: ", e);
    return respErr("resources failed");
  }
}

将以下代码改为调用 POST API 方式。

getResources.ts:

export async function getResourceList({page, pageSize}: {page: number, pageSize: number}) {
    const res = await fetch(process.env.BASE_URL + "/resources" + `?page=${page}&perPage=${pageSize}`);
    console.log('---res:',res);

    if (!res.ok) {
        console.error('Failed to fetch:', res.status, res.statusText);
        throw new Error('Failed to fetch resources');
    }

    const data = await res.json() as ResourceRaw[];
    return data.map((resource) => {
        let content = [] as Content[];
        try {
            console.log('---resource.content:',resource.content);
            content = JSON.parse(resource.content);
        } catch (e) {
            console.log('---JSON parse error:',e);
        }
        return {
            ...resource,
            content,
        }
    })
}

You


export async function getResourceList({page, pageSize}: {page: number, pageSize: number}) {
    const res = await fetch(process.env.BASE_URL + "/resources", {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ page, per_page: pageSize })
    });

    console.log('---res:', res);

    if (!res.ok) {
        console.error('Failed to fetch:', res.status, res.statusText);
        throw new Error('Failed to fetch resources');
    }

    const data = await res.json() as ResourceRaw[];
    return data.map((resource) => {
        let content = [] as Content[];
        try {
            console.log('---resource.content:', resource.content);
            content = JSON.parse(resource.content);
        } catch (e) {
            console.log('---JSON parse error:', e);
        }
        return {
            ...resource,
            content,
        }
    });
}

运行报错:

Unhandled Runtime Error
Error: data.map is not a function

返回结构:

{
    "code": 0,
    "message": "ok",
    "data": [
        {
            "id": 1,
            "part_name": "背部",
            "model_id": "1001",
            "item_name": "硬拉",
            "tag_text": "全身",
            "video_url": "http://aifit.ifree258.top/training-videos/1001.mp4",
            "content": "[\n  {\n    \"title\": \"硬拉\",\n    \"text_list\": [\n      {\n        \"description\": \"全身\",\n        \"order_list\": []\n      }\n    ]\n  },\n  {\n    \"title\": \"教练的评论\",\n    \"text_list\": [\n      {\n        \"description\": \"这对你背部肌肉的发展是一个很好的锻炼,因为你身体后部的肌肉在承受重物时可以高度参与。如果你想拥有完美的背部肌肉,这是一项至关重要的锻炼!\",\n        \"order_list\": []\n      }\n    ]\n  },\n  {\n    \"title\": \"锻炼指南\",\n    \"text_list\": [\n      {\n        \"description\": \"开始的姿势\",\n        \"order_list\": [\n          \"1. 双脚分开与臀部同宽站立,杠铃放在脚前的地面上。\",\n          \"2. 弯曲你的臀部和膝盖,使你的身体向杆的方向降低,同时保持背部平坦。\",\n          \"3.用过手握杠铃,距离比肩宽稍宽。\",\n          \"4. 胸部向前推,眼睛稍微向上看。\"\n        ]\n      },\n      {\n        \"description\": \"如何锻炼\",\n        \"order_list\": [\n          \"1. 深呼吸,支撑你的身体。\",\n          \"2. 抬起杠铃,双脚穿过地面,伸展臀部和膝盖,直到你站直。\",\n          \"3.保持杠铃靠近身体,手臂伸直。\",\n          \"4. 一旦你到达顶部,保持这个位置一会儿。\"\n        ]\n      },\n      {\n        \"description\": \"呼吸控制\",\n        \"order_list\": [\n          \"1. 开始举重前先深呼吸。\",\n          \"2. 举起杠铃时呼气。\",\n          \"3.在抬举的顶端吸气。\"\n        ]\n      }\n    ]\n  },\n  {\n    \"title\": \"注意事项\",\n    \"text_list\": [\n      {\n        \"description\": \"\",\n        \"order_list\": [\n          \"1. 在整个举重过程中,背部保持平坦,核心部位保持活动。\",\n          \"2. 不要把背绕起来,也不要用力举杠铃。\",\n          \"3.避免猛拉或弹跳重物离开地面。\",\n          \"4. 确保使用适合你的体重。\"\n        ]\n      }\n    ]\n  }\n]"
        }
    ]
}

怎么解决?


You


根据以下代码,实现需求:

  • 根据model_id查询指定的resources
  • 给我 NextJS App Router TS 代码
@app.route('/resources', methods=['GET'])
def get_resources():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)
    resources = Resource.query.limit(per_page).offset((page - 1) * per_page).all()
    return jsonify([resource.to_json() for resource in resources])

@app.route('/resources/<string:model_id>', methods=['GET'])
def get_resource(model_id):
    resource = Resource.query.filter_by(model_id=model_id).first()
    if resource is None:
        return jsonify({'message': 'Resource not found'})
    return jsonify(resource.to_json())

app/api/resources/route.ts:

import { respData, respErr } from "@/lib/resp";
import prisma from "../../../lib/prisma";

export async function POST(req: Request) {
  try {
    const body = await req.json();
    console.log(`---resources req=`, body);
    let { page, per_page } = body;
    if (page == 0) {
      page = 1;
    }
    if (per_page == 0) {
      per_page = 10;
    }
    const resources = await prisma.resource.findMany({
      take: per_page,
      skip: (page - 1) * per_page,
    });

    console.log("---resources:", resources);
    return respData(resources);
  } catch (e) {
    console.error("resources failed: ", e);
    return respErr("resources failed");
  }
}