This commit is contained in:
vipg
2025-11-19 16:51:52 +08:00
parent b2882ed70a
commit 8a8dd48726
3 changed files with 72 additions and 50 deletions

View File

@@ -98,9 +98,9 @@ BEGIN
CREATE OR REPLACE VIEW country_info_view AS CREATE OR REPLACE VIEW country_info_view AS
SELECT SELECT
u.id AS country_id, u.id AS country_id,
n.name AS country_name, -- 国家名称 n.name AS name, -- 国家名称
c.code AS country_code, -- 国家代码 c.code AS code, -- 国家代码
f.flag AS country_flag -- 国旗信息 f.flag AS flag -- 国旗信息
FROM FROM
country u country u
LEFT JOIN LEFT JOIN

View File

@@ -3,20 +3,27 @@ import 'package:asset_assistant/utils/host_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
// 国家数据模型 // 国家数据模型新增flag字段
class Country { class Country {
final String countryId; final String countryId;
final String name; final String name;
final String code; final String code;
final String? flag; // 新增国旗字段
Country({required this.countryId, required this.name, required this.code}); Country({
required this.countryId,
required this.name,
required this.code,
this.flag, // 新增参数
});
// 从JSON构建对象 // 从JSON构建对象添加flag字段解析
factory Country.fromJson(Map<String, dynamic> json) { factory Country.fromJson(Map<String, dynamic> json) {
return Country( return Country(
countryId: json['country_id'], countryId: json['country_id'],
name: json['name'], name: json['name'],
code: json['code'], code: json['code'],
flag: json['flag'], // 解析国旗字段
); );
} }
} }
@@ -83,14 +90,33 @@ class _CountryPageState extends State<CountryPage> {
int _currentPage = 1; int _currentPage = 1;
final int _pageSize = 20; final int _pageSize = 20;
bool _hasMoreData = true; bool _hasMoreData = true;
late ScrollController _scrollController; // 优化滚动控制器
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scrollController = ScrollController();
_scrollController.addListener(_onScroll); // 注册滚动监听
_fetchCountries(); _fetchCountries();
} }
// 加载国家列表数据 @override
void dispose() {
_scrollController.dispose(); // 释放资源
super.dispose();
}
// 滚动监听处理加载更多
void _onScroll() {
if (_isLoading) return;
if (_hasMoreData &&
_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 200) {
_loadMore();
}
}
// 加载国家列表数据使用POST请求
Future<void> _fetchCountries({bool isRefresh = false}) async { Future<void> _fetchCountries({bool isRefresh = false}) async {
if (isRefresh) { if (isRefresh) {
setState(() { setState(() {
@@ -109,25 +135,27 @@ class _CountryPageState extends State<CountryPage> {
final baseUrl = HostUtils().currentHost; final baseUrl = HostUtils().currentHost;
final path = '/country/read'; final path = '/country/read';
final url = '$baseUrl$path'; final url = '$baseUrl$path';
// 打印请求基本信息
debugPrint('====== 开始请求国家列表 ======'); debugPrint('====== 开始请求国家列表 ======');
debugPrint('请求URL: $url'); debugPrint('请求URL: $url');
debugPrint('请求参数: page=$_currentPage, page_size=$_pageSize'); debugPrint('请求参数: page=$_currentPage, page_size=$_pageSize');
final dio = Dio(); final dio = Dio();
// 使用POST请求通过data传递表单数据适配后端form接收方式
final response = await dio.post( final response = await dio.post(
url, url,
queryParameters: { data: {
'page': _currentPage, 'page': _currentPage,
'page_size': _pageSize, 'page_size': _pageSize,
'name': '', 'name': '',
'code': '', 'code': '',
'country_id': '', 'country_id': '',
'flag': '', // 新增国旗查询参数
}, },
options: Options(headers: {'Content-Type': 'application/json'}), options: Options(
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
),
); );
// 打印响应状态
debugPrint('请求成功,状态码: ${response.statusCode}'); debugPrint('请求成功,状态码: ${response.statusCode}');
debugPrint('响应数据: ${response.data}'); debugPrint('响应数据: ${response.data}');
@@ -149,7 +177,6 @@ class _CountryPageState extends State<CountryPage> {
_currentPage++; _currentPage++;
_errorMessage = null; _errorMessage = null;
}); });
// 打印数据处理结果
debugPrint('数据解析成功,当前列表总数: ${_countries.length}'); debugPrint('数据解析成功,当前列表总数: ${_countries.length}');
debugPrint('是否还有更多数据: $_hasMoreData,下一页: $_currentPage'); debugPrint('是否还有更多数据: $_hasMoreData,下一页: $_currentPage');
} else { } else {
@@ -232,7 +259,6 @@ class _CountryPageState extends State<CountryPage> {
icon: const Icon(Icons.add), icon: const Icon(Icons.add),
onPressed: () async { onPressed: () async {
debugPrint('点击新增按钮,跳转到新增页面'); debugPrint('点击新增按钮,跳转到新增页面');
// 跳转到新增页面,返回时刷新列表
final result = await Navigator.push( final result = await Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => const AddCountryPage()), MaterialPageRoute(builder: (context) => const AddCountryPage()),
@@ -294,21 +320,12 @@ class _CountryPageState extends State<CountryPage> {
); );
} }
}, },
// 监听滚动加载更多 controller: _scrollController, // 使用优化后的滚动控制器
controller: ScrollController()
..addListener(() {
if (_isLoading) return;
if (_hasMoreData &&
ScrollController().position.pixels >=
ScrollController().position.maxScrollExtent - 200) {
_loadMore();
}
}),
), ),
); );
} }
// 构建国家列表项 // 构建国家列表项(新增国旗显示)
Widget _buildCountryItem(ThemeData theme, Country country) { Widget _buildCountryItem(ThemeData theme, Country country) {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -327,18 +344,27 @@ class _CountryPageState extends State<CountryPage> {
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row( child: Row(
children: [ children: [
// 国旗显示区域
Container( Container(
width: 40, width: 40,
height: 40, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest, color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
image: country.flag != null && country.flag!.isNotEmpty
? DecorationImage(
image: NetworkImage(country.flag!),
fit: BoxFit.cover,
)
: null,
), ),
child: Icon( child: country.flag == null || country.flag!.isEmpty
? Icon(
Icons.flag, Icons.flag,
size: 24, size: 24,
color: theme.colorScheme.secondary, color: theme.colorScheme.secondary,
), )
: null,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded( Expanded(

View File

@@ -19,6 +19,7 @@ type ReadRequest struct {
CountryID string `form:"country_id"` // 国家ID可选 CountryID string `form:"country_id"` // 国家ID可选
Name string `form:"name"` // 国家名称,可选 Name string `form:"name"` // 国家名称,可选
Code string `form:"code"` // 国家代码,可选 Code string `form:"code"` // 国家代码,可选
Flag string `form:"flag"` // 国旗信息,新增可选参数
Page string `form:"page"` // 页码,可选 Page string `form:"page"` // 页码,可选
PageSize string `form:"page_size"` // 每页条数,可选 PageSize string `form:"page_size"` // 每页条数,可选
} }
@@ -31,11 +32,12 @@ type ReadData struct {
Items []CountryInfoViewItem `json:"items"` // 数据列表 Items []CountryInfoViewItem `json:"items"` // 数据列表
} }
// CountryInfoViewItem 视图数据项结构 // CountryInfoViewItem 视图数据项结构,新增国旗字段
type CountryInfoViewItem struct { type CountryInfoViewItem struct {
CountryID string `json:"country_id"` // 国家ID CountryID string `json:"country_id"` // 国家ID
Name string `json:"name"` // 国家名称 Name string `json:"name"` // 国家名称
Code string `json:"code"` // 国家代码 Code string `json:"code"` // 国家代码
Flag string `json:"flag"` // 国旗信息,新增字段
} }
// ReadResponse 读取响应结构 // ReadResponse 读取响应结构
@@ -76,19 +78,6 @@ func ReadHandler(c *gin.Context) {
return return
} }
// 验证查询条件至少有一个不为空
if req.CountryID == "" && req.Name == "" && req.Code == "" {
zap.L().Warn("⚠️ 请求参数验证失败",
zap.String("req_id", reqID),
zap.String("reason", "country_id、name、code不能同时为空"),
)
c.JSON(http.StatusBadRequest, ReadResponse{
Success: false,
Message: "请求参数错误country_id、name、code不能同时为空",
})
return
}
// 处理分页参数默认值 // 处理分页参数默认值
page, err := strconv.Atoi(req.Page) page, err := strconv.Atoi(req.Page)
if err != nil || page < 1 { if err != nil || page < 1 {
@@ -104,6 +93,7 @@ func ReadHandler(c *gin.Context) {
zap.String("country_id", req.CountryID), zap.String("country_id", req.CountryID),
zap.String("name", req.Name), zap.String("name", req.Name),
zap.String("code", req.Code), zap.String("code", req.Code),
zap.String("flag", req.Flag), // 新增国旗查询参数日志
zap.Int("page", page), zap.Int("page", page),
zap.Int("page_size", pageSize), zap.Int("page_size", pageSize),
) )
@@ -128,9 +118,15 @@ func ReadHandler(c *gin.Context) {
args = append(args, "%"+req.Code+"%") args = append(args, "%"+req.Code+"%")
paramIndex++ paramIndex++
} }
// 新增国旗查询条件
if req.Flag != "" {
whereClauses = append(whereClauses, "flag LIKE $"+strconv.Itoa(paramIndex))
args = append(args, "%"+req.Flag+"%")
paramIndex++
}
// 构建基础SQL // 构建基础SQL新增flag字段查询
baseSQL := "SELECT country_id, name, code FROM country_info_view" baseSQL := "SELECT country_id, name, code, flag FROM country_info_view"
countSQL := "SELECT COUNT(*) FROM country_info_view" countSQL := "SELECT COUNT(*) FROM country_info_view"
if len(whereClauses) > 0 { if len(whereClauses) > 0 {
whereStr := " WHERE " + strings.Join(whereClauses, " AND ") whereStr := " WHERE " + strings.Join(whereClauses, " AND ")
@@ -141,11 +137,11 @@ func ReadHandler(c *gin.Context) {
// 计算分页偏移量 // 计算分页偏移量
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
// 拼接分页SQL使用fmt.Sprintf更清晰 // 拼接分页SQL
querySQL := fmt.Sprintf("%s ORDER BY country_id LIMIT $%d OFFSET $%d", baseSQL, paramIndex, paramIndex+1) querySQL := fmt.Sprintf("%s ORDER BY country_id LIMIT $%d OFFSET $%d", baseSQL, paramIndex, paramIndex+1)
args = append(args, pageSize, offset) args = append(args, pageSize, offset)
// 查询总条数(修正参数传递方式) // 查询总条数
var total int64 var total int64
countArgs := args[:len(args)-2] // 排除分页参数 countArgs := args[:len(args)-2] // 排除分页参数
err = db.DB.QueryRow(countSQL, countArgs...).Scan(&total) err = db.DB.QueryRow(countSQL, countArgs...).Scan(&total)
@@ -176,11 +172,11 @@ func ReadHandler(c *gin.Context) {
} }
defer rows.Close() defer rows.Close()
// 处理查询结果 // 处理查询结果新增flag字段扫描
var items []CountryInfoViewItem var items []CountryInfoViewItem
for rows.Next() { for rows.Next() {
var item CountryInfoViewItem var item CountryInfoViewItem
if err := rows.Scan(&item.CountryID, &item.Name, &item.Code); err != nil { if err := rows.Scan(&item.CountryID, &item.Name, &item.Code, &item.Flag); err != nil {
zap.L().Error("❌ 解析查询结果失败", zap.L().Error("❌ 解析查询结果失败",
zap.String("req_id", reqID), zap.String("req_id", reqID),
zap.Error(err), zap.Error(err),