P2P金融
内容大纲
p2p金融行业知识初步了解
公司产品研发团队的构成[产品+UI+开发+测试+运营]
服务器端提供接口文档的必要性
4.模拟服务器环境配置及简要讲解
5.通过此项目需要掌握:
5.1 熟悉金融业务相关知识及业务流程
5.2 掌握数据加密解密相关操作
5.3 掌握代码的抽取
5.4 掌握支付和提现业务的实现流程
5.5 熟悉使用更多框架
AsyncHttpClient,ButterKnife,Picasso,ViewPagerIndicator,Banner,MpAndroidChart,GestureLock,集成支付宝sdk,shareSDK等
5.6 掌握更多的自定义控件
5.7 熟悉客户端与服务器端的交互
什么是p2p
P2P金融又叫P2P信贷
P2P:person to person / Peer to Peer;
信贷:信用贷款
P2P金融指个人与个人间的小额借贷交易,一般需要借助电子商务专业网络平台帮助借贷双方确立借贷关系并完成相关交易手续。
电子商务网络平台指什么?
指的就是经营P2P金融业务的公司开发的线上移动APP、网站、pc端软件。通过这些平台帮助借贷双方确定借贷关系并完成相关交易手续。(对于我们Android开发,我们需要关注的就是移动端App软件的P2P金融平台)
1. 数据库MySQL
/*
SQLyog Ultimate v11.25 (64 bit)
MySQL - 5.5.28 : Database - P2PInvest
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`atguigu` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `P2PInvest`;
/*Table structure for table `customer` */
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`salary` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*Data for the table `customer` */
insert into `customer`(`id`,`name`,`salary`) values (1,'方志',200);
/*Table structure for table `feedback_table` */
DROP TABLE IF EXISTS `feedback_table`;
CREATE TABLE `feedback_table` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`department` varchar(30) DEFAULT NULL,
`content` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
/*Data for the table `feedback_table` */
insert into `feedback_table`(`id`,`department`,`content`) values (7,'理财部','sfafd'),(8,'咨询部','dsf'),(9,'咨询部','hello'),(10,'技术部','fasfe'),(12,'理财部','啊理我'),(13,'咨询部','不知道你们部门美女多不多');
/*Table structure for table `user_table` */
DROP TABLE IF EXISTS `user_table`;
CREATE TABLE `user_table` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(25) NOT NULL,
`password` varchar(40) NOT NULL,
`phone` varchar(15) NOT NULL,
`imageurl` varchar(60) DEFAULT 'http://192.168.191.1:8080/P2PInvest/images/tx.png',
`iscredit` tinyint(1) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
/*Data for the table `user_table` */
insert into `user_table`(`id`,`name`,`password`,`phone`,`imageurl`,`iscredit`) values (1,'李雷','fa7d5dcd109a27b6fa057412ef454626','13212341234','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(2,'shkstart','e10adc3949ba59abbe56e057f20f883e','13012341234','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(3,'HanMeimei','2cf07917d46c0d312f36d9d21237baa2','13212341234','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(4,'Tom','b1e6d1bd047f43af0ab59556c394a376','13712344321','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(5,'范冰冰','03e3ea991be309de78456bf0f017fa84','13712367898','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(7,'2432','81dc9bdb52d04dc20036dbd8313ed055','13022332245','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(8,'songhk','827ccb0eea8a706c4c34a16891f84e7b','13033332222','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(9,'cody','e10adc3949ba59abbe56e057f20f883e','18611119374','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(10,'Lilei','e10adc3949ba59abbe56e057f20f883e','13545667864','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(19,'阿狸','b2ca678b4c936f905fb82f2733f5297f','14477','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(20,'qq123','111','13011112222','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(21,'悟空','698d51a19d8a121ce581499d7b701668','13811112222','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(22,'song nanshen','e10adc3949ba59abbe56e057f20f883e','13012345678','http://192.168.191.1:8080/P2PInvest/images/tx.png',1),(23,'æ��空1','698d51a19d8a121ce581499d7b701668','13811112223','http://192.168.191.1:8080/P2PInvest/images/tx.png',1);
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
2. 服务器
服务器部署在本地Tomcat访问地址:
http://localhost/P2PInvest/
具体资源访问地址应该设置成相应的Ip和端口号
image3. Android 客户端
UI基本框架
image01-主界面activity_main.xml的设置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical"
>
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<RadioGroup
android:id="@+id/rg_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/home_bottom_parent_bg"
android:gravity="center_vertical"
android:padding="3dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_home"
style="@style/MainButtonStyle"
android:drawableTop="@drawable/home_button_selector"
android:text="首页" />
<RadioButton
android:id="@+id/rb_invest"
style="@style/MainButtonStyle"
android:drawableTop="@drawable/invest_button_selector"
android:text="投资" />
<RadioButton
android:id="@+id/rb_assets"
style="@style/MainButtonStyle"
android:drawableTop="@drawable/assets_button_selector"
android:textColor="@drawable/buttom_assets_text_selector"
android:text="我的资产" />
<RadioButton
android:id="@+id/rb_more"
style="@style/MainButtonStyle"
android:drawableTop="@drawable/more_button_selector"
android:text="更多" />
</RadioGroup>
</LinearLayout>
02-欢迎界面activity_splash.xml的设置
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_welcome"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/start_background">
<ImageView
android:id="@+id/iv_welcome_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:background="@drawable/app_icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_welcome_icon"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="硅谷金融"
android:textColor="@color/white"
android:textSize="40sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="100dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前版本:"
android:textColor="@color/white"
android:textSize="15sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1.1"
android:textColor="@color/white"
android:textSize="15sp" />
</LinearLayout>
</RelativeLayout>
设置为全屏显示
方式一:动态编码的方式:
// 去掉窗口标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 隐藏顶部的状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView();
方式二:
android:theme="@android:style/Theme.Black.NoTitleBar"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
方式三
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:windowNoTitle">true</item><!-- 没有标题 -->
</style>
03-提供common-MyApplication类
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/18 20:09
* @function:Application类里获取的Context,Handler对象可以被当前Module里的任何类库引用到,而不必在需要用到的时候去new对象或者是通过构造传入。
Application类里获取的主线程和主线程id对象可以在当前Module任意的地方,判断当前代码是否运行在主线程当中,或者将一段代码指定在主线程中运行。
*/
public class MyApplication extends Application
{
//在整个应用执行过程中,需要提供的变量
public static Context context;//需要使用的上下文对象
public static Handler handler;//需要使用的handler
public static Thread mainThread;//提供主线程对象
public static int mainThreadId;//提供主线程对象的id
@Override
public void onCreate()
{
super.onCreate();
context = this.getApplicationContext();
handler = new Handler();
mainThread = Thread.currentThread();//实例化当前Application的线程即为主线程
mainThreadId = android.os.Process.myTid();//获取当前线程的id
//设置未捕获异常的处理器
// CrashHandler.getInstance().init();
}
}
04-提供util-UIUtils类
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/18 20:24
* @function:专门提供为处理一些UI相关的问题而创建的工具类, 提供资源获取的通用方法,避免每次都写重复的代码获取结果。
*/
public class UIUtils
{
public static Context getContext()
{
return MyApplication.context;
}
public static Handler getHandler()
{
return MyApplication.handler;
}
//返回指定colorId对应的颜色值
public static int getColor(int colorId)
{
return getContext().getResources().getColor(colorId);
}
//加载指定viewId的视图对象,并返回
public static View getView(int viewId)
{
View view = View.inflate(getContext(), viewId, null);
return view;
}
public static String[] getStringArr(int strArrId)
{
String[] stringArray = getContext().getResources().getStringArray(strArrId);
return stringArray;
}
//将dp转化为px
public static int dp2px(int dp)
{
//获取手机密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5);//实现四舍五入
}
public static int px2dp(int px)
{
//获取手机密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (px / density + 0.5);//实现四舍五入
}
}
05-提供common-AppNetConfig类
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/18 20:22
* @function:配置网络请求相关的地址
*/
public class AppNetConfig
{
public static final String IPADDRESS = "192.168.1.101";
public static final String BASE_URL = "http://" + IPADDRESS + ":80/P2PInvest/";
public static final String PRODUCT = BASE_URL + "product";//访问“全部理财”产品
public static final String LOGIN = BASE_URL + "login";//登录
public static final String INDEX = BASE_URL + "index";//访问“homeFragment”
public static final String USERREGISTER = BASE_URL + "UserRegister";//访问“homeFragment”
public static final String FEEDBACK = BASE_URL + "FeedBack";//注册
public static final String UPDATE = BASE_URL + "update.json";//更新应用
}
06-提供栈管理器common-AcivityManager类
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/18 20:10
* @function:统一应用程序中所有的Activity的栈管理(单例) 涉及到activity的添加、删除指定、删除当前、删除所有、返回栈大小的方法
*/
public class ActivityManager
{
//提供栈的对象
private Stack<Activity> activityStack = new Stack<>();
private static ActivityManager activityManager = new ActivityManager();
//单例模式:饿汉式,,私有化构造器
private ActivityManager()
{
}
/**
* 提供获取单例实例的方法
*
* @return
*/
public static ActivityManager getInstance()
{
return activityManager;
}
/**
* activity的添加
*
* @param activity
*/
public void add(Activity activity)
{
if (activity != null)
{
activityStack.add(activity);
}
}
/**
* 移除除指定的activity
*
* @param activity
*/
public void remove(Activity activity)
{
if (activity != null)
{
//栈顶不断变化,故不采用按如下写法
// for(int i = 0; i < activityStack.size(); i++) {
// Activity currentActivity = activityStack.get(i);
// if(currentActivity.getClass().equals(activity.getClass())){
// currentActivity.finish();//销毁当前的activity
// activityStack.remove(i);//从栈空间移除
// }
// }
for (int i = activityStack.size(); i >= 0; i--)
{
Activity currentActivity = activityStack.get(i);
if (currentActivity.getClass().equals(activity.getClass()))//移除所有同类的activity对象
{
currentActivity.finish();//销毁当前的activity
activityStack.remove(i);//从栈空间移除
}
}
}
}
/**
* 移除当前的activity
*/
public void removeCurrentActivity()
{
//方式一:
// Activity activity = activityStack.get(activityStack.size() - 1);
// activity.finish();
// activityStack.remove(activityStack.size() - 1);
//方式二:
Activity activity = activityStack.lastElement();
activity.finish();
activityStack.remove(activity);
}
/**
* 移除所有的activity
*/
public void removeAll()
{
for (int i = activityStack.size(); i >= 0; i--)
{
Activity activity = activityStack.get(i);
activity.finish();
activityStack.remove(activity);
}
}
/**
* 返回栈大小
*
* @return
*/
public int size()
{
return activityStack.size();
}
}
07-提供common-CrashHandler类
/**
* @author: Hashub
* @WeChat: NGSHMVP
* @Date: 2018/9/18 20:23
* @function:程序中的未捕获的全局异常的捕获(单例) 解决两个问题:
* 1.当出现未捕获的异常时,能够给用户一个相对友好的提示
* 2.在出现异常时,能够将异常信息发送给后台,便于在后续的版本中解决bug
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler
{
//系统默认的处理未捕获异常的处理器
private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler;
//单例模式:(懒汉式)
//本身实例化未捕获异常的处理器的操作就是系统在一个单独的线程中完成的,所有不涉及到
//多线程的问题,所以使用懒汉式更好!
private CrashHandler()
{
}
private static CrashHandler crashHandler = null;
public static CrashHandler getInstance()
{
if (crashHandler == null)
{
crashHandler = new CrashHandler();
}
return crashHandler;
}
public void init()
{
defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
//将当前类设置为出现未捕获异常的处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
//一旦系统出现未捕获的异常,就会调用如下的回调方法
@Override
public void uncaughtException(Thread thread, Throwable ex)
{
// Log.e("TAG", "亲,出现了未捕获的异常了!" + ex.getMessage());
new Thread()
{
public void run()
{
//prepare()和loop()之间的操作就是在主线程中执行的!
//在android系统中,默认情况下,一个线程中是不可以调用Looper进行消息的处理的。除非是主线程
Looper.prepare();
Toast.makeText(UIUtils.getContext(), "亲,出现了未捕获的异常了!", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
//收集异常信息
collectionException(ex);
try
{
Thread.sleep(2000);
//移除当前activity
ActivityManager.getInstance().removeCurrentActivity();
//结束当前的进程
android.os.Process.killProcess(android.os.Process.myPid());
//结束虚拟机
System.exit(0);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
private void collectionException(Throwable ex)
{
final String exMessage = ex.getMessage();
//收集具体的客户的手机、系统的信息
final String message = Build.DEVICE + ":" + Build.MODEL + ":" + Build.PRODUCT + ":" + Build.VERSION.SDK_INT;
//发送给后台此异常信息
new Thread()
{
public void run()
{
//需要按照指定的url,访问后台的sevlet,将异常信息发送过去
Log.e("TAG", "exception = " + exMessage);
Log.e("TAG", "message = " + message);
}
}.start();
}
}
08-在功能清单文件中配置部分用到的权限
<!-- 配置所需权限 -->
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CALL_PHONE" />
网友评论