出入库修改

This commit is contained in:
jjLv 2026-02-12 09:09:57 +08:00
parent 5f703104f9
commit 7da823c249
9 changed files with 431 additions and 49 deletions

View File

@ -25,6 +25,7 @@ import androidx.multidex.MultiDex;
import com.bonus.canteen.db.AppDatabase; import com.bonus.canteen.db.AppDatabase;
import com.bonus.canteen.db.ConfigRepository; import com.bonus.canteen.db.ConfigRepository;
import com.bonus.canteen.utils.CrashMonitor;
import com.bonus.canteen.utils.sdkinit.ANRWatchDogInit; import com.bonus.canteen.utils.sdkinit.ANRWatchDogInit;
import com.bonus.canteen.utils.sdkinit.UMengInit; import com.bonus.canteen.utils.sdkinit.UMengInit;
import com.bonus.canteen.utils.sdkinit.XBasicLibInit; import com.bonus.canteen.utils.sdkinit.XBasicLibInit;
@ -50,6 +51,8 @@ public class SmartCanteenApp extends Application {
instance = this; instance = this;
initLibs(); initLibs();
initConfigRepository(); initConfigRepository();
// 注册全局崩溃捕获
CrashMonitor.init(this);
} }
/** /**
* 初始化配置仓库 * 初始化配置仓库

View File

@ -70,6 +70,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
@ -88,6 +89,7 @@ import com.bonus.canteen.entity.PhotoResponse;
import com.bonus.canteen.entity.SpinnerBean; import com.bonus.canteen.entity.SpinnerBean;
import com.bonus.canteen.entity.SupplierBean; import com.bonus.canteen.entity.SupplierBean;
import com.bonus.canteen.entity.WarehouseBean; import com.bonus.canteen.entity.WarehouseBean;
import com.bonus.canteen.fragment.EnterWarehouseFragment;
import com.bonus.canteen.fragment.callback.ResponseCallBack; import com.bonus.canteen.fragment.callback.ResponseCallBack;
import com.bonus.canteen.fragment.commonFragment.DocumentSelectionFragment; import com.bonus.canteen.fragment.commonFragment.DocumentSelectionFragment;
import com.bonus.canteen.fragment.commonFragment.ProductSelectionFragment; import com.bonus.canteen.fragment.commonFragment.ProductSelectionFragment;
@ -137,6 +139,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
@ -276,12 +279,24 @@ public class InspectionWarehousingFragment extends BaseFragment<ActivityInspecti
} }
// 解析selectedGoods为GoodsBean列表 // 解析selectedGoods为GoodsBean列表
goodsList = JSON.parseArray(selectedGoods, GoodsBean.class); goodsList = JSON.parseArray(selectedGoods, GoodsBean.class);
goodsList = goodsList.stream().filter(name -> name.getMaterialName() != null && !name.getMaterialName().isEmpty()) goodsList = goodsList.stream().filter(name -> name.getMaterialName() != null && !name.getMaterialName().isEmpty() && (name.getIntoNum().compareTo(BigDecimal.ZERO) == 0 || name.getIntoNum().compareTo(name.getMaterialTotalNum()) < 0))
.collect(Collectors.toList()); .collect(Collectors.toList());
Map<String, GoodsBean> goodsMap =
goodsList.stream()
.collect(Collectors.toMap(
GoodsBean::getMaterialName,
Function.identity(),
(a, b) -> a // 防重复 key
));
pages = goodsList.stream() pages = goodsList.stream()
.map(GoodsBean::getMaterialName) .map(GoodsBean::getMaterialName)
.filter(name -> name != null && !name.isEmpty()) .filter(name -> name != null && !name.isEmpty())
.toArray(String[]::new); .toArray(String[]::new);
L.d(TAG,"pages====" + Arrays.toString(pages));
// pages = goodsList.stream()
// .map(GoodsBean::getMaterialName)
// .filter(name -> name != null && !name.isEmpty())
// .toArray(String[]::new);
pagesList = List.of(pages); pagesList = List.of(pages);
MultiPage[] multiPages = MultiPage.fromList(pagesList); MultiPage[] multiPages = MultiPage.fromList(pagesList);
mDestPage = multiPages[0]; mDestPage = multiPages[0];
@ -295,8 +310,15 @@ public class InspectionWarehousingFragment extends BaseFragment<ActivityInspecti
mContentViewPager.setCurrentItem(mDestPage.getPosition(), false); mContentViewPager.setCurrentItem(mDestPage.getPosition(), false);
for (int i = 0; i < mCurrentItemCount; i++) { for (int i = 0; i < mCurrentItemCount; i++) {
mTabSegment.addTab(new TabSegment.Tab(pages[i])); mTabSegment.addTab(new TabSegment.Tab(pages[i]));
GoodsBean goodsBean = goodsMap.get(pages[i]);
if (goodsBean == null) continue;
if (goodsBean.getIntoNum().compareTo(BigDecimal.ZERO) > 0) {
mTabSegment.hideSignCountView(i);
} else {
mTabSegment.showSignCountView(requireContext(), i, 1); mTabSegment.showSignCountView(requireContext(), i, 1);
} }
}
int space = DensityUtils.dp2px(getContext(), 16); int space = DensityUtils.dp2px(getContext(), 16);
mTabSegment.setHasIndicator(true); mTabSegment.setHasIndicator(true);
mTabSegment.setMode(TabSegment.MODE_SCROLLABLE); mTabSegment.setMode(TabSegment.MODE_SCROLLABLE);
@ -565,7 +587,12 @@ public class InspectionWarehousingFragment extends BaseFragment<ActivityInspecti
Log.i(TAG, "result==" + result); Log.i(TAG, "result==" + result);
if (!ObjectUtil.isEmpty(result)) { if (!ObjectUtil.isEmpty(result)) {
if (JSONObject.parseObject(result).getInteger("code") == 500) { if (JSONObject.parseObject(result).getInteger("code") == 500) {
//判断是否存在msg
if (JSONObject.parseObject(result).containsKey("msg")) {
requireActivity().runOnUiThread(() -> callback.onFail(JSONObject.parseObject(result).get("msg").toString())); requireActivity().runOnUiThread(() -> callback.onFail(JSONObject.parseObject(result).get("msg").toString()));
}else{
requireActivity().runOnUiThread(() -> callback.onFail("服务器异常!!!"));
}
return; return;
} }
requireActivity().runOnUiThread(callback::onSuccess); requireActivity().runOnUiThread(callback::onSuccess);
@ -825,9 +852,9 @@ public class InspectionWarehousingFragment extends BaseFragment<ActivityInspecti
enterWeight.setClickable(true); enterWeight.setClickable(true);
view.findViewById(R.id.linear_weight_change).setVisibility(View.GONE); view.findViewById(R.id.linear_weight_change).setVisibility(View.GONE);
} }
num.setText(StringHelper.isEmptyAndNullOrZero(goodsBean.getMaterialTotalNum()) ? "0" : goodsBean.getMaterialTotalNum() + ""); num.setText(StringHelper.isEmptyAndNullOrZero(goodsBean.getMaterialTotalNum()) ? "0" : goodsBean.getMaterialTotalNum().subtract(goodsBean.getIntoNum()) + "");
price.setText(StringHelper.isEmptyAndNullOrZero(goodsBean.getUnitPrice()) ? "0" : goodsBean.getUnitPrice().divide(BigDecimal.valueOf(100)).toString()); price.setText(StringHelper.isEmptyAndNullOrZero(goodsBean.getUnitPrice()) ? "0" : goodsBean.getUnitPrice().divide(BigDecimal.valueOf(100)).toString());
String numText = StringHelper.isEmptyAndNullOrZero(goodsBean.getMaterialTotalNum()) ? "0" : goodsBean.getMaterialTotalNum() + ""; String numText = StringHelper.isEmptyAndNullOrZero(goodsBean.getMaterialTotalNum()) ? "0" : goodsBean.getMaterialTotalNum().subtract(goodsBean.getIntoNum()) + "";
weight.setText(pageType.equals("enter") ? "0" : numText); weight.setText(pageType.equals("enter") ? "0" : numText);
enterWeight.setText(pageType.equals("enter") ? "0" : numText); enterWeight.setText(pageType.equals("enter") ? "0" : numText);
if (!StringHelper.isEmptyAndNull(goodsBean.getSupplierName())) { if (!StringHelper.isEmptyAndNull(goodsBean.getSupplierName())) {

View File

@ -56,6 +56,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import okhttp3.MediaType; import okhttp3.MediaType;
@ -198,6 +200,9 @@ public class EnterWarehouseFragment extends BaseFragment<ActivityEnterMainPageBi
} }
private void initWarehouseSpinner(View view) { private void initWarehouseSpinner(View view) {
CompletableFuture.runAsync(() -> {
});
ThreadPoolManager.getExecutor().execute(() -> { ThreadPoolManager.getExecutor().execute(() -> {
String url = WorkConfig.getBaseUrl() + "/ims_warehouse_info/list?pageSize=" + 1000 + "&pageNum=" + 1; String url = WorkConfig.getBaseUrl() + "/ims_warehouse_info/list?pageSize=" + 1000 + "&pageNum=" + 1;
String result = service.httpGet(url, requireContext()); String result = service.httpGet(url, requireContext());

View File

@ -66,6 +66,7 @@ import org.easydarwin.easypusher.databinding.ActivityDocumentStroageListBinding;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import okhttp3.MediaType; import okhttp3.MediaType;
@ -331,6 +332,13 @@ public class DocumentSelectionFragment extends BaseFragment<ActivityDocumentStro
} else { } else {
Log.e(TAG, "totalIntoNum==" + inspectionNum); Log.e(TAG, "totalIntoNum==" + inspectionNum);
Log.e(TAG, "intoNum==" + intoNum); Log.e(TAG, "intoNum==" + intoNum);
List<GoodsBean> newList = new ArrayList<>();
newList = goodsList.stream().filter(name -> name.getMaterialName() != null && !name.getMaterialName().isEmpty() && (name.getIntoNum().compareTo(BigDecimal.ZERO) == 0 || name.getIntoNum().compareTo(name.getMaterialTotalNum()) < 0))
.collect(Collectors.toList());
if (newList.isEmpty()){
requireActivity().runOnUiThread(()-> XToastUtils.warning("当前验货单暂无可入库货品!"));
return;
}
//展示出入库单据信息 //展示出入库单据信息
showDetail(documentBean, goodsList); showDetail(documentBean, goodsList);
} }

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2026 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.bonus.canteen.utils;
import android.content.Context;
import android.os.Build;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
public final class CrashDumper {
public static void dump(Context ctx, Throwable e) {
try {
File dir = new File(ctx.getExternalFilesDir(null), "crash");
if (!dir.exists()) dir.mkdirs();
File file = new File(dir,
"crash-" + DateTimeHelper.getCurrentTimeStr() + ".txt");
PrintWriter pw = new PrintWriter(
new FileWriter(file, false));
pw.println("===== Device Info =====");
pw.println(Build.MANUFACTURER + " " + Build.MODEL);
pw.println("SDK: " + Build.VERSION.SDK_INT);
pw.println();
e.printStackTrace(pw);
pw.close();
} catch (Exception ignored) {}
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2026 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.bonus.canteen.utils;
import android.content.Context;
public final class CrashMonitor {
public static void init(Context context) {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
// 1. dump 异常
CrashDumper.dump(context, throwable);
// 2. 打包日志 + crash
LogPackager.packAndUpload(context);
// 3. 交还系统或直接杀进程
android.os.Process.killProcess(
android.os.Process.myPid()
);
System.exit(10);
});
}
}

View File

@ -1,56 +1,120 @@
/*
* Copyright (C) 2026 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.bonus.canteen.utils; package com.bonus.canteen.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log; import android.util.Log;
public class L { import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public final class L {
private static final String TAG = "APP_LOG";
private static final long MAX_SIZE = 10 * 1024 * 1024; // 10MB
private static File logDir;
private static final LinkedBlockingQueue<String> LOG_QUEUE = new LinkedBlockingQueue<>();
private static final ExecutorService LOG_EXECUTOR =
Executors.newSingleThreadExecutor(); // 关键单线程
private static final SimpleDateFormat DAY_FMT =
new SimpleDateFormat("yyyy-MM-dd", Locale.US);
private static final SimpleDateFormat TIME_FMT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
/* ================= 初始化Application 调用一次) ================= */
public static void init(Context context) {
logDir = new File(context.getExternalFilesDir("logs"), "");
if (!logDir.exists()) {
logDir.mkdirs();
}
startWriteLoop();
}
/* ================= 对外日志接口 ================= */
public static void d(String tag, String msg) { public static void d(String tag, String msg) {
StackTraceElement element = Thread.currentThread().getStackTrace()[3]; log(Log.DEBUG, tag, msg);
String className = element.getFileName();
String methodName = element.getMethodName();
int line = element.getLineNumber();
Log.d(tag, className + ":" + line + "" + methodName + "(): " + msg);
} }
public static void i(String tag, String msg) { public static void i(String tag, String msg) {
StackTraceElement element = Thread.currentThread().getStackTrace()[3]; log(Log.INFO, tag, msg);
String className = element.getFileName();
String methodName = element.getMethodName();
int line = element.getLineNumber();
Log.i(tag, className + ":" + line + "" + methodName + "(): " + msg);
} }
public static void e(String tag, String msg) {
StackTraceElement element = Thread.currentThread().getStackTrace()[3];
String className = element.getFileName();
String methodName = element.getMethodName();
int line = element.getLineNumber();
Log.e(tag, className + ":" + line + "" + methodName + "(): " + msg);
}
public static void w(String tag, String msg) { public static void w(String tag, String msg) {
StackTraceElement element = Thread.currentThread().getStackTrace()[3]; log(Log.WARN, tag, msg);
String className = element.getFileName();
String methodName = element.getMethodName();
int line = element.getLineNumber();
Log.w(tag, className + ":" + line + "" + methodName + "(): " + msg);
} }
public static void e(String tag, String msg) {
log(Log.ERROR, tag, msg);
}
/* ================= 内部实现 ================= */
private static void log(int level, String tag, String msg) {
StackTraceElement e = Thread.currentThread().getStackTrace()[4];
@SuppressLint("DefaultLocale") String log = String.format(
"[%s][%s:%d %s] %s",
TIME_FMT.format(new Date()),
e.getFileName(),
e.getLineNumber(),
e.getMethodName(),
msg
);
Log.println(level, tag, log);
LOG_QUEUE.offer(log);
}
/* ================= 单线程写文件 ================= */
private static void startWriteLoop() {
LOG_EXECUTOR.execute(() -> {
while (true) {
try {
String log = LOG_QUEUE.take();
writeToFile(log);
} catch (Exception ignored) {
}
}
});
}
private static void writeToFile(String log) {
try {
File file = getLogFile();
BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
bw.write(log);
bw.newLine();
bw.close();
} catch (Exception ignored) {
}
}
private static File getLogFile() {
String day = DAY_FMT.format(new Date());
File base = new File(logDir, "log-" + day + ".txt");
if (!base.exists() || base.length() < MAX_SIZE) {
return base;
}
int index = 1;
while (true) {
File f = new File(logDir, "log-" + day + "-" + index + ".txt");
if (!f.exists() || f.length() < MAX_SIZE) {
return f;
}
index++;
}
}
} }

View File

@ -0,0 +1,186 @@
/*
* Copyright (C) 2026 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.bonus.canteen.utils;
import android.content.Context;
import android.os.Build;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public final class LogPackager {
private static final long MAX_LOG_DIR_SIZE = 10 * 1024 * 1024; // 10MB
private static final String LOG_DIR = "logs";
private static final String CRASH_DIR = "crash";
private static final String UPLOAD_DIR = "upload";
private LogPackager() {}
/**
* 对外唯一入口异步
*/
public static void packAndUploadAsync(Context context) {
new Thread(() -> packAndUpload(context), "log-pack-thread").start();
}
/**
* 核心逻辑
*/
static void packAndUpload(Context context) {
try {
File root = context.getExternalFilesDir(null);
if (root == null) return;
File logsDir = new File(root, LOG_DIR);
File crashDir = new File(root, CRASH_DIR);
File uploadDir = new File(root, UPLOAD_DIR);
if (!uploadDir.exists()) uploadDir.mkdirs();
// 1 日志目录限流
checkAndTrim(logsDir);
// 2 创建 zip
File zipFile = new File(uploadDir, buildZipName());
ZipOutputStream zos =
new ZipOutputStream(new BufferedOutputStream(
new FileOutputStream(zipFile)));
// 3 设备信息
zipText(zos, "device_info.txt", buildDeviceInfo());
// 4 logs
zipDir(logsDir, "logs", zos);
// 5 crash
zipDir(crashDir, "crash", zos);
zos.close();
// 6 触发上传你后面接服务器
// LogUploader.uploadAsync(zipFile);
} catch (Throwable t) {
// 这里不能再崩
t.printStackTrace();
}
}
// ====================== 工具方法 ======================
private static String buildZipName() {
String time = new SimpleDateFormat(
"yyyyMMdd_HHmmss", Locale.US).format(new Date());
return "log_crash_" + time + ".zip";
}
/**
* 设备 & 系统信息
*/
private static String buildDeviceInfo() {
StringBuilder sb = new StringBuilder();
sb.append("Manufacturer: ").append(Build.MANUFACTURER).append('\n');
sb.append("Model: ").append(Build.MODEL).append('\n');
sb.append("Board: ").append(Build.BOARD).append('\n');
sb.append("CPU ABI: ").append(Build.SUPPORTED_ABIS[0]).append('\n');
sb.append("Android: ").append(Build.VERSION.RELEASE).append('\n');
sb.append("SDK: ").append(Build.VERSION.SDK_INT).append('\n');
sb.append("Time: ").append(new Date()).append('\n');
return sb.toString();
}
/**
* 打包目录
*/
private static void zipDir(File dir, String rootName, ZipOutputStream zos)
throws IOException {
if (dir == null || !dir.exists() || !dir.isDirectory()) return;
File[] files = dir.listFiles();
if (files == null) return;
for (File f : files) {
if (f.isFile()) {
zipFile(f, rootName + "/" + f.getName(), zos);
}
}
}
/**
* 打包单文件
*/
private static void zipFile(File file, String entryName,
ZipOutputStream zos) throws IOException {
zos.putNextEntry(new ZipEntry(entryName));
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream(file));
byte[] buffer = new byte[4096];
int len;
while ((len = bis.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
bis.close();
zos.closeEntry();
}
/**
* 打包文本内容设备信息
*/
private static void zipText(ZipOutputStream zos,
String entryName, String content)
throws IOException {
zos.putNextEntry(new ZipEntry(entryName));
zos.write(content.getBytes("UTF-8"));
zos.closeEntry();
}
/**
* 日志目录限流超过 10MB 删最老
*/
private static void checkAndTrim(File dir) {
if (dir == null || !dir.exists()) return;
File[] files = dir.listFiles();
if (files == null) return;
long total = 0;
for (File f : files) total += f.length();
if (total <= MAX_LOG_DIR_SIZE) return;
// 按时间排序先删最老
java.util.Arrays.sort(files,
(a, b) -> Long.compare(a.lastModified(), b.lastModified()));
for (File f : files) {
if (total <= MAX_LOG_DIR_SIZE) break;
total -= f.length();
//noinspection ResultOfMethodCallIgnored
f.delete();
}
}
}